You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by gg...@apache.org on 2019/02/27 14:55:40 UTC

[logging-log4j2] branch release-2.x updated: [LOG4J2-2559] NullPointerException in JdbcAppender.createAppender().

This is an automated email from the ASF dual-hosted git repository.

ggregory pushed a commit to branch release-2.x
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git


The following commit(s) were added to refs/heads/release-2.x by this push:
     new 14baf29  [LOG4J2-2559] NullPointerException in JdbcAppender.createAppender().
14baf29 is described below

commit 14baf294e93148cbbd627b6a8ddf7434f1fce69d
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Wed Feb 27 09:55:38 2019 -0500

    [LOG4J2-2559] NullPointerException in JdbcAppender.createAppender().
---
 .../core/appender/db/jdbc/JdbcDatabaseManager.java | 207 +++++++++++----------
 src/changes/changes.xml                            |   5 +
 2 files changed, 117 insertions(+), 95 deletions(-)

diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jdbc/JdbcDatabaseManager.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jdbc/JdbcDatabaseManager.java
index b449272..f919966 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jdbc/JdbcDatabaseManager.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jdbc/JdbcDatabaseManager.java
@@ -113,34 +113,39 @@ public final class JdbcDatabaseManager extends AbstractDatabaseManager {
             appendColumnNames("INSERT", data, sb);
             sb.append(") values (");
             int i = 1;
-            for (final ColumnMapping mapping : data.columnMappings) {
-                final String mappingName = mapping.getName();
-                if (Strings.isNotEmpty(mapping.getLiteralValue())) {
-                    logger().trace("Adding INSERT VALUES literal for ColumnMapping[{}]: {}={} ", i, mappingName,
-                            mapping.getLiteralValue());
-                    sb.append(mapping.getLiteralValue());
-                } else if (Strings.isNotEmpty(mapping.getParameter())) {
-                    logger().trace("Adding INSERT VALUES parameter for ColumnMapping[{}]: {}={} ", i, mappingName,
-                            mapping.getParameter());
-                    sb.append(mapping.getParameter());
-                } else {
-                    logger().trace("Adding INSERT VALUES parameter marker for ColumnMapping[{}]: {}={} ", i,
-                            mappingName, PARAMETER_MARKER);
-                    sb.append(PARAMETER_MARKER);
-                }
-                sb.append(',');
-                i++;
-            }
-            final List<ColumnConfig> columnConfigs = new ArrayList<>(data.columnConfigs.length);
-            for (final ColumnConfig config : data.columnConfigs) {
-                if (Strings.isNotEmpty(config.getLiteralValue())) {
-                    sb.append(config.getLiteralValue());
-                } else {
-                    sb.append(PARAMETER_MARKER);
-                    columnConfigs.add(config);
-                }
-                sb.append(',');
-            }
+			if (data.columnMappings != null) {
+				for (final ColumnMapping mapping : data.columnMappings) {
+					final String mappingName = mapping.getName();
+					if (Strings.isNotEmpty(mapping.getLiteralValue())) {
+						logger().trace("Adding INSERT VALUES literal for ColumnMapping[{}]: {}={} ", i, mappingName,
+								mapping.getLiteralValue());
+						sb.append(mapping.getLiteralValue());
+					} else if (Strings.isNotEmpty(mapping.getParameter())) {
+						logger().trace("Adding INSERT VALUES parameter for ColumnMapping[{}]: {}={} ", i, mappingName,
+								mapping.getParameter());
+						sb.append(mapping.getParameter());
+					} else {
+						logger().trace("Adding INSERT VALUES parameter marker for ColumnMapping[{}]: {}={} ", i,
+								mappingName, PARAMETER_MARKER);
+						sb.append(PARAMETER_MARKER);
+					}
+					sb.append(',');
+					i++;
+				}
+			}
+			final int columnConfigsLen = data.columnConfigs == null ? 0 : data.columnConfigs.length;
+			final List<ColumnConfig> columnConfigs = new ArrayList<>(columnConfigsLen);
+			if (data.columnConfigs != null) {
+				for (final ColumnConfig config : data.columnConfigs) {
+					if (Strings.isNotEmpty(config.getLiteralValue())) {
+						sb.append(config.getLiteralValue());
+					} else {
+						sb.append(PARAMETER_MARKER);
+						columnConfigs.add(config);
+					}
+					sb.append(',');
+				}
+			}
             // at least one of those arrays is guaranteed to be non-empty
             sb.setCharAt(sb.length() - 1, ')');
             final String sqlStatement = sb.toString();
@@ -334,24 +339,31 @@ public final class JdbcDatabaseManager extends AbstractDatabaseManager {
     /**
      * Appends column names to the given buffer in the format {@code "A,B,C"}.
      */
-    private static void appendColumnNames(final String sqlVerb, final FactoryData data, final StringBuilder sb) {
-        // so this gets a little more complicated now that there are two ways to configure column mappings, but
-        // both mappings follow the same exact pattern for the prepared statement
-        int i = 1;
-        final String messagePattern = "Appending {} {}[{}]: {}={} ";
-        for (final ColumnMapping colMapping : data.columnMappings) {
-            final String columnName = colMapping.getName();
-            appendColumnName(i, columnName, sb);
-            logger().trace(messagePattern, sqlVerb, colMapping.getClass().getSimpleName(), i, columnName, colMapping);
-            i++;
-        }
-        for (final ColumnConfig colConfig : data.columnConfigs) {
-            final String columnName = colConfig.getColumnName();
-            appendColumnName(i, columnName, sb);
-            logger().trace(messagePattern, sqlVerb, colConfig.getClass().getSimpleName(), i, columnName, colConfig);
-            i++;
-        }
-    }
+	private static void appendColumnNames(final String sqlVerb, final FactoryData data, final StringBuilder sb) {
+		// so this gets a little more complicated now that there are two ways to
+		// configure column mappings, but
+		// both mappings follow the same exact pattern for the prepared statement
+		int i = 1;
+		final String messagePattern = "Appending {} {}[{}]: {}={} ";
+		if (data.columnMappings != null) {
+			for (final ColumnMapping colMapping : data.columnMappings) {
+				final String columnName = colMapping.getName();
+				appendColumnName(i, columnName, sb);
+				logger().trace(messagePattern, sqlVerb, colMapping.getClass().getSimpleName(), i, columnName,
+						colMapping);
+				i++;
+			}
+			if (data.columnConfigs != null) {
+				for (final ColumnConfig colConfig : data.columnConfigs) {
+					final String columnName = colConfig.getColumnName();
+					appendColumnName(i, columnName, sb);
+					logger().trace(messagePattern, sqlVerb, colConfig.getClass().getSimpleName(), i, columnName,
+							colConfig);
+					i++;
+				}
+			}
+		}
+	}
 
     private static JdbcDatabaseManagerFactory getFactory() {
         return INSTANCE;
@@ -665,26 +677,28 @@ public final class JdbcDatabaseManager extends AbstractDatabaseManager {
         }
     }
 
-    private void setFields(final MapMessage<?, ?> mapMessage) throws SQLException {
-        final IndexedReadOnlyStringMap map = mapMessage.getIndexedReadOnlyStringMap();
-        final String simpleName = statement.getClass().getName();
-        int j = 1; // JDBC indices start at 1
-        for (final ColumnMapping mapping : this.factoryData.columnMappings) {
-            if (mapping.getLiteralValue() == null) {
-                final String source = mapping.getSource();
-                final String key = Strings.isEmpty(source) ? mapping.getName() : source;
-                final Object value = map.getValue(key);
-                if (logger().isTraceEnabled()) {
-                    final String valueStr = value instanceof String ? "\"" + value + "\""
-                            : Objects.toString(value, null);
-                    logger().trace("{} setObject({}, {}) for key '{}' and mapping '{}'", simpleName, j, valueStr, key,
-                            mapping.getName());
-                }
-                setStatementObject(j, mapping.getNameKey(), value);
-                j++;
-            }
-        }
-    }
+	private void setFields(final MapMessage<?, ?> mapMessage) throws SQLException {
+		final IndexedReadOnlyStringMap map = mapMessage.getIndexedReadOnlyStringMap();
+		final String simpleName = statement.getClass().getName();
+		int j = 1; // JDBC indices start at 1
+		if (this.factoryData.columnMappings != null) {
+			for (final ColumnMapping mapping : this.factoryData.columnMappings) {
+				if (mapping.getLiteralValue() == null) {
+					final String source = mapping.getSource();
+					final String key = Strings.isEmpty(source) ? mapping.getName() : source;
+					final Object value = map.getValue(key);
+					if (logger().isTraceEnabled()) {
+						final String valueStr = value instanceof String ? "\"" + value + "\""
+								: Objects.toString(value, null);
+						logger().trace("{} setObject({}, {}) for key '{}' and mapping '{}'", simpleName, j, valueStr,
+								key, mapping.getName());
+					}
+					setStatementObject(j, mapping.getNameKey(), value);
+					j++;
+				}
+			}
+		}
+	}
 
     /**
      * Sets the given Object in the prepared statement. The value is truncated if needed.
@@ -740,35 +754,38 @@ public final class JdbcDatabaseManager extends AbstractDatabaseManager {
                 setFields((MapMessage<?, ?>) serializable);
             }
             int j = 1; // JDBC indices start at 1
-            for (final ColumnMapping mapping : this.factoryData.columnMappings) {
-                if (ThreadContextMap.class.isAssignableFrom(mapping.getType())
-                        || ReadOnlyStringMap.class.isAssignableFrom(mapping.getType())) {
-                    this.statement.setObject(j++, event.getContextData().toMap());
-                } else if (ThreadContextStack.class.isAssignableFrom(mapping.getType())) {
-                    this.statement.setObject(j++, event.getContextStack().asList());
-                } else if (Date.class.isAssignableFrom(mapping.getType())) {
-                    this.statement.setObject(j++, DateTypeConverter.fromMillis(event.getTimeMillis(),
-                            mapping.getType().asSubclass(Date.class)));
-                } else {
-                    final StringLayout layout = mapping.getLayout();
-                    if (layout != null) {
-                        if (Clob.class.isAssignableFrom(mapping.getType())) {
-                            this.statement.setClob(j++, new StringReader(layout.toSerializable(event)));
-                        } else if (NClob.class.isAssignableFrom(mapping.getType())) {
-                            this.statement.setNClob(j++, new StringReader(layout.toSerializable(event)));
-                        } else {
-                            final Object value = TypeConverters.convert(layout.toSerializable(event), mapping.getType(),
-                                    null);
-                            if (value == null) {
-                                // TODO We might need to always initialize the columnMetaData to specify the type.
-                                this.statement.setNull(j++, Types.NULL);
-                            } else {
-                                setStatementObject(j++, mapping.getNameKey(), value);
-                            }
-                        }
-                    }
-                }
-            }
+			if (this.factoryData.columnMappings != null) {
+				for (final ColumnMapping mapping : this.factoryData.columnMappings) {
+					if (ThreadContextMap.class.isAssignableFrom(mapping.getType())
+							|| ReadOnlyStringMap.class.isAssignableFrom(mapping.getType())) {
+						this.statement.setObject(j++, event.getContextData().toMap());
+					} else if (ThreadContextStack.class.isAssignableFrom(mapping.getType())) {
+						this.statement.setObject(j++, event.getContextStack().asList());
+					} else if (Date.class.isAssignableFrom(mapping.getType())) {
+						this.statement.setObject(j++, DateTypeConverter.fromMillis(event.getTimeMillis(),
+								mapping.getType().asSubclass(Date.class)));
+					} else {
+						final StringLayout layout = mapping.getLayout();
+						if (layout != null) {
+							if (Clob.class.isAssignableFrom(mapping.getType())) {
+								this.statement.setClob(j++, new StringReader(layout.toSerializable(event)));
+							} else if (NClob.class.isAssignableFrom(mapping.getType())) {
+								this.statement.setNClob(j++, new StringReader(layout.toSerializable(event)));
+							} else {
+								final Object value = TypeConverters.convert(layout.toSerializable(event),
+										mapping.getType(), null);
+								if (value == null) {
+									// TODO We might need to always initialize the columnMetaData to specify the
+									// type.
+									this.statement.setNull(j++, Types.NULL);
+								} else {
+									setStatementObject(j++, mapping.getNameKey(), value);
+								}
+							}
+						}
+					}
+				}
+			}
             for (final ColumnConfig column : this.columnConfigs) {
                 if (column.isEventTimestamp()) {
                     this.statement.setTimestamp(j++, new Timestamp(event.getTimeMillis()));
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 01e8787..4580e70 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -30,6 +30,11 @@
          - "update" - Change
          - "remove" - Removed
     -->
+    <release version="2.12.0" date="2019-MM-DD" description="GA Release 2.12.0">
+      <action issue="LOG4J2-2559" dev="ggregory" type="fix" due-to="Li Lei, Gary Gregory">
+        NullPointerException in JdbcAppender.createAppender().
+      </action>
+    </release>
     <release version="2.11.2" date="2019-02-04" description="GA Release 2.11.2">
       <action issue="LOG4J2-2500" dev="rgoers" type="fix">
         Document that Properties element must be the first configuration element.