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<Object>}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<Object>}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("")); + } +}
