LOG4J2-1447 JPA appender changes for the migration of Map<String,String> to 
ContextData


Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo
Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/3cb8aa6c
Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/3cb8aa6c
Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/3cb8aa6c

Branch: 
refs/heads/LOG4J2-1010&LOG4J2-1447-injectable-contextdata&better-datastructure
Commit: 3cb8aa6cb799e7da4796092cb7e00a2b99043db4
Parents: 10dad1f
Author: rpopma <[email protected]>
Authored: Wed Jul 27 01:22:03 2016 +0900
Committer: rpopma <[email protected]>
Committed: Wed Jul 27 01:22:03 2016 +0900

----------------------------------------------------------------------
 .../db/jpa/AbstractLogEventWrapperEntity.java   | 31 ++++++-
 .../appender/db/jpa/BasicLogEventEntity.java    |  1 -
 .../ContextDataAttributeConverter.java          | 46 ++++++++++
 .../ContextDataJsonAttributeConverter.java      | 92 ++++++++++++++++++++
 .../core/appender/db/jpa/TestBaseEntity.java    |  7 ++
 .../ContextDataAttributeConverterTest.java      | 70 +++++++++++++++
 .../ContextDataJsonAttributeConverterTest.java  | 84 ++++++++++++++++++
 7 files changed, 327 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/3cb8aa6c/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/AbstractLogEventWrapperEntity.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/AbstractLogEventWrapperEntity.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/AbstractLogEventWrapperEntity.java
index edddfb4..80e1d62 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/AbstractLogEventWrapperEntity.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/AbstractLogEventWrapperEntity.java
@@ -17,7 +17,6 @@
 package org.apache.logging.log4j.core.appender.db.jpa;
 
 import java.util.Map;
-
 import javax.persistence.Inheritance;
 import javax.persistence.InheritanceType;
 import javax.persistence.MappedSuperclass;
@@ -27,7 +26,9 @@ import org.apache.logging.log4j.Level;
 import org.apache.logging.log4j.Marker;
 import org.apache.logging.log4j.ThreadContext;
 import org.apache.logging.log4j.core.AbstractLogEvent;
+import org.apache.logging.log4j.core.ContextData;
 import org.apache.logging.log4j.core.LogEvent;
+import 
org.apache.logging.log4j.core.appender.db.jpa.converter.ContextDataAttributeConverter;
 import org.apache.logging.log4j.message.Message;
 
 /**
@@ -219,10 +220,20 @@ public abstract class AbstractLogEventWrapperEntity 
implements LogEvent {
     /**
      * A no-op mutator to satisfy JPA requirements, as this entity is 
write-only.
      *
-     * @param contextMap Ignored.
+     * @param contextData Ignored.
+     */
+    @SuppressWarnings("unused")
+    public void setContextData(final ContextData contextData) {
+        // this entity is write-only
+    }
+
+    /**
+     * A no-op mutator to satisfy JPA requirements, as this entity is 
write-only.
+     *
+     * @param map Ignored.
      */
     @SuppressWarnings("unused")
-    public void setContextMap(final Map<String, String> contextMap) {
+    public void setContextMap(final Map<String, String> map) {
         // this entity is write-only
     }
 
@@ -281,6 +292,20 @@ public abstract class AbstractLogEventWrapperEntity 
implements LogEvent {
     }
 
     /**
+     * Gets the context map. Transient, since the String version of the data 
is obtained via ContextMap.
+     *
+     * @return the context data.
+     * @see ContextDataAttributeConverter
+     * @see 
org.apache.logging.log4j.core.appender.db.jpa.converter.ContextDataAttributeConverter
+     */
+    @Override
+    @Transient
+    //@Convert(converter = ContextDataAttributeConverter.class)
+    public ContextData getContextData() {
+        return this.getWrappedEvent().getContextData();
+    }
+
+    /**
      * A no-op log event class to prevent {@code NullPointerException}s. O/RMs 
tend to create instances of entities in
      * order to "play around" with them.
      */

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/3cb8aa6c/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/BasicLogEventEntity.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/BasicLogEventEntity.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/BasicLogEventEntity.java
index 8fc49dc..d318353 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/BasicLogEventEntity.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/BasicLogEventEntity.java
@@ -17,7 +17,6 @@
 package org.apache.logging.log4j.core.appender.db.jpa;
 
 import java.util.Map;
-
 import javax.persistence.Basic;
 import javax.persistence.Convert;
 import javax.persistence.MappedSuperclass;

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/3cb8aa6c/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextDataAttributeConverter.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextDataAttributeConverter.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextDataAttributeConverter.java
new file mode 100644
index 0000000..a25fca8
--- /dev/null
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextDataAttributeConverter.java
@@ -0,0 +1,46 @@
+/*
+ * 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 org.apache.logging.log4j.core.ContextData;
+
+/**
+ * A JPA 2.1 attribute converter for {@link ContextData 
ContextData&lt;Object&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 ContextDataAttributeConverter implements 
AttributeConverter<ContextData, String> {
+    @Override
+    public String convertToDatabaseColumn(final ContextData contextData) {
+        if (contextData == null) {
+            return null;
+        }
+
+        return contextData.toString();
+    }
+
+    @Override
+    public ContextData convertToEntityAttribute(final String s) {
+        throw new UnsupportedOperationException("Log events can only be 
persisted, not extracted.");
+    }
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/3cb8aa6c/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextDataJsonAttributeConverter.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextDataJsonAttributeConverter.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextDataJsonAttributeConverter.java
new file mode 100644
index 0000000..93f339d
--- /dev/null
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextDataJsonAttributeConverter.java
@@ -0,0 +1,92 @@
+/*
+ * 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 java.io.IOException;
+import java.util.Iterator;
+import java.util.Map;
+import javax.persistence.AttributeConverter;
+import javax.persistence.Converter;
+import javax.persistence.PersistenceException;
+
+import org.apache.logging.log4j.core.ContextData;
+import org.apache.logging.log4j.core.impl.ArrayContextData;
+import org.apache.logging.log4j.core.util.BiConsumer;
+import org.apache.logging.log4j.util.Strings;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.JsonNodeFactory;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+/**
+ * A JPA 2.1 attribute converter for {@link ContextData 
ContextData&lt;Object&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 Data
+ * Processor.
+ */
+@Converter(autoApply = false)
+public class ContextDataJsonAttributeConverter implements 
AttributeConverter<ContextData, String> {
+    static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
+
+    @Override
+    public String convertToDatabaseColumn(final ContextData contextData) {
+        if (contextData == null) {
+            return null;
+        }
+
+        try {
+            final JsonNodeFactory factory = OBJECT_MAPPER.getNodeFactory();
+            final ObjectNode root = factory.objectNode();
+            contextData.forEach(new BiConsumer<String, Object>() {
+                @Override
+                public void accept(final String key, final Object value) {
+                    // we will cheat here and write the toString of the 
Object... meh, but ok.
+                    root.put(key, String.valueOf(value));
+                }
+            });
+            return OBJECT_MAPPER.writeValueAsString(root);
+        } catch (final Exception e) {
+            throw new PersistenceException("Failed to convert contextData to 
JSON string.", e);
+        }
+    }
+
+    @Override
+    public ContextData convertToEntityAttribute(final String s) {
+        if (Strings.isEmpty(s)) {
+            return null;
+        }
+        try {
+            final ArrayContextData result = new ArrayContextData();
+            final ObjectNode root = (ObjectNode) OBJECT_MAPPER.readTree(s);
+            final Iterator<Map.Entry<String, JsonNode>> entries = 
root.fields();
+            while (entries.hasNext()) {
+                final Map.Entry<String, JsonNode> entry = entries.next();
+
+                // Don't know what to do with non-text values.
+                // Maybe users who need this need to provide custom converter?
+                final Object value = entry.getValue().textValue();
+                result.putValue(entry.getKey(), value);
+            }
+            return result;
+        } catch (final IOException e) {
+            throw new PersistenceException("Failed to convert JSON string to 
map.", e);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/3cb8aa6c/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/TestBaseEntity.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/TestBaseEntity.java
 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/TestBaseEntity.java
index 02ef9ab..f0e94ec 100644
--- 
a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/TestBaseEntity.java
+++ 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/TestBaseEntity.java
@@ -34,6 +34,7 @@ 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.ContextData;
 import org.apache.logging.log4j.core.LogEvent;
 import 
org.apache.logging.log4j.core.appender.db.jpa.converter.LevelAttributeConverter;
 import 
org.apache.logging.log4j.core.appender.db.jpa.converter.MessageAttributeConverter;
@@ -161,6 +162,12 @@ public class TestBaseEntity extends 
AbstractLogEventWrapperEntity {
 
     @Override
     @Transient
+    public ContextData getContextData() {
+        return this.getWrappedEvent().getContextData();
+    }
+
+    @Override
+    @Transient
     public ThreadContext.ContextStack getContextStack() {
         return this.getWrappedEvent().getContextStack();
     }

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/3cb8aa6c/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextDataAttributeConverterTest.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextDataAttributeConverterTest.java
 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextDataAttributeConverterTest.java
new file mode 100644
index 0000000..9f86d16
--- /dev/null
+++ 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextDataAttributeConverterTest.java
@@ -0,0 +1,70 @@
+/*
+ * 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.core.impl.ArrayContextData;
+import org.apache.logging.log4j.core.impl.MutableContextData;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+public class ContextDataAttributeConverterTest {
+    private ContextDataAttributeConverter converter;
+
+    @Before
+    public void setUp() {
+        this.converter = new ContextDataAttributeConverter();
+    }
+
+    @After
+    public void tearDown() {
+
+    }
+
+    @Test
+    public void testConvertToDatabaseColumn01() {
+        final MutableContextData map = new ArrayContextData();
+        map.putValue("test1", "another1");
+        map.putValue("key2", "value2");
+
+        assertEquals("The converted value is not correct.", map.toString(),
+                this.converter.convertToDatabaseColumn(map));
+    }
+
+    @Test
+    public void testConvertToDatabaseColumn02() {
+        final MutableContextData map = new ArrayContextData();
+        map.putValue("someKey", "coolValue");
+        map.putValue("anotherKey", "testValue");
+        map.putValue("myKey", "yourValue");
+
+        assertEquals("The converted value is not correct.", map.toString(),
+                this.converter.convertToDatabaseColumn(map));
+    }
+
+    @Test
+    public void testConvertNullToDatabaseColumn() {
+        assertNull("The converted value should be null.", 
this.converter.convertToDatabaseColumn(null));
+    }
+
+    @Test(expected = UnsupportedOperationException.class)
+    public void testConvertToEntityAttribute() {
+        this.converter.convertToEntityAttribute(null);
+    }
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/3cb8aa6c/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextDataJsonAttributeConverterTest.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextDataJsonAttributeConverterTest.java
 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextDataJsonAttributeConverterTest.java
new file mode 100644
index 0000000..76ce5d2
--- /dev/null
+++ 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextDataJsonAttributeConverterTest.java
@@ -0,0 +1,84 @@
+/*
+ * 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.core.ContextData;
+import org.apache.logging.log4j.core.impl.ArrayContextData;
+import org.apache.logging.log4j.core.impl.MutableContextData;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+public class ContextDataJsonAttributeConverterTest {
+    private ContextDataJsonAttributeConverter converter;
+
+    @Before
+    public void setUp() {
+        this.converter = new ContextDataJsonAttributeConverter();
+    }
+
+    @After
+    public void tearDown() {
+
+    }
+
+    @Test
+    public void testConvert01() {
+        final MutableContextData map = new ArrayContextData();
+        map.putValue("test1", "another1");
+        map.putValue("key2", "value2");
+
+        final String converted = this.converter.convertToDatabaseColumn(map);
+
+        assertNotNull("The converted value should not be null.", converted);
+
+        final ContextData reversed = (ContextData) 
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() {
+        final MutableContextData map = new ArrayContextData();
+        map.putValue("someKey", "coolValue");
+        map.putValue("anotherKey", "testValue");
+        map.putValue("myKey", "yourValue");
+
+        final String converted = this.converter.convertToDatabaseColumn(map);
+
+        assertNotNull("The converted value should not be null.", converted);
+
+        final ContextData reversed = (ContextData) 
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 testConvertNullToDatabaseColumn() {
+        assertNull("The converted value should be null.", 
this.converter.convertToDatabaseColumn(null));
+    }
+
+    @Test
+    public void testConvertNullOrBlankToEntityAttribute() {
+        assertNull("The converted attribute should be null (1).", 
this.converter.convertToEntityAttribute(null));
+        assertNull("The converted attribute should be null (2).", 
this.converter.convertToEntityAttribute(""));
+    }
+}

Reply via email to