logging-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From nickwilli...@apache.org
Subject svn commit: r1481664 [1/2] - in /logging/log4j/log4j2/trunk: ./ core/ core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/ core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ core/src/test/java/org/apache/logging/lo...
Date Sun, 12 May 2013 22:52:11 GMT
Author: nickwilliams
Date: Sun May 12 22:52:10 2013
New Revision: 1481664

URL: http://svn.apache.org/r1481664
Log:
Updating JPAAppender to use JPA 2.1 instead, test with Hibernate 4.3

Added:
    logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/LogEventEntity.java
    logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/converter/
    logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextMapAttributeConverter.java
    logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextMapJsonAttributeConverter.java
    logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextStackAttributeConverter.java
    logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextStackJsonAttributeConverter.java
    logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/converter/MarkerAttributeConverter.java
    logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/converter/MessageAttributeConverter.java
    logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/converter/StackTraceElementAttributeConverter.java
    logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ThrowableAttributeConverter.java
    logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/converter/package-info.java
    logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/TestBasicEntity.java
    logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/converter/
    logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextMapAttributeConverterTest.java
    logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextMapJsonAttributeConverterTest.java
    logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextStackAttributeConverterTest.java
    logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextStackJsonAttributeConverterTest.java
    logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/converter/MarkerAttributeConverterTest.java
    logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/converter/MessageAttributeConverterTest.java
    logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/converter/StackTraceElementAttributeConverterTest.java
    logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ThrowableAttributeConverterTest.java
    logging/log4j/log4j2/trunk/core/src/test/resources/org/apache/logging/log4j/core/appender/db/jdbc/log4j2-data-source.xml
    logging/log4j/log4j2/trunk/core/src/test/resources/org/apache/logging/log4j/core/appender/db/jpa/log4j2-jpa-base.xml
      - copied, changed from r1481329, logging/log4j/log4j2/trunk/core/src/test/resources/org/apache/logging/log4j/core/appender/db/jpa/log4j2-jpa.xml
    logging/log4j/log4j2/trunk/core/src/test/resources/org/apache/logging/log4j/core/appender/db/jpa/log4j2-jpa-basic.xml
Removed:
    logging/log4j/log4j2/trunk/core/src/test/resources/org/apache/logging/log4j/core/appender/db/jpa/log4j2-jpa.xml
Modified:
    logging/log4j/log4j2/trunk/core/pom.xml
    logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/LogEventWrapperEntity.java
    logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/package-info.java
    logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/db/jdbc/JDBCAppenderTest.java
    logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/JPAAppenderTest.java
    logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/TestEntity.java
    logging/log4j/log4j2/trunk/core/src/test/resources/META-INF/persistence.xml
    logging/log4j/log4j2/trunk/core/src/test/resources/org/apache/logging/log4j/core/appender/db/jdbc/log4j2-driver-manager.xml
    logging/log4j/log4j2/trunk/core/src/test/resources/org/apache/logging/log4j/core/appender/db/jdbc/log4j2-factory-method.xml
    logging/log4j/log4j2/trunk/pom.xml

Modified: logging/log4j/log4j2/trunk/core/pom.xml
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/core/pom.xml?rev=1481664&r1=1481663&r2=1481664&view=diff
==============================================================================
--- logging/log4j/log4j2/trunk/core/pom.xml (original)
+++ logging/log4j/log4j2/trunk/core/pom.xml Sun May 12 22:52:10 2013
@@ -136,7 +136,7 @@
     </dependency>
     <dependency>
       <groupId>org.hibernate.javax.persistence</groupId>
-      <artifactId>hibernate-jpa-2.0-api</artifactId>
+      <artifactId>hibernate-jpa-2.1-api</artifactId>
       <scope>compile</scope>
       <optional>true</optional>
     </dependency>

Added: logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/LogEventEntity.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/LogEventEntity.java?rev=1481664&view=auto
==============================================================================
--- logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/LogEventEntity.java (added)
+++ logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/LogEventEntity.java Sun May 12 22:52:10 2013
@@ -0,0 +1,126 @@
+package org.apache.logging.log4j.core.appender.db.jpa;
+
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.Marker;
+import org.apache.logging.log4j.ThreadContext;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.appender.db.jpa.converter.ContextMapAttributeConverter;
+import org.apache.logging.log4j.core.appender.db.jpa.converter.ContextStackAttributeConverter;
+import org.apache.logging.log4j.core.appender.db.jpa.converter.MarkerAttributeConverter;
+import org.apache.logging.log4j.core.appender.db.jpa.converter.MessageAttributeConverter;
+import org.apache.logging.log4j.core.appender.db.jpa.converter.StackTraceElementAttributeConverter;
+import org.apache.logging.log4j.core.appender.db.jpa.converter.ThrowableAttributeConverter;
+import org.apache.logging.log4j.message.Message;
+
+import javax.persistence.Basic;
+import javax.persistence.Convert;
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
+import javax.persistence.MappedSuperclass;
+import java.util.Map;
+
+/**
+ * Users of the JPA appender may want to extend this class instead of {@link LogEventWrapperEntity}. This class
+ * implements all of the required mutator methods but does not implement a mutable entity ID property. In order to
+ * create an entity based on this class, you need only create two constructors matching this class's
+ * constructors, annotate the class {@link javax.persistence.Entity @Entity} and {@link javax.persistence.Table @Table},
+ * and implement the fully mutable entity ID property annotated with {@link javax.persistence.Id @Id} and
+ * {@link javax.persistence.GeneratedValue @GeneratedValue} to tell the JPA provider how to calculate an ID for new
+ * events.<br>
+ * <br>
+ * The attributes in this entity use the default column names (which, according to the JPA spec, are the property names
+ * minus the "get" and "set" from the accessors/mutators). If you want to use different column names for one or more
+ * columns, override the necessary accessor methods defined in this class with the same annotations plus the
+ * {@link javax.persistence.Column @Column} annotation to specify the column name.<br>
+ * <br>
+ * The {@link #getContextMap()} and {@link #getContextStack()} attributes in this entity use the
+ * {@link ContextMapAttributeConverter} and {@link ContextStackAttributeConverter}, respectively. These convert the
+ * properties to simple strings that cannot be converted back to the properties. If you wish to instead convert these to
+ * a reversible JSON string, override these attributes with the same annotations but use the
+ * {@link org.apache.logging.log4j.core.appender.db.jpa.converter.ContextMapJsonAttributeConverter} and
+ * {@link org.apache.logging.log4j.core.appender.db.jpa.converter.ContextStackJsonAttributeConverter} instead.<br>
+ * <br>
+ * All other attributes in this entity use reversible converters that can be used for both persistence and retrieval. If
+ * there are any attributes you do not want persistent, you should override their accessor methods and annotate with
+ * {@link javax.persistence.Transient @Transient}.
+ *
+ * @see LogEventWrapperEntity
+ */
+@MappedSuperclass
+public abstract class LogEventEntity extends LogEventWrapperEntity {
+    @SuppressWarnings("unused") // JPA requires this
+    public LogEventEntity() {
+        super();
+    }
+
+    public LogEventEntity(final LogEvent wrapped) {
+        super(wrapped);
+    }
+
+    @Override
+    @Basic
+    @Enumerated(EnumType.STRING)
+    public Level getLevel() {
+        return this.getWrappedEvent().getLevel();
+    }
+
+    @Override
+    @Basic
+    public String getLoggerName() {
+        return this.getWrappedEvent().getLoggerName();
+    }
+
+    @Override
+    @Convert(converter = StackTraceElementAttributeConverter.class)
+    public StackTraceElement getSource() {
+        return this.getWrappedEvent().getSource();
+    }
+
+    @Override
+    @Convert(converter = MessageAttributeConverter.class)
+    public Message getMessage() {
+        return this.getWrappedEvent().getMessage();
+    }
+
+    @Override
+    @Convert(converter = MarkerAttributeConverter.class)
+    public Marker getMarker() {
+        return this.getWrappedEvent().getMarker();
+    }
+
+    @Override
+    @Basic
+    public String getThreadName() {
+        return this.getWrappedEvent().getThreadName();
+    }
+
+    @Override
+    @Basic
+    public long getMillis() {
+        return this.getWrappedEvent().getMillis();
+    }
+
+    @Override
+    @Convert(converter = ThrowableAttributeConverter.class)
+    public Throwable getThrown() {
+        return this.getWrappedEvent().getThrown();
+    }
+
+    @Override
+    @Convert(converter = ContextMapAttributeConverter.class)
+    public Map<String, String> getContextMap() {
+        return this.getWrappedEvent().getContextMap();
+    }
+
+    @Override
+    @Convert(converter = ContextStackAttributeConverter.class)
+    public ThreadContext.ContextStack getContextStack() {
+        return this.getWrappedEvent().getContextStack();
+    }
+
+    @Override
+    @Basic
+    public String getFQCN() {
+        return this.getWrappedEvent().getFQCN();
+    }
+}

Modified: logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/LogEventWrapperEntity.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/LogEventWrapperEntity.java?rev=1481664&r1=1481663&r2=1481664&view=diff
==============================================================================
--- logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/LogEventWrapperEntity.java (original)
+++ logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/LogEventWrapperEntity.java Sun May 12 22:52:10 2013
@@ -16,39 +16,43 @@
  */
 package org.apache.logging.log4j.core.appender.db.jpa;
 
-import java.util.Map;
-
-import javax.persistence.MappedSuperclass;
-import javax.persistence.Transient;
-
 import org.apache.logging.log4j.Level;
 import org.apache.logging.log4j.Marker;
 import org.apache.logging.log4j.ThreadContext;
 import org.apache.logging.log4j.core.LogEvent;
 import org.apache.logging.log4j.message.Message;
 
+import javax.persistence.MappedSuperclass;
+import javax.persistence.Transient;
+import java.util.Map;
+
 /**
- * Users of the JPA appender MUST implement this class, using JPA annotations on the concrete class and all of its
+ * Users of the JPA appender MUST extend this class, using JPA annotations on the concrete class and all of its
  * accessor methods (as needed) to map them to the proper table and columns. Accessors you do not want persisted should
  * be annotated with {@link Transient @Transient}. All accessors should call {@link #getWrappedEvent()} and delegate the
- * call to the underlying event.<br>
+ * call to the underlying event. Users may want to instead extend {@link LogEventEntity}, which takes care of all of
+ * this for you.<br>
  * <br>
  * The concrete class must have two constructors: a public no-arg constructor to convince the JPA provider that it's a
- * valid entity (this constructor will call {@link #LogEventWrapperEntity(LogEvent) super(null)}), and a public
- * constructor that takes a single {@link LogEvent event} and passes it to the parent class with
- * {@link #LogEventWrapperEntity(LogEvent) super(event)}. The concrete class must also have a mutable
- * {@link javax.persistence.Id @Id} property with fully-functional mutator and accessor methods (usually configured as a
- * {@link javax.persistence.GeneratedValue @GeneratedValue} property, and should be annotated with
- * {@link javax.persistence.Entity @Entity}<br>
+ * valid entity, and a public constructor that takes a single {@link LogEvent event} and passes it to the parent class
+ * with {@link #LogEventWrapperEntity(LogEvent) super(event)}. Furthermore, the concrete class must be annotated
+ * {@link javax.persistence.Entity @Entity} and {@link javax.persistence.Table @Table} and must implement a fully
+ * mutable ID property annotated with {@link javax.persistence.Id @Id} and
+ * {@link javax.persistence.GeneratedValue @GeneratedValue} to tell the JPA provider how to calculate an ID for new
+ * events.<br>
  * <br>
  * Many of the return types of {@link LogEvent} methods (e.g., {@link StackTraceElement}, {@link Message},
- * {@link Marker}) will not be recognized by the JPA provider. In these cases, you must either implement custom
- * persistence serializers <em>or</em> (probably easier) mark those methods {@link Transient @Transient} and create
- * similar methods that return the String form of these properties.<br>
+ * {@link Marker}, {@link Throwable}, {@link ThreadContext.ContextStack}, and {@link Map Map&lt;String, String&gt}) will
+ * not be recognized by the JPA provider. In conjunction with {@link javax.persistence.Convert @Convert}, you can use
+ * the converters in the {@link org.apache.logging.log4j.core.appender.db.jpa.converter} package to convert these
+ * types to database columns. If you want to retrieve log events from the database, you can create a true POJO entity
+ * and also use these converters for extracting persisted values.<br>
  * <br>
  * The mutator methods in this class not specified in {@link LogEvent} are no-op methods, implemented to satisfy the JPA
  * requirement that accessor methods have matching mutator methods. If you create additional accessor methods, you must
  * likewise create matching no-op mutator methods.
+ *
+ * @see LogEventEntity
  */
 @MappedSuperclass
 public abstract class LogEventWrapperEntity implements LogEvent {
@@ -58,6 +62,11 @@ public abstract class LogEventWrapperEnt
     private static final long serialVersionUID = 1L;
     private final LogEvent wrappedEvent;
 
+    @SuppressWarnings("unused") // JPA requires this
+    protected LogEventWrapperEntity() {
+        this(null);
+    }
+
     /**
      * Instantiates the base class. All concrete implementations must have two constructors: a no-arg constructor that
      * calls this constructor with a null argument, and a constructor matching this constructor's signature.
@@ -79,82 +88,137 @@ public abstract class LogEventWrapperEnt
         return this.wrappedEvent;
     }
 
-    @Override
-    @Transient
-    public final boolean isEndOfBatch() {
-        return this.getWrappedEvent().isEndOfBatch();
-    }
-
-    @Override
-    @Transient
-    public final boolean isIncludeLocation() {
-        return this.getWrappedEvent().isIncludeLocation();
-    }
-
+    /**
+     * A no-op mutator to satisfy JPA requirements, as this entity is write-only.
+     *
+     * @param level Ignored.
+     */
     @SuppressWarnings("unused")
-    public void setContextMap(final Map<String, String> contextMap) {
+    public void setLevel(final Level level) {
         // this entity is write-only
     }
 
+    /**
+     * A no-op mutator to satisfy JPA requirements, as this entity is write-only.
+     *
+     * @param loggerName Ignored.
+     */
     @SuppressWarnings("unused")
-    public void setContextStack(final ThreadContext.ContextStack contextStack) {
+    public void setLoggerName(final String loggerName) {
         // this entity is write-only
     }
 
-    @Override
-    @Transient
-    public final void setEndOfBatch(final boolean endOfBatch) {
-        this.getWrappedEvent().setEndOfBatch(endOfBatch);
-    }
-
+    /**
+     * A no-op mutator to satisfy JPA requirements, as this entity is write-only.
+     *
+     * @param source Ignored.
+     */
     @SuppressWarnings("unused")
-    public void setFQCN(final String fqcn) {
+    public void setSource(final StackTraceElement source) {
         // this entity is write-only
     }
 
-    @Override
-    @Transient
-    public final void setIncludeLocation(final boolean locationRequired) {
-        this.getWrappedEvent().setIncludeLocation(locationRequired);
-    }
-
+    /**
+     * A no-op mutator to satisfy JPA requirements, as this entity is write-only.
+     *
+     * @param message Ignored.
+     */
     @SuppressWarnings("unused")
-    public void setLevel(final Level level) {
+    public void setMessage(final Message message) {
         // this entity is write-only
     }
 
+    /**
+     * A no-op mutator to satisfy JPA requirements, as this entity is write-only.
+     *
+     * @param marker Ignored.
+     */
     @SuppressWarnings("unused")
-    public void setLoggerName(final String name) {
+    public void setMarker(final Marker marker) {
         // this entity is write-only
     }
 
+    /**
+     * A no-op mutator to satisfy JPA requirements, as this entity is write-only.
+     *
+     * @param threadName Ignored.
+     */
     @SuppressWarnings("unused")
-    public void setMarker(final Marker marker) {
+    public void setThreadName(final String threadName) {
         // this entity is write-only
     }
 
+    /**
+     * A no-op mutator to satisfy JPA requirements, as this entity is write-only.
+     *
+     * @param millis Ignored.
+     */
     @SuppressWarnings("unused")
-    public void setMessage(final Message message) {
+    public void setMillis(final long millis) {
         // this entity is write-only
     }
 
+    /**
+     * A no-op mutator to satisfy JPA requirements, as this entity is write-only.
+     *
+     * @param throwable Ignored.
+     */
     @SuppressWarnings("unused")
-    public void setMillis(final long millis) {
+    public void setThrown(final Throwable throwable) {
         // this entity is write-only
     }
 
+    /**
+     * A no-op mutator to satisfy JPA requirements, as this entity is write-only.
+     *
+     * @param contextMap Ignored.
+     */
     @SuppressWarnings("unused")
-    public void setSource(final StackTraceElement element) {
+    public void setContextMap(final Map<String, String> contextMap) {
         // this entity is write-only
     }
 
+    /**
+     * A no-op mutator to satisfy JPA requirements, as this entity is write-only.
+     *
+     * @param contextStack Ignored.
+     */
     @SuppressWarnings("unused")
-    public void setThreadName(final String name) {
+    public void setContextStack(final ThreadContext.ContextStack contextStack) {
         // this entity is write-only
     }
 
+    /**
+     * A no-op mutator to satisfy JPA requirements, as this entity is write-only.
+     *
+     * @param fqcn Ignored.
+     */
     @SuppressWarnings("unused")
-    public void setThrown(final Throwable throwable) {
+    public void setFQCN(final String fqcn) {
         // this entity is write-only
     }
+
+    @Override
+    @Transient
+    public final boolean isIncludeLocation() {
+        return this.getWrappedEvent().isIncludeLocation();
+    }
+
+    @Override
+    @Transient
+    public final void setIncludeLocation(final boolean locationRequired) {
+        this.getWrappedEvent().setIncludeLocation(locationRequired);
+    }
+
+    @Override
+    @Transient
+    public final boolean isEndOfBatch() {
+        return this.getWrappedEvent().isEndOfBatch();
+    }
+
+    @Override
+    @Transient
+    public final void setEndOfBatch(final boolean endOfBatch) {
+        this.getWrappedEvent().setEndOfBatch(endOfBatch);
+    }
 }

Added: logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextMapAttributeConverter.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextMapAttributeConverter.java?rev=1481664&view=auto
==============================================================================
--- logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextMapAttributeConverter.java (added)
+++ logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextMapAttributeConverter.java Sun May 12 22:52:10 2013
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.core.appender.db.jpa.converter;
+
+import javax.persistence.AttributeConverter;
+import javax.persistence.Converter;
+import java.util.Map;
+
+/**
+ * A JPA 2.1 attribute converter for {@link Map Map&lt;String, String&gt;}s in
+ * {@link org.apache.logging.log4j.core.LogEvent}s. This converter is only capable of converting to {@link String}s. The
+ * {@link #convertToEntityAttribute(String)} method throws an {@link UnsupportedOperationException}. If you need to
+ * support converting to an entity attribute, you should use the {@link ContextMapJsonAttributeConverter} for conversion
+ * both ways.
+ */
+@Converter(autoApply = false)
+public class ContextMapAttributeConverter implements AttributeConverter<Map<String, String>, String> {
+    @Override
+    public String convertToDatabaseColumn(final Map<String, String> contextMap) {
+        return contextMap.toString();
+    }
+
+    @Override
+    public Map<String, String> convertToEntityAttribute(final String s) {
+        throw new UnsupportedOperationException("Log events can only be persisted, not extracted.");
+    }
+}

Added: logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextMapJsonAttributeConverter.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextMapJsonAttributeConverter.java?rev=1481664&view=auto
==============================================================================
--- logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextMapJsonAttributeConverter.java (added)
+++ logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextMapJsonAttributeConverter.java Sun May 12 22:52:10 2013
@@ -0,0 +1,41 @@
+package org.apache.logging.log4j.core.appender.db.jpa.converter;
+
+import org.codehaus.jackson.map.ObjectMapper;
+import org.codehaus.jackson.type.TypeReference;
+
+import javax.persistence.AttributeConverter;
+import javax.persistence.PersistenceException;
+import java.io.IOException;
+import java.util.Map;
+
+/**
+ * A JPA 2.1 attribute converter for {@link Map Map&lt;String, String&gt;}s in
+ * {@link org.apache.logging.log4j.core.LogEvent}s. This converter is capable of converting both to and from
+ * {@link String}s.
+ *
+ * In addition to other optional dependencies required by the JPA appender, this converter requires the Jackson Mapper.
+ */
+public class ContextMapJsonAttributeConverter implements AttributeConverter<Map<String, String>, String> {
+    static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
+
+    @Override
+    public String convertToDatabaseColumn(final Map<String, String> contextMap) {
+        try {
+            return OBJECT_MAPPER.writeValueAsString(contextMap);
+        } catch (IOException e) {
+            throw new PersistenceException("Failed to convert map to JSON string.", e);
+        }
+    }
+
+    @Override
+    public Map<String, String> convertToEntityAttribute(final String s) {
+        if (s == null || s.length() == 0) {
+            return null;
+        }
+        try {
+            return OBJECT_MAPPER.readValue(s, new TypeReference<Map<String, String>>() { });
+        } catch (IOException e) {
+            throw new PersistenceException("Failed to convert JSON string to map.", e);
+        }
+    }
+}

Added: logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextStackAttributeConverter.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextStackAttributeConverter.java?rev=1481664&view=auto
==============================================================================
--- logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextStackAttributeConverter.java (added)
+++ logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextStackAttributeConverter.java Sun May 12 22:52:10 2013
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.core.appender.db.jpa.converter;
+
+import org.apache.logging.log4j.ThreadContext;
+
+import javax.persistence.AttributeConverter;
+import javax.persistence.Converter;
+
+/**
+ * A JPA 2.1 attribute converter for {@link ThreadContext.ContextStack}s in
+ * {@link org.apache.logging.log4j.core.LogEvent}s. This converter is only capable of converting to {@link String}s. The
+ * {@link #convertToEntityAttribute(String)} method throws an {@link UnsupportedOperationException}. If you need to
+ * support converting to an entity attribute, you should use the {@link ContextStackJsonAttributeConverter} for
+ * conversion both ways.
+ */
+@Converter(autoApply = false)
+public class ContextStackAttributeConverter implements AttributeConverter<ThreadContext.ContextStack, String> {
+    @Override
+    public String convertToDatabaseColumn(final ThreadContext.ContextStack contextStack) {
+        StringBuilder builder = new StringBuilder();
+        for (String value : contextStack.asList()) {
+            if (builder.length() > 0) {
+                builder.append('\n');
+            }
+            builder.append(value);
+        }
+        return builder.toString();
+    }
+
+    @Override
+    public ThreadContext.ContextStack convertToEntityAttribute(final String s) {
+        throw new UnsupportedOperationException("Log events can only be persisted, not extracted.");
+    }
+}

Added: logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextStackJsonAttributeConverter.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextStackJsonAttributeConverter.java?rev=1481664&view=auto
==============================================================================
--- logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextStackJsonAttributeConverter.java (added)
+++ logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextStackJsonAttributeConverter.java Sun May 12 22:52:10 2013
@@ -0,0 +1,43 @@
+package org.apache.logging.log4j.core.appender.db.jpa.converter;
+
+import org.apache.logging.log4j.ThreadContext;
+import org.codehaus.jackson.type.TypeReference;
+
+import javax.persistence.AttributeConverter;
+import javax.persistence.PersistenceException;
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * A JPA 2.1 attribute converter for {@link ThreadContext.ContextStack}s in
+ * {@link org.apache.logging.log4j.core.LogEvent}s. This converter is capable of converting both to and from
+ * {@link String}s.
+ *
+ * In addition to other optional dependencies required by the JPA appender, this converter requires the Jackson Mapper.
+ */
+public class ContextStackJsonAttributeConverter implements AttributeConverter<ThreadContext.ContextStack, String> {
+    @Override
+    public String convertToDatabaseColumn(final ThreadContext.ContextStack contextStack) {
+        try {
+            return ContextMapJsonAttributeConverter.OBJECT_MAPPER.writeValueAsString(contextStack.asList());
+        } catch (IOException e) {
+            throw new PersistenceException("Failed to convert stack list to JSON string.", e);
+        }
+    }
+
+    @Override
+    public ThreadContext.ContextStack convertToEntityAttribute(final String s) {
+        if (s == null || s.length() == 0) {
+            return null;
+        }
+
+        List<String> list;
+        try {
+            list = ContextMapJsonAttributeConverter.OBJECT_MAPPER.readValue(s, new TypeReference<List<String>>(){ });
+        } catch (IOException e) {
+            throw new PersistenceException("Failed to convert JSON string to list for stack.", e);
+        }
+
+        return new ThreadContext.ImmutableStack(list);
+    }
+}

Added: logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/converter/MarkerAttributeConverter.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/converter/MarkerAttributeConverter.java?rev=1481664&view=auto
==============================================================================
--- logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/converter/MarkerAttributeConverter.java (added)
+++ logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/converter/MarkerAttributeConverter.java Sun May 12 22:52:10 2013
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.core.appender.db.jpa.converter;
+
+import org.apache.logging.log4j.Marker;
+import org.apache.logging.log4j.MarkerManager;
+
+import javax.persistence.AttributeConverter;
+import javax.persistence.Converter;
+
+/**
+ * A JPA 2.1 attribute converter for {@link Marker}s in {@link org.apache.logging.log4j.core.LogEvent}s. This
+ * converter is capable of converting both to and from {@link String}s.
+ */
+@Converter(autoApply = false)
+public class MarkerAttributeConverter implements AttributeConverter<Marker, String> {
+    @Override
+    public String convertToDatabaseColumn(final Marker marker) {
+        StringBuilder builder = new StringBuilder(marker.getName());
+        Marker parent = marker.getParent();
+        int levels = 0;
+        boolean hasParent = false;
+        while (parent != null) {
+            levels++;
+            hasParent = true;
+            builder.append("[ ").append(parent.getName());
+            parent = parent.getParent();
+        }
+        for (int i = 0; i < levels; i++) {
+            builder.append(" ]");
+        }
+        if (hasParent) {
+            builder.append(" ]");
+        }
+        return builder.toString();
+    }
+
+    @Override
+    public Marker convertToEntityAttribute(final String s) {
+        int bracket = s.indexOf("[");
+
+        return bracket < 1 ? MarkerManager.getMarker(s) : MarkerManager.getMarker(s.substring(0, bracket));
+    }
+}

Added: logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/converter/MessageAttributeConverter.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/converter/MessageAttributeConverter.java?rev=1481664&view=auto
==============================================================================
--- logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/converter/MessageAttributeConverter.java (added)
+++ logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/converter/MessageAttributeConverter.java Sun May 12 22:52:10 2013
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.core.appender.db.jpa.converter;
+
+import org.apache.logging.log4j.message.Message;
+import org.apache.logging.log4j.status.StatusLogger;
+
+import javax.persistence.AttributeConverter;
+import javax.persistence.Converter;
+
+/**
+ * A JPA 2.1 attribute converter for {@link Message}s in {@link org.apache.logging.log4j.core.LogEvent}s. This
+ * converter is capable of converting both to and from {@link String}s.
+ */
+@Converter(autoApply = false)
+public class MessageAttributeConverter implements AttributeConverter<Message, String> {
+    private static final StatusLogger log = StatusLogger.getLogger();
+
+    @Override
+    public String convertToDatabaseColumn(final Message message) {
+        return message.getFormattedMessage();
+    }
+
+    @Override
+    public Message convertToEntityAttribute(final String s) {
+        return log.getMessageFactory().newMessage(s);
+    }
+}

Added: logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/converter/StackTraceElementAttributeConverter.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/converter/StackTraceElementAttributeConverter.java?rev=1481664&view=auto
==============================================================================
--- logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/converter/StackTraceElementAttributeConverter.java (added)
+++ logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/converter/StackTraceElementAttributeConverter.java Sun May 12 22:52:10 2013
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.core.appender.db.jpa.converter;
+
+import javax.persistence.AttributeConverter;
+
+/**
+ * A JPA 2.1 attribute converter for {@link StackTraceElement}s in {@link org.apache.logging.log4j.core.LogEvent}s. This
+ * converter is capable of converting both to and from {@link String}s.
+ */
+public class StackTraceElementAttributeConverter implements AttributeConverter<StackTraceElement, String> {
+    @Override
+    public String convertToDatabaseColumn(final StackTraceElement element) {
+        return element.toString();
+    }
+
+    @Override
+    public StackTraceElement convertToEntityAttribute(final String s) {
+        return StackTraceElementAttributeConverter.convertString(s);
+    }
+
+    static StackTraceElement convertString(final String s) {
+        int open = s.indexOf("(");
+
+        String classMethod = s.substring(0, open);
+        String className = classMethod.substring(0, classMethod.lastIndexOf("."));
+        String methodName = classMethod.substring(classMethod.lastIndexOf(".") + 1);
+
+        String parenthesisContents = s.substring(open + 1, s.indexOf(")"));
+
+        String fileName = null;
+        int lineNumber = -1;
+        if ("Native Method".equals(parenthesisContents)) {
+            lineNumber = -2;
+        } else if (!"Unknown Source".equals(parenthesisContents)) {
+            int colon = parenthesisContents.indexOf(":");
+            if (colon > -1) {
+                fileName = parenthesisContents.substring(0, colon);
+                try {
+                    lineNumber = Integer.parseInt(parenthesisContents.substring(colon + 1));
+                } catch (NumberFormatException ignore) {
+                    // we don't care
+                }
+            } else {
+                fileName = parenthesisContents.substring(0);
+            }
+        }
+
+        return new StackTraceElement(className, methodName, fileName, lineNumber);
+    }
+}

Added: logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ThrowableAttributeConverter.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ThrowableAttributeConverter.java?rev=1481664&view=auto
==============================================================================
--- logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ThrowableAttributeConverter.java (added)
+++ logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ThrowableAttributeConverter.java Sun May 12 22:52:10 2013
@@ -0,0 +1,227 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.core.appender.db.jpa.converter;
+
+import javax.persistence.AttributeConverter;
+import javax.persistence.Converter;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.ListIterator;
+
+/**
+ * A JPA 2.1 attribute converter for {@link Throwable}s in {@link org.apache.logging.log4j.core.LogEvent}s. This
+ * converter is capable of converting both to and from {@link String}s.
+ */
+@Converter(autoApply = false)
+public class ThrowableAttributeConverter implements AttributeConverter<Throwable, String> {
+    private static final Field THROWABLE_CAUSE;
+
+    private static final Field THROWABLE_MESSAGE;
+
+    static {
+        try {
+            THROWABLE_CAUSE = Throwable.class.getDeclaredField("cause");
+            THROWABLE_CAUSE.setAccessible(true);
+            THROWABLE_MESSAGE = Throwable.class.getDeclaredField("detailMessage");
+            THROWABLE_MESSAGE.setAccessible(true);
+        } catch (NoSuchFieldException e) {
+            throw new IllegalStateException("Something is wrong with java.lang.Throwable.", e);
+        }
+    }
+
+    @Override
+    public String convertToDatabaseColumn(final Throwable throwable) {
+        StringBuilder builder = new StringBuilder();
+        this.convertThrowable(builder, throwable);
+        return builder.toString();
+    }
+
+    private void convertThrowable(final StringBuilder builder, final Throwable throwable) {
+        builder.append(throwable.toString()).append('\n');
+        for (StackTraceElement element : throwable.getStackTrace()) {
+            builder.append("\tat ").append(element).append('\n');
+        }
+        if (throwable.getCause() != null) {
+            builder.append("Caused by ");
+            this.convertThrowable(builder, throwable.getCause());
+        }
+    }
+
+    @Override
+    public Throwable convertToEntityAttribute(final String s) {
+        if (s == null || s.length() == 0) {
+            return null;
+        }
+
+        List<String> lines = Arrays.asList(s.split("(\n|\r\n)"));
+        return this.convertString(lines.listIterator(), false);
+    }
+
+    private Throwable convertString(final ListIterator<String> lines, boolean removeCausedBy) {
+        String firstLine = lines.next();
+        if (removeCausedBy) {
+            firstLine = firstLine.substring(10);
+        }
+        int colon = firstLine.indexOf(":");
+        String throwableClassName;
+        String message = null;
+        if (colon > 1) {
+            throwableClassName = firstLine.substring(0, colon);
+            if (firstLine.length() > colon + 1) {
+                message = firstLine.substring(colon + 1).trim();
+            }
+        } else {
+            throwableClassName = firstLine;
+        }
+
+        List<StackTraceElement> stackTrace = new ArrayList<StackTraceElement>();
+        Throwable cause = null;
+        while (lines.hasNext()) {
+            String line = lines.next();
+
+            if (line.startsWith("Caused by ")) {
+                lines.previous();
+                cause = convertString(lines, true);
+                break;
+            }
+
+            stackTrace.add(
+                    StackTraceElementAttributeConverter.convertString(line.trim().substring(3).trim())
+            );
+        }
+
+        return this.getThrowable(throwableClassName, message, cause,
+                stackTrace.toArray(new StackTraceElement[stackTrace.size()]));
+    }
+
+    private Throwable getThrowable(final String throwableClassName, final String message, final Throwable cause,
+                                   final StackTraceElement[] stackTrace) {
+        try {
+            @SuppressWarnings("unchecked")
+            Class<Throwable> throwableClass = (Class<Throwable>)Class.forName(throwableClassName);
+
+            if (!Throwable.class.isAssignableFrom(throwableClass)) {
+                return null;
+            }
+
+            Throwable throwable;
+            if (message != null && cause != null) {
+                throwable = this.getThrowable(throwableClass, message, cause);
+                if (throwable == null) {
+                    throwable = this.getThrowable(throwableClass, cause);
+                    if (throwable == null) {
+                        throwable = this.getThrowable(throwableClass, message);
+                        if (throwable == null) {
+                            throwable = this.getThrowable(throwableClass);
+                            if (throwable != null) {
+                                THROWABLE_MESSAGE.set(throwable, message);
+                                THROWABLE_CAUSE.set(throwable, cause);
+                            }
+                        } else {
+                            THROWABLE_CAUSE.set(throwable, cause);
+                        }
+                    } else {
+                        THROWABLE_MESSAGE.set(throwable, message);
+                    }
+                }
+            } else if (cause != null) {
+                throwable = this.getThrowable(throwableClass, cause);
+                if (throwable == null) {
+                    throwable = this.getThrowable(throwableClass);
+                    if (throwable != null) {
+                        THROWABLE_CAUSE.set(throwable, cause);
+                    }
+                }
+            } else if (message != null) {
+                throwable = this.getThrowable(throwableClass, message);
+                if (throwable == null) {
+                    throwable = this.getThrowable(throwableClass);
+                    if (throwable != null) {
+                        THROWABLE_MESSAGE.set(throwable, cause);
+                    }
+                }
+            } else {
+                throwable = this.getThrowable(throwableClass);
+            }
+
+            if (throwable == null) {
+                return null;
+            } else {
+                throwable.setStackTrace(stackTrace);
+                return throwable;
+            }
+        } catch (Exception e) {
+            return null;
+        }
+    }
+
+    private Throwable getThrowable(final Class<Throwable> throwableClass, final String message, final Throwable cause) {
+        try {
+            @SuppressWarnings("unchecked")
+            Constructor<Throwable>[] constructors = (Constructor<Throwable>[])throwableClass.getConstructors();
+            for (Constructor<Throwable> constructor : constructors) {
+                Class<?>[] parameterTypes = constructor.getParameterTypes();
+                if (parameterTypes.length == 2) {
+                    if (String.class == parameterTypes[0] && Throwable.class.isAssignableFrom(parameterTypes[1])) {
+                        return constructor.newInstance(message, cause);
+                    } else if (String.class == parameterTypes[1] &&
+                            Throwable.class.isAssignableFrom(parameterTypes[0])) {
+                        return constructor.newInstance(cause, message);
+                    }
+                }
+            }
+            return null;
+        } catch (Exception e) {
+            return null;
+        }
+    }
+
+    private Throwable getThrowable(final Class<Throwable> throwableClass, final Throwable cause) {
+        try {
+            @SuppressWarnings("unchecked")
+            Constructor<Throwable>[] constructors = (Constructor<Throwable>[])throwableClass.getConstructors();
+            for (Constructor<Throwable> constructor : constructors) {
+                Class<?>[] parameterTypes = constructor.getParameterTypes();
+                if (parameterTypes.length == 1 && Throwable.class.isAssignableFrom(parameterTypes[0])) {
+                    return constructor.newInstance(cause);
+                }
+            }
+            return null;
+        } catch (Exception e) {
+            return null;
+        }
+    }
+
+    private Throwable getThrowable(final Class<Throwable> throwableClass, final String message) {
+        try {
+            return throwableClass.getConstructor(String.class).newInstance(message);
+        } catch (Exception e) {
+            return null;
+        }
+    }
+
+    private Throwable getThrowable(final Class<Throwable> throwableClass) {
+        try {
+            return throwableClass.newInstance();
+        } catch (Exception e) {
+            return null;
+        }
+    }
+}

Added: logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/converter/package-info.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/converter/package-info.java?rev=1481664&view=auto
==============================================================================
--- logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/converter/package-info.java (added)
+++ logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/converter/package-info.java Sun May 12 22:52:10 2013
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+/**
+ * The converters in this package implement the JPA 2.1 mechanism for converting non-standard types to and from
+ * database fields. Most of these types are capable of two-way conversion and can be used to both persist and retrieve
+ * entities. The {@link ContextMapAttributeConverter} and {@link ContextStackAttributeConverter} only support
+ * persistence and not retrieval, persisting the type as a simple string. You can use the
+ * {@link ContextMapJsonAttributeConverter} and {@link ContextStackJsonAttributeConverter} instead, which require the
+ * Jackson Mapper dependency to also be on your class path.
+ */
+package org.apache.logging.log4j.core.appender.db.jpa.converter;

Modified: logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/package-info.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/package-info.java?rev=1481664&r1=1481663&r2=1481664&view=diff
==============================================================================
--- logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/package-info.java (original)
+++ logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/package-info.java Sun May 12 22:52:10 2013
@@ -16,9 +16,8 @@
  */
 /**
  * The JPA Appender supports writing log events to a relational database using the Java Persistence API. You will need
- * a JDBC driver on your classpath for the database you wish to log to. You will also need the Java Persistence API
+ * a JDBC driver on your classpath for the database you wish to log to. You will also need the Java Persistence API 2.1
  * and your JPA provider of choice on the class path; these Maven dependencies are optional and will not automatically
  * be added to your classpath.
  */
 package org.apache.logging.log4j.core.appender.db.jpa;
-

Modified: logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/db/jdbc/JDBCAppenderTest.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/db/jdbc/JDBCAppenderTest.java?rev=1481664&r1=1481663&r2=1481664&view=diff
==============================================================================
--- logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/db/jdbc/JDBCAppenderTest.java (original)
+++ logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/db/jdbc/JDBCAppenderTest.java Sun May 12 22:52:10 2013
@@ -16,11 +16,20 @@
  */
 package org.apache.logging.log4j.core.appender.db.jdbc;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.Appender;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.config.ConfigurationFactory;
+import org.apache.logging.log4j.core.config.DefaultConfiguration;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.easymock.IAnswer;
+import org.junit.After;
+import org.junit.Test;
+import org.mockejb.jndi.MockContextFactory;
 
+import javax.naming.InitialContext;
+import javax.sql.DataSource;
 import java.io.ByteArrayOutputStream;
 import java.io.PrintWriter;
 import java.sql.Connection;
@@ -30,31 +39,20 @@ import java.sql.SQLException;
 import java.sql.Statement;
 import java.util.Map;
 
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-import org.apache.logging.log4j.core.Appender;
-import org.apache.logging.log4j.core.LoggerContext;
-import org.apache.logging.log4j.core.config.ConfigurationFactory;
-import org.apache.logging.log4j.core.config.DefaultConfiguration;
-import org.apache.logging.log4j.status.StatusLogger;
-import org.junit.After;
-import org.junit.Test;
+import static org.easymock.EasyMock.*;
+import static org.junit.Assert.*;
 
 public class JDBCAppenderTest {
-    @SuppressWarnings("unused")
-    public static Connection testFactoryMethodConfigMethod() throws SQLException {
-        return DriverManager.getConnection("jdbc:hsqldb:mem:Log4j;ifexists=true", "sa", "");
-    }
-
     private Connection connection;
 
     public void setUp(final String tableName, final String configFileName) throws SQLException {
         this.connection = DriverManager.getConnection("jdbc:hsqldb:mem:Log4j", "sa", "");
 
         final Statement statement = this.connection.createStatement();
-        statement.executeUpdate("CREATE TABLE " + tableName + " ( "
-                + "id INTEGER IDENTITY, eventDate DATETIME, literalColumn VARCHAR(255), level VARCHAR(10), "
-                + "logger VARCHAR(255), message VARCHAR(1024), exception VARCHAR(1048576)" + " )");
+        statement.executeUpdate("CREATE TABLE " + tableName + " ( " +
+                    "id INTEGER IDENTITY, eventDate DATETIME, literalColumn VARCHAR(255), level VARCHAR(10), "  +
+                    "logger VARCHAR(255), message VARCHAR(1024), exception VARCHAR(1048576)" +
+                " )");
         statement.close();
 
         System.setProperty(ConfigurationFactory.CONFIGURATION_FILE_PROPERTY,
@@ -192,4 +190,77 @@ public class JDBCAppenderTest {
 
         assertFalse("There should not be three rows.", resultSet.next());
     }
+
+    @Test
+    public void testDataSourceConfig() throws Exception {
+        System.out.println("Before creating mock data source.");
+        DataSource dataSource = createStrictMock(DataSource.class);
+
+        expect(dataSource.getConnection()).andAnswer(new IAnswer<Connection>() {
+            @Override
+            public Connection answer() throws Throwable {
+                return DriverManager.getConnection("jdbc:hsqldb:mem:Log4j", "sa", "");
+            }
+        }).atLeastOnce();
+        replay(dataSource);
+
+        System.out.println("Before creating mock context.");
+        MockContextFactory.setAsInitial();
+
+        System.out.println("Before instantiating context.");
+        InitialContext context = new InitialContext();
+        context.createSubcontext("java:");
+        context.createSubcontext("java:/comp");
+        context.createSubcontext("java:/comp/env");
+        context.createSubcontext("java:/comp/env/jdbc");
+
+        System.out.println("Before binding data source.");
+        context.bind("java:/comp/env/jdbc/TestDataSourceAppender", dataSource);
+
+        try {
+            System.out.println("Before setting up.");
+            this.setUp("dsLogEntry", "log4j2-data-source.xml");
+
+            System.out.println("After setting up.");
+            final Error exception = new Error("Final error massage is fatal!");
+            final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+            final PrintWriter writer = new PrintWriter(outputStream);
+            exception.printStackTrace(writer);
+            writer.close();
+            final String stackTrace = outputStream.toString();
+
+            final long millis = System.currentTimeMillis();
+
+            final Logger logger = LogManager.getLogger(this.getClass().getName() + ".testDataSourceConfig");
+            logger.trace("Data source logged message 01.");
+            logger.fatal("Error from data source 02.", exception);
+
+            final Statement statement = this.connection.createStatement();
+            final ResultSet resultSet = statement.executeQuery("SELECT * FROM dsLogEntry ORDER BY id");
+
+            assertTrue("There should be at least one row.", resultSet.next());
+
+            long date = resultSet.getTimestamp("eventDate").getTime();
+            assertTrue("The date should be later than pre-logging (1).", date >= millis);
+            assertTrue("The date should be earlier than now (1).", date <= System.currentTimeMillis());
+            assertEquals("The literal column is not correct (1).", "Literal Value of Data Source",
+                    resultSet.getString("literalColumn"));
+            assertEquals("The level column is not correct (1).", "FATAL", resultSet.getString("level"));
+            assertEquals("The logger column is not correct (1).", logger.getName(), resultSet.getString("logger"));
+            assertEquals("The message column is not correct (1).", "Error from data source 02.",
+                    resultSet.getString("message"));
+            assertEquals("The exception column is not correct (1).", stackTrace, resultSet.getString("exception"));
+
+            assertFalse("There should not be two rows.", resultSet.next());
+
+            verify(dataSource);
+        } finally {
+            MockContextFactory.revertSetAsInitial();
+        }
+    }
+
+    @SuppressWarnings("unused")
+    public static Connection testFactoryMethodConfigMethod() throws SQLException {
+        return DriverManager.getConnection("jdbc:hsqldb:mem:Log4j;ifexists=true", "sa", "");
+    }
 }

Modified: logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/JPAAppenderTest.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/JPAAppenderTest.java?rev=1481664&r1=1481663&r2=1481664&view=diff
==============================================================================
--- logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/JPAAppenderTest.java (original)
+++ logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/JPAAppenderTest.java Sun May 12 22:52:10 2013
@@ -16,21 +16,6 @@
  */
 package org.apache.logging.log4j.core.appender.db.jpa;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-
-import java.io.ByteArrayOutputStream;
-import java.io.PrintWriter;
-import java.sql.Connection;
-import java.sql.DriverManager;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Statement;
-import java.util.Map;
-
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 import org.apache.logging.log4j.core.Appender;
@@ -39,40 +24,38 @@ import org.apache.logging.log4j.core.Log
 import org.apache.logging.log4j.core.config.ConfigurationFactory;
 import org.apache.logging.log4j.core.config.DefaultConfiguration;
 import org.apache.logging.log4j.status.StatusLogger;
+import org.junit.Ignore;
 import org.junit.Test;
 
-public class JPAAppenderTest {
-    @SuppressWarnings("unused")
-    public static class BadConstructorEntity1 extends TestEntity {
-        private static final long serialVersionUID = 1L;
-
-        public BadConstructorEntity1(final LogEvent wrappedEvent) {
-            super(wrappedEvent);
-        }
-    }
-
-    @SuppressWarnings("unused")
-    public static class BadConstructorEntity2 extends TestEntity {
-        private static final long serialVersionUID = 1L;
-
-        public BadConstructorEntity2() {
-            super(null);
-        }
+import java.io.ByteArrayOutputStream;
+import java.io.PrintWriter;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.Map;
 
-        public BadConstructorEntity2(final LogEvent wrappedEvent, final String badParameter) {
-            super(wrappedEvent);
-        }
-    }
+import static org.junit.Assert.*;
 
+public class JPAAppenderTest {
     private Connection connection;
 
     public void setUp(final String configFileName) throws SQLException {
         this.connection = DriverManager.getConnection("jdbc:hsqldb:mem:Log4j", "sa", "");
 
-        final Statement statement = this.connection.createStatement();
-        statement.executeUpdate("CREATE TABLE jpaLogEntry ( "
-                + "id INTEGER IDENTITY, eventDate DATETIME, level VARCHAR(10), logger VARCHAR(255), "
-                + "message VARCHAR(1024), exception VARCHAR(1048576)" + " )");
+        Statement statement = this.connection.createStatement();
+        statement.executeUpdate("CREATE TABLE jpaBaseLogEntry ( " +
+                    "id INTEGER IDENTITY, eventDate DATETIME, level VARCHAR(10), logger VARCHAR(255), " +
+                    "message VARCHAR(1024), exception VARCHAR(1048576)" +
+                " )");
+        statement.close();
+
+        statement = this.connection.createStatement();
+        statement.executeUpdate("CREATE TABLE jpaBasicLogEntry ( " +
+                    "id INTEGER IDENTITY, millis BIGINT, level VARCHAR(10), logger VARCHAR(255), " +
+                    "message VARCHAR(1024), thrown VARCHAR(1048576), contextMapJson VARCHAR(1048576)" +
+                " )");
         statement.close();
 
         System.setProperty(ConfigurationFactory.CONFIGURATION_FILE_PROPERTY,
@@ -115,6 +98,29 @@ public class JPAAppenderTest {
     }
 
     @Test
+    public void testNoEntityClassName() {
+        final JPAAppender appender = JPAAppender.createAppender("name", null, null, null, null, "jpaAppenderTestUnit");
+
+        assertNull("The appender should be null.", appender);
+    }
+
+    @Test
+    public void testNonLogEventEntity() {
+        final JPAAppender appender = JPAAppender.createAppender("name", null, null, null, Object.class.getName(),
+                "jpaAppenderTestUnit");
+
+        assertNull("The appender should be null.", appender);
+    }
+
+    @Test
+    public void testNoPersistenceUnitName() {
+        final JPAAppender appender = JPAAppender.createAppender("name", null, null, null, TestEntity.class.getName(),
+                null);
+
+        assertNull("The appender should be null.", appender);
+    }
+
+    @Test
     public void testBadConstructorEntity01() {
         final JPAAppender appender = JPAAppender.createAppender("name", null, null, null,
                 BadConstructorEntity1.class.getName(), "jpaAppenderTestUnit");
@@ -139,9 +145,10 @@ public class JPAAppenderTest {
     }
 
     @Test
-    public void testConfiguredAppender() throws SQLException {
+    @Ignore("until Hibernate implements support for @Convert, @Converter")
+    public void testBaseJpaEntityAppender() throws SQLException {
         try {
-            this.setUp("log4j2-jpa.xml");
+            this.setUp("log4j2-jpa-base.xml");
 
             final RuntimeException exception = new RuntimeException("Hello, world!");
             final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
@@ -152,14 +159,14 @@ public class JPAAppenderTest {
 
             final long millis = System.currentTimeMillis();
 
-            final Logger logger1 = LogManager.getLogger(this.getClass().getName() + ".testConfiguredAppender");
-            final Logger logger2 = LogManager.getLogger(this.getClass().getName() + ".testConfiguredAppenderAgain");
+            final Logger logger1 = LogManager.getLogger(this.getClass().getName() + ".testBaseJpaEntityAppender");
+            final Logger logger2 = LogManager.getLogger(this.getClass().getName() + ".testBaseJpaEntityAppenderAgain");
             logger1.info("Test my message 01.");
             logger1.error("This is another message 02.", exception);
             logger2.warn("A final warning has been issued.");
 
             final Statement statement = this.connection.createStatement();
-            final ResultSet resultSet = statement.executeQuery("SELECT * FROM jpaLogEntry ORDER BY id");
+            final ResultSet resultSet = statement.executeQuery("SELECT * FROM jpaBaseLogEntry ORDER BY id");
 
             assertTrue("There should be at least one row.", resultSet.next());
 
@@ -201,25 +208,87 @@ public class JPAAppenderTest {
     }
 
     @Test
-    public void testNoEntityClassName() {
-        final JPAAppender appender = JPAAppender.createAppender("name", null, null, null, null, "jpaAppenderTestUnit");
+    @Ignore("until Hibernate implements support for @Convert, @Converter")
+    public void testBasicJpaEntityAppender() throws SQLException {
+        try {
+            this.setUp("log4j2-jpa-basic.xml");
 
-        assertNull("The appender should be null.", appender);
+            final Error exception = new Error("Goodbye, cruel world!");
+            final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+            final PrintWriter writer = new PrintWriter(outputStream);
+            exception.printStackTrace(writer);
+            writer.close();
+            final String stackTrace = outputStream.toString();
+
+            final long millis = System.currentTimeMillis();
+
+            final Logger logger1 = LogManager.getLogger(this.getClass().getName() + ".testBasicJpaEntityAppender");
+            final Logger logger2 = LogManager.getLogger(this.getClass().getName() + ".testBasicJpaEntityAppenderAgain");
+            logger1.debug("Test my debug 01.");
+            logger1.warn("This is another warning 02.", exception);
+            logger2.fatal("A fatal warning has been issued.");
+
+            final Statement statement = this.connection.createStatement();
+            final ResultSet resultSet = statement.executeQuery("SELECT * FROM jpaBasicLogEntry ORDER BY id");
+
+            assertTrue("There should be at least one row.", resultSet.next());
+
+            long date = resultSet.getLong("millis");
+            assertTrue("The date should be later than pre-logging (1).", date >= millis);
+            assertTrue("The date should be earlier than now (1).", date <= System.currentTimeMillis());
+            assertEquals("The level column is not correct (1).", "DEBUG", resultSet.getString("level"));
+            assertEquals("The logger column is not correct (1).", logger1.getName(), resultSet.getString("logger"));
+            assertEquals("The message column is not correct (1).", "Test my debug 01.",
+                    resultSet.getString("message"));
+            assertNull("The exception column is not correct (1).", resultSet.getString("exception"));
+
+            assertTrue("There should be at least two rows.", resultSet.next());
+
+            date = resultSet.getLong("millis");
+            assertTrue("The date should be later than pre-logging (2).", date >= millis);
+            assertTrue("The date should be earlier than now (2).", date <= System.currentTimeMillis());
+            assertEquals("The level column is not correct (2).", "WARN", resultSet.getString("level"));
+            assertEquals("The logger column is not correct (2).", logger1.getName(), resultSet.getString("logger"));
+            assertEquals("The message column is not correct (2).", "This is another warning 02.",
+                    resultSet.getString("message"));
+            assertEquals("The exception column is not correct (2).", stackTrace, resultSet.getString("exception"));
+
+            assertTrue("There should be three rows.", resultSet.next());
+
+            date = resultSet.getLong("millis");
+            assertTrue("The date should be later than pre-logging (3).", date >= millis);
+            assertTrue("The date should be earlier than now (3).", date <= System.currentTimeMillis());
+            assertEquals("The level column is not correct (3).", "FATAL", resultSet.getString("level"));
+            assertEquals("The logger column is not correct (3).", logger2.getName(), resultSet.getString("logger"));
+            assertEquals("The message column is not correct (3).", "A fatal warning has been issued.",
+                    resultSet.getString("message"));
+            assertNull("The exception column is not correct (3).", resultSet.getString("exception"));
+
+            assertFalse("There should not be four rows.", resultSet.next());
+        } finally {
+            this.tearDown();
+        }
     }
 
-    @Test
-    public void testNonLogEventEntity() {
-        final JPAAppender appender = JPAAppender.createAppender("name", null, null, null, Object.class.getName(),
-                "jpaAppenderTestUnit");
+    @SuppressWarnings("unused")
+    public static class BadConstructorEntity1 extends TestEntity {
+        private static final long serialVersionUID = 1L;
 
-        assertNull("The appender should be null.", appender);
+        public BadConstructorEntity1(final LogEvent wrappedEvent) {
+            super(wrappedEvent);
+        }
     }
 
-    @Test
-    public void testNoPersistenceUnitName() {
-        final JPAAppender appender = JPAAppender.createAppender("name", null, null, null, TestEntity.class.getName(),
-                null);
+    @SuppressWarnings("unused")
+    public static class BadConstructorEntity2 extends TestEntity {
+        private static final long serialVersionUID = 1L;
 
-        assertNull("The appender should be null.", appender);
+        public BadConstructorEntity2() {
+            super(null);
+        }
+
+        public BadConstructorEntity2(final LogEvent wrappedEvent, final String badParameter) {
+            super(wrappedEvent);
+        }
     }
 }

Added: logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/TestBasicEntity.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/TestBasicEntity.java?rev=1481664&view=auto
==============================================================================
--- logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/TestBasicEntity.java (added)
+++ logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/TestBasicEntity.java Sun May 12 22:52:10 2013
@@ -0,0 +1,81 @@
+package org.apache.logging.log4j.core.appender.db.jpa;
+
+import org.apache.logging.log4j.Marker;
+import org.apache.logging.log4j.ThreadContext;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.appender.db.jpa.converter.ContextMapJsonAttributeConverter;
+
+import javax.persistence.Column;
+import javax.persistence.Convert;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.Table;
+import javax.persistence.Transient;
+import java.util.Map;
+
+@Entity
+@Table(name = "jpaBasicLogEntry")
+@SuppressWarnings("unused")
+public class TestBasicEntity extends LogEventEntity {
+    private static final long serialVersionUID = 1L;
+
+    private long id = 0L;
+
+    public TestBasicEntity() {
+        super();
+    }
+
+    public TestBasicEntity(final LogEvent wrapped) {
+        super(wrapped);
+    }
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    @Column(name = "id")
+    public long getId() {
+        return this.id;
+    }
+
+    public void setId(final long id) {
+        this.id = id;
+    }
+
+    @Override
+    @Convert(converter = ContextMapJsonAttributeConverter.class)
+    @Column(name = "contextMapJson")
+    public Map<String, String> getContextMap() {
+        return super.getContextMap();
+    }
+
+    @Override
+    @Transient
+    public StackTraceElement getSource() {
+        return super.getSource();
+    }
+
+    @Override
+    @Transient
+    public Marker getMarker() {
+        return super.getMarker();
+    }
+
+    @Override
+    @Transient
+    public String getThreadName() {
+        return super.getThreadName();
+    }
+
+    @Override
+    @Transient
+    public ThreadContext.ContextStack getContextStack() {
+        return super.getContextStack();
+    }
+
+    @Override
+    @Transient
+    public String getFQCN() {
+        return super.getFQCN();
+    }
+}

Modified: logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/TestEntity.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/TestEntity.java?rev=1481664&r1=1481663&r2=1481664&view=diff
==============================================================================
--- logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/TestEntity.java (original)
+++ logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/TestEntity.java Sun May 12 22:52:10 2013
@@ -16,13 +16,17 @@
  */
 package org.apache.logging.log4j.core.appender.db.jpa;
 
-import java.io.ByteArrayOutputStream;
-import java.io.PrintWriter;
-import java.util.Date;
-import java.util.Map;
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.Marker;
+import org.apache.logging.log4j.ThreadContext;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.appender.db.jpa.converter.MessageAttributeConverter;
+import org.apache.logging.log4j.core.appender.db.jpa.converter.ThrowableAttributeConverter;
+import org.apache.logging.log4j.message.Message;
 
 import javax.persistence.Basic;
 import javax.persistence.Column;
+import javax.persistence.Convert;
 import javax.persistence.Entity;
 import javax.persistence.EnumType;
 import javax.persistence.Enumerated;
@@ -33,15 +37,11 @@ import javax.persistence.Table;
 import javax.persistence.Temporal;
 import javax.persistence.TemporalType;
 import javax.persistence.Transient;
-
-import org.apache.logging.log4j.Level;
-import org.apache.logging.log4j.Marker;
-import org.apache.logging.log4j.ThreadContext;
-import org.apache.logging.log4j.core.LogEvent;
-import org.apache.logging.log4j.message.Message;
+import java.util.Date;
+import java.util.Map;
 
 @Entity
-@Table(name = "jpaLogEntry")
+@Table(name = "jpaBaseLogEntry")
 @SuppressWarnings("unused")
 public class TestEntity extends LogEventWrapperEntity {
     private static final long serialVersionUID = 1L;
@@ -49,23 +49,22 @@ public class TestEntity extends LogEvent
     private long id = 0L;
 
     public TestEntity() {
-        super(null);
+        super();
     }
 
     public TestEntity(final LogEvent wrappedEvent) {
         super(wrappedEvent);
     }
 
-    @Override
-    @Transient
-    public Map<String, String> getContextMap() {
-        return getWrappedEvent().getContextMap();
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    @Column(name = "id")
+    public long getId() {
+        return this.id;
     }
 
-    @Override
-    @Transient
-    public ThreadContext.ContextStack getContextStack() {
-        return getWrappedEvent().getContextStack();
+    public void setId(final long id) {
+        this.id = id;
     }
 
     @Temporal(TemporalType.TIMESTAMP)
@@ -74,31 +73,8 @@ public class TestEntity extends LogEvent
         return new Date(this.getMillis());
     }
 
-    @Basic
-    @Column(name = "exception")
-    @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
-    public String getException() {
-        if (this.getThrown() != null) {
-            final ByteArrayOutputStream stream = new ByteArrayOutputStream();
-            final PrintWriter writer = new PrintWriter(stream);
-            this.getThrown().printStackTrace(writer);
-            writer.close();
-            return stream.toString();
-        }
-        return null;
-    }
-
-    @Override
-    @Transient
-    public String getFQCN() {
-        return getWrappedEvent().getFQCN();
-    }
-
-    @Id
-    @GeneratedValue(strategy = GenerationType.IDENTITY)
-    @Column(name = "id")
-    public long getId() {
-        return this.id;
+    public void setEventDate(final Date date) {
+        // this entity is write-only
     }
 
     @Override
@@ -117,59 +93,56 @@ public class TestEntity extends LogEvent
 
     @Override
     @Transient
-    public Marker getMarker() {
-        return getWrappedEvent().getMarker();
+    public StackTraceElement getSource() {
+        return getWrappedEvent().getSource();
     }
 
     @Override
-    @Transient
+    @Convert(converter = MessageAttributeConverter.class)
     public Message getMessage() {
         return getWrappedEvent().getMessage();
     }
 
-    @Basic
-    @Column(name = "message")
-    public String getMessageString() {
-        return this.getMessage().getFormattedMessage();
-    }
-
     @Override
     @Transient
-    public long getMillis() {
-        return getWrappedEvent().getMillis();
+    public Marker getMarker() {
+        return getWrappedEvent().getMarker();
     }
 
     @Override
     @Transient
-    public StackTraceElement getSource() {
-        return getWrappedEvent().getSource();
+    public String getThreadName() {
+        return getWrappedEvent().getThreadName();
     }
 
     @Override
     @Transient
-    public String getThreadName() {
-        return getWrappedEvent().getThreadName();
+    public long getMillis() {
+        return getWrappedEvent().getMillis();
     }
 
     @Override
-    @Transient
+    @Convert(converter = ThrowableAttributeConverter.class)
+    @Column(name = "exception")
     public Throwable getThrown() {
         return getWrappedEvent().getThrown();
     }
 
-    public void setEventDate(final Date date) {
-        // this entity is write-only
-    }
-
-    public void setException(final String exception) {
-        // this entity is write-only
+    @Override
+    @Transient
+    public Map<String, String> getContextMap() {
+        return getWrappedEvent().getContextMap();
     }
 
-    public void setId(final long id) {
-        this.id = id;
+    @Override
+    @Transient
+    public ThreadContext.ContextStack getContextStack() {
+        return getWrappedEvent().getContextStack();
     }
 
-    public void setMessageString(final String messageString) {
-        // this entity is write-only
+    @Override
+    @Transient
+    public String getFQCN() {
+        return getWrappedEvent().getFQCN();
     }
 }

Added: logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextMapAttributeConverterTest.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextMapAttributeConverterTest.java?rev=1481664&view=auto
==============================================================================
--- logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextMapAttributeConverterTest.java (added)
+++ logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextMapAttributeConverterTest.java Sun May 12 22:52:10 2013
@@ -0,0 +1,50 @@
+package org.apache.logging.log4j.core.appender.db.jpa.converter;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.Assert.*;
+
+public class ContextMapAttributeConverterTest {
+    private ContextMapAttributeConverter converter;
+
+    @Before
+    public void setUp() {
+        this.converter = new ContextMapAttributeConverter();
+    }
+
+    @After
+    public void tearDown() {
+
+    }
+
+    @Test
+    public void testConvertToDatabaseColumn01() {
+        Map<String, String> map = new HashMap<String, String>();
+        map.put("test1", "another1");
+        map.put("key2", "value2");
+
+        assertEquals("The converted value is not correct.", map.toString(),
+                this.converter.convertToDatabaseColumn(map));
+    }
+
+    @Test
+    public void testConvertToDatabaseColumn02() {
+        Map<String, String> map = new HashMap<String, String>();
+        map.put("someKey", "coolValue");
+        map.put("anotherKey", "testValue");
+        map.put("myKey", "yourValue");
+
+        assertEquals("The converted value is not correct.", map.toString(),
+                this.converter.convertToDatabaseColumn(map));
+    }
+
+    @Test(expected = UnsupportedOperationException.class)
+    public void testConvertToEntityAttribute() {
+        this.converter.convertToEntityAttribute(null);
+    }
+}

Added: logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextMapJsonAttributeConverterTest.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextMapJsonAttributeConverterTest.java?rev=1481664&view=auto
==============================================================================
--- logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextMapJsonAttributeConverterTest.java (added)
+++ logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextMapJsonAttributeConverterTest.java Sun May 12 22:52:10 2013
@@ -0,0 +1,57 @@
+package org.apache.logging.log4j.core.appender.db.jpa.converter;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.Assert.*;
+
+public class ContextMapJsonAttributeConverterTest {
+    private ContextMapJsonAttributeConverter converter;
+
+    @Before
+    public void setUp() {
+        this.converter = new ContextMapJsonAttributeConverter();
+    }
+
+    @After
+    public void tearDown() {
+
+    }
+
+    @Test
+    public void testConvert01() {
+        Map<String, String> map = new HashMap<String, String>();
+        map.put("test1", "another1");
+        map.put("key2", "value2");
+
+        String converted = this.converter.convertToDatabaseColumn(map);
+
+        assertNotNull("The converted value should not be null.", converted);
+
+        Map<String, String> reversed = this.converter.convertToEntityAttribute(converted);
+
+        assertNotNull("The reversed value should not be null.", reversed);
+        assertEquals("The reversed value is not correct.", map, reversed);
+    }
+
+    @Test
+    public void testConvert02() {
+        Map<String, String> map = new HashMap<String, String>();
+        map.put("someKey", "coolValue");
+        map.put("anotherKey", "testValue");
+        map.put("myKey", "yourValue");
+
+        String converted = this.converter.convertToDatabaseColumn(map);
+
+        assertNotNull("The converted value should not be null.", converted);
+
+        Map<String, String> reversed = this.converter.convertToEntityAttribute(converted);
+
+        assertNotNull("The reversed value should not be null.", reversed);
+        assertEquals("The reversed value is not correct.", map, reversed);
+    }
+}

Added: logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextStackAttributeConverterTest.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextStackAttributeConverterTest.java?rev=1481664&view=auto
==============================================================================
--- logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextStackAttributeConverterTest.java (added)
+++ logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextStackAttributeConverterTest.java Sun May 12 22:52:10 2013
@@ -0,0 +1,45 @@
+package org.apache.logging.log4j.core.appender.db.jpa.converter;
+
+import org.apache.logging.log4j.ThreadContext;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Arrays;
+
+import static org.junit.Assert.*;
+
+public class ContextStackAttributeConverterTest {
+    private ContextStackAttributeConverter converter;
+
+    @Before
+    public void setUp() {
+        this.converter = new ContextStackAttributeConverter();
+    }
+
+    @After
+    public void tearDown() {
+
+    }
+
+    @Test
+    public void testConvertToDatabaseColumn01() {
+        ThreadContext.ContextStack stack = new ThreadContext.ImmutableStack(Arrays.asList("value1", "another2"));
+
+        assertEquals("The converted value is not correct.", "value1\nanother2",
+                this.converter.convertToDatabaseColumn(stack));
+    }
+
+    @Test
+    public void testConvertToDatabaseColumn02() {
+        ThreadContext.ContextStack stack = new ThreadContext.ImmutableStack(Arrays.asList("key1", "value2", "my3"));
+
+        assertEquals("The converted value is not correct.", "key1\nvalue2\nmy3",
+                this.converter.convertToDatabaseColumn(stack));
+    }
+
+    @Test(expected = UnsupportedOperationException.class)
+    public void testConvertToEntityAttribute() {
+        this.converter.convertToEntityAttribute(null);
+    }
+}



Mime
View raw message