http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/78fe32c0/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jdbc/JdbcDatabaseManager.java ---------------------------------------------------------------------- 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 76e1901..6f9c66a 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 @@ -1,255 +1,260 @@ -/* - * 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.jdbc; - -import java.io.StringReader; -import java.sql.Connection; -import java.sql.DatabaseMetaData; -import java.sql.PreparedStatement; -import java.sql.SQLException; -import java.sql.Timestamp; -import java.util.ArrayList; -import java.util.List; - -import org.apache.logging.log4j.core.LogEvent; -import org.apache.logging.log4j.core.appender.AppenderLoggingException; -import org.apache.logging.log4j.core.appender.ManagerFactory; -import org.apache.logging.log4j.core.appender.db.AbstractDatabaseManager; -import org.apache.logging.log4j.core.layout.PatternLayout; -import org.apache.logging.log4j.core.util.Closer; - -/** - * An {@link AbstractDatabaseManager} implementation for relational databases accessed via JDBC. - */ -public final class JdbcDatabaseManager extends AbstractDatabaseManager { - - private static final JdbcDatabaseManagerFactory INSTANCE = new JdbcDatabaseManagerFactory(); - - private final List<Column> columns; - private final ConnectionSource connectionSource; - private final String sqlStatement; - - private Connection connection; - private PreparedStatement statement; - private boolean isBatchSupported; - - private JdbcDatabaseManager(final String name, final int bufferSize, final ConnectionSource connectionSource, - final String sqlStatement, final List<Column> columns) { - super(name, bufferSize); - this.connectionSource = connectionSource; - this.sqlStatement = sqlStatement; - this.columns = columns; - } - - @Override - protected void startupInternal() throws Exception { - this.connection = this.connectionSource.getConnection(); - final DatabaseMetaData metaData = this.connection.getMetaData(); - this.isBatchSupported = metaData.supportsBatchUpdates(); - Closer.closeSilently(this.connection); - } - - @Override - protected void shutdownInternal() { - if (this.connection != null || this.statement != null) { - this.commitAndClose(); - } - } - - @Override - protected void connectAndStart() { - try { - this.connection = this.connectionSource.getConnection(); - this.connection.setAutoCommit(false); - this.statement = this.connection.prepareStatement(this.sqlStatement); - } catch (final SQLException e) { - throw new AppenderLoggingException( - "Cannot write logging event or flush buffer; JDBC manager cannot connect to the database.", e - ); - } - } - - @Override - protected void writeInternal(final LogEvent event) { - StringReader reader = null; - try { - if (!this.isRunning() || this.connection == null || this.connection.isClosed() || this.statement == null - || this.statement.isClosed()) { - throw new AppenderLoggingException( - "Cannot write logging event; JDBC manager not connected to the database."); - } - - int i = 1; - for (final Column column : this.columns) { - if (column.isEventTimestamp) { - this.statement.setTimestamp(i++, new Timestamp(event.getTimeMillis())); - } else { - if (column.isClob) { - reader = new StringReader(column.layout.toSerializable(event)); - if (column.isUnicode) { - this.statement.setNClob(i++, reader); - } else { - this.statement.setClob(i++, reader); - } - } else { - if (column.isUnicode) { - this.statement.setNString(i++, column.layout.toSerializable(event)); - } else { - this.statement.setString(i++, column.layout.toSerializable(event)); - } - } - } - } - - if (this.isBatchSupported) { - this.statement.addBatch(); - } else if (this.statement.executeUpdate() == 0) { - throw new AppenderLoggingException( - "No records inserted in database table for log event in JDBC manager."); - } - } catch (final SQLException e) { - throw new AppenderLoggingException("Failed to insert record for log event in JDBC manager: " + - e.getMessage(), e); - } finally { - Closer.closeSilently(reader); - } - } - - @Override - protected void commitAndClose() { - try { - if (this.connection != null && !this.connection.isClosed()) { - if (this.isBatchSupported) { - this.statement.executeBatch(); - } - this.connection.commit(); - } - } catch (final SQLException e) { - throw new AppenderLoggingException("Failed to commit transaction logging event or flushing buffer.", e); - } finally { - try { - Closer.close(this.statement); - } catch (final Exception e) { - logWarn("Failed to close SQL statement logging event or flushing buffer", e); - } finally { - this.statement = null; - } - - try { - Closer.close(this.connection); - } catch (final Exception e) { - logWarn("Failed to close database connection logging event or flushing buffer", e); - } finally { - this.connection = null; - } - } - } - - /** - * Creates a JDBC manager for use within the {@link JdbcAppender}, or returns a suitable one if it already exists. - * - * @param name The name of the manager, which should include connection details and hashed passwords where possible. - * @param bufferSize The size of the log event buffer. - * @param connectionSource The source for connections to the database. - * @param tableName The name of the database table to insert log events into. - * @param columnConfigs Configuration information about the log table columns. - * @return a new or existing JDBC manager as applicable. - */ - public static JdbcDatabaseManager getJDBCDatabaseManager(final String name, final int bufferSize, - final ConnectionSource connectionSource, - final String tableName, - final ColumnConfig[] columnConfigs) { - - return AbstractDatabaseManager.getManager( - name, new FactoryData(bufferSize, connectionSource, tableName, columnConfigs), getFactory() - ); - } - - private static JdbcDatabaseManagerFactory getFactory() { - return INSTANCE; - } - - /** - * Encapsulates data that {@link JdbcDatabaseManagerFactory} uses to create managers. - */ - private static final class FactoryData extends AbstractDatabaseManager.AbstractFactoryData { - private final ColumnConfig[] columnConfigs; - private final ConnectionSource connectionSource; - private final String tableName; - - protected FactoryData(final int bufferSize, final ConnectionSource connectionSource, final String tableName, - final ColumnConfig[] columnConfigs) { - super(bufferSize); - this.connectionSource = connectionSource; - this.tableName = tableName; - this.columnConfigs = columnConfigs; - } - } - - /** - * Creates managers. - */ - private static final class JdbcDatabaseManagerFactory implements ManagerFactory<JdbcDatabaseManager, FactoryData> { - @Override - public JdbcDatabaseManager createManager(final String name, final FactoryData data) { - final StringBuilder columnPart = new StringBuilder(); - final StringBuilder valuePart = new StringBuilder(); - final List<Column> columns = new ArrayList<>(); - int i = 0; - for (final ColumnConfig config : data.columnConfigs) { - if (i++ > 0) { - columnPart.append(','); - valuePart.append(','); - } - - columnPart.append(config.getColumnName()); - - if (config.getLiteralValue() != null) { - valuePart.append(config.getLiteralValue()); - } else { - columns.add(new Column( - config.getLayout(), config.isEventTimestamp(), config.isUnicode(), config.isClob() - )); - valuePart.append('?'); - } - } - - final String sqlStatement = "INSERT INTO " + data.tableName + " (" + columnPart + ") VALUES (" + - valuePart + ')'; - - return new JdbcDatabaseManager(name, data.getBufferSize(), data.connectionSource, sqlStatement, columns); - } - } - - /** - * Encapsulates information about a database column and how to persist data to it. - */ - private static final class Column { - private final PatternLayout layout; - private final boolean isEventTimestamp; - private final boolean isUnicode; - private final boolean isClob; - - private Column(final PatternLayout layout, final boolean isEventDate, final boolean isUnicode, - final boolean isClob) { - this.layout = layout; - this.isEventTimestamp = isEventDate; - this.isUnicode = isUnicode; - this.isClob = isClob; - } - } -} +/* + * 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.jdbc; + +import java.io.StringReader; +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.List; + +import org.apache.logging.log4j.core.LogEvent; +import org.apache.logging.log4j.core.appender.AppenderLoggingException; +import org.apache.logging.log4j.core.appender.ManagerFactory; +import org.apache.logging.log4j.core.appender.db.AbstractDatabaseManager; +import org.apache.logging.log4j.core.layout.PatternLayout; +import org.apache.logging.log4j.core.util.Closer; + +/** + * An {@link AbstractDatabaseManager} implementation for relational databases accessed via JDBC. + */ +public final class JdbcDatabaseManager extends AbstractDatabaseManager { + + private static final JdbcDatabaseManagerFactory INSTANCE = new JdbcDatabaseManagerFactory(); + + private final List<Column> columns; + private final ConnectionSource connectionSource; + private final String sqlStatement; + + private Connection connection; + private PreparedStatement statement; + private boolean isBatchSupported; + + private JdbcDatabaseManager(final String name, final int bufferSize, final ConnectionSource connectionSource, + final String sqlStatement, final List<Column> columns) { + super(name, bufferSize); + this.connectionSource = connectionSource; + this.sqlStatement = sqlStatement; + this.columns = columns; + } + + @Override + protected void startupInternal() throws Exception { + this.connection = this.connectionSource.getConnection(); + final DatabaseMetaData metaData = this.connection.getMetaData(); + this.isBatchSupported = metaData.supportsBatchUpdates(); + Closer.closeSilently(this.connection); + } + + @Override + protected boolean shutdownInternal() { + if (this.connection != null || this.statement != null) { + return this.commitAndClose(); + } + return true; + } + + @Override + protected void connectAndStart() { + try { + this.connection = this.connectionSource.getConnection(); + this.connection.setAutoCommit(false); + this.statement = this.connection.prepareStatement(this.sqlStatement); + } catch (final SQLException e) { + throw new AppenderLoggingException( + "Cannot write logging event or flush buffer; JDBC manager cannot connect to the database.", e + ); + } + } + + @Override + protected void writeInternal(final LogEvent event) { + StringReader reader = null; + try { + if (!this.isRunning() || this.connection == null || this.connection.isClosed() || this.statement == null + || this.statement.isClosed()) { + throw new AppenderLoggingException( + "Cannot write logging event; JDBC manager not connected to the database."); + } + + int i = 1; + for (final Column column : this.columns) { + if (column.isEventTimestamp) { + this.statement.setTimestamp(i++, new Timestamp(event.getTimeMillis())); + } else { + if (column.isClob) { + reader = new StringReader(column.layout.toSerializable(event)); + if (column.isUnicode) { + this.statement.setNClob(i++, reader); + } else { + this.statement.setClob(i++, reader); + } + } else { + if (column.isUnicode) { + this.statement.setNString(i++, column.layout.toSerializable(event)); + } else { + this.statement.setString(i++, column.layout.toSerializable(event)); + } + } + } + } + + if (this.isBatchSupported) { + this.statement.addBatch(); + } else if (this.statement.executeUpdate() == 0) { + throw new AppenderLoggingException( + "No records inserted in database table for log event in JDBC manager."); + } + } catch (final SQLException e) { + throw new AppenderLoggingException("Failed to insert record for log event in JDBC manager: " + + e.getMessage(), e); + } finally { + Closer.closeSilently(reader); + } + } + + @Override + protected boolean commitAndClose() { + boolean closed = true; + try { + if (this.connection != null && !this.connection.isClosed()) { + if (this.isBatchSupported) { + this.statement.executeBatch(); + } + this.connection.commit(); + } + } catch (final SQLException e) { + throw new AppenderLoggingException("Failed to commit transaction logging event or flushing buffer.", e); + } finally { + try { + Closer.close(this.statement); + } catch (final Exception e) { + logWarn("Failed to close SQL statement logging event or flushing buffer", e); + closed = false; + } finally { + this.statement = null; + } + + try { + Closer.close(this.connection); + } catch (final Exception e) { + logWarn("Failed to close database connection logging event or flushing buffer", e); + closed = false; + } finally { + this.connection = null; + } + } + return closed; + } + + /** + * Creates a JDBC manager for use within the {@link JdbcAppender}, or returns a suitable one if it already exists. + * + * @param name The name of the manager, which should include connection details and hashed passwords where possible. + * @param bufferSize The size of the log event buffer. + * @param connectionSource The source for connections to the database. + * @param tableName The name of the database table to insert log events into. + * @param columnConfigs Configuration information about the log table columns. + * @return a new or existing JDBC manager as applicable. + */ + public static JdbcDatabaseManager getJDBCDatabaseManager(final String name, final int bufferSize, + final ConnectionSource connectionSource, + final String tableName, + final ColumnConfig[] columnConfigs) { + + return AbstractDatabaseManager.getManager( + name, new FactoryData(bufferSize, connectionSource, tableName, columnConfigs), getFactory() + ); + } + + private static JdbcDatabaseManagerFactory getFactory() { + return INSTANCE; + } + + /** + * Encapsulates data that {@link JdbcDatabaseManagerFactory} uses to create managers. + */ + private static final class FactoryData extends AbstractDatabaseManager.AbstractFactoryData { + private final ColumnConfig[] columnConfigs; + private final ConnectionSource connectionSource; + private final String tableName; + + protected FactoryData(final int bufferSize, final ConnectionSource connectionSource, final String tableName, + final ColumnConfig[] columnConfigs) { + super(bufferSize); + this.connectionSource = connectionSource; + this.tableName = tableName; + this.columnConfigs = columnConfigs; + } + } + + /** + * Creates managers. + */ + private static final class JdbcDatabaseManagerFactory implements ManagerFactory<JdbcDatabaseManager, FactoryData> { + @Override + public JdbcDatabaseManager createManager(final String name, final FactoryData data) { + final StringBuilder columnPart = new StringBuilder(); + final StringBuilder valuePart = new StringBuilder(); + final List<Column> columns = new ArrayList<>(); + int i = 0; + for (final ColumnConfig config : data.columnConfigs) { + if (i++ > 0) { + columnPart.append(','); + valuePart.append(','); + } + + columnPart.append(config.getColumnName()); + + if (config.getLiteralValue() != null) { + valuePart.append(config.getLiteralValue()); + } else { + columns.add(new Column( + config.getLayout(), config.isEventTimestamp(), config.isUnicode(), config.isClob() + )); + valuePart.append('?'); + } + } + + final String sqlStatement = "INSERT INTO " + data.tableName + " (" + columnPart + ") VALUES (" + + valuePart + ')'; + + return new JdbcDatabaseManager(name, data.getBufferSize(), data.connectionSource, sqlStatement, columns); + } + } + + /** + * Encapsulates information about a database column and how to persist data to it. + */ + private static final class Column { + private final PatternLayout layout; + private final boolean isEventTimestamp; + private final boolean isUnicode; + private final boolean isClob; + + private Column(final PatternLayout layout, final boolean isEventDate, final boolean isUnicode, + final boolean isClob) { + this.layout = layout; + this.isEventTimestamp = isEventDate; + this.isUnicode = isUnicode; + this.isClob = isClob; + } + } +}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/78fe32c0/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/JpaDatabaseManager.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/JpaDatabaseManager.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/JpaDatabaseManager.java index 17c5642..d5fc922 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/JpaDatabaseManager.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/JpaDatabaseManager.java @@ -1,188 +1,193 @@ -/* - * 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; - -import java.lang.reflect.Constructor; - -import javax.persistence.EntityManager; -import javax.persistence.EntityManagerFactory; -import javax.persistence.EntityTransaction; -import javax.persistence.Persistence; - -import org.apache.logging.log4j.core.LogEvent; -import org.apache.logging.log4j.core.appender.AppenderLoggingException; -import org.apache.logging.log4j.core.appender.ManagerFactory; -import org.apache.logging.log4j.core.appender.db.AbstractDatabaseManager; - -/** - * An {@link AbstractDatabaseManager} implementation for relational databases accessed via JPA. - */ -public final class JpaDatabaseManager extends AbstractDatabaseManager { - private static final JPADatabaseManagerFactory FACTORY = new JPADatabaseManagerFactory(); - - private final String entityClassName; - private final Constructor<? extends AbstractLogEventWrapperEntity> entityConstructor; - private final String persistenceUnitName; - - private EntityManagerFactory entityManagerFactory; - - private EntityManager entityManager; - private EntityTransaction transaction; - - private JpaDatabaseManager(final String name, final int bufferSize, - final Class<? extends AbstractLogEventWrapperEntity> entityClass, - final Constructor<? extends AbstractLogEventWrapperEntity> entityConstructor, - final String persistenceUnitName) { - super(name, bufferSize); - this.entityClassName = entityClass.getName(); - this.entityConstructor = entityConstructor; - this.persistenceUnitName = persistenceUnitName; - } - - @Override - protected void startupInternal() { - this.entityManagerFactory = Persistence.createEntityManagerFactory(this.persistenceUnitName); - } - - @Override - protected void shutdownInternal() { - if (this.entityManager != null || this.transaction != null) { - this.commitAndClose(); - } - if (this.entityManagerFactory != null && this.entityManagerFactory.isOpen()) { - this.entityManagerFactory.close(); - } - } - - @Override - protected void connectAndStart() { - try { - this.entityManager = this.entityManagerFactory.createEntityManager(); - this.transaction = this.entityManager.getTransaction(); - this.transaction.begin(); - } catch (final Exception e) { - throw new AppenderLoggingException( - "Cannot write logging event or flush buffer; manager cannot create EntityManager or transaction.", e - ); - } - } - - @Override - protected void writeInternal(final LogEvent event) { - if (!this.isRunning() || this.entityManagerFactory == null || this.entityManager == null - || this.transaction == null) { - throw new AppenderLoggingException( - "Cannot write logging event; JPA manager not connected to the database."); - } - - AbstractLogEventWrapperEntity entity; - try { - entity = this.entityConstructor.newInstance(event); - } catch (final Exception e) { - throw new AppenderLoggingException("Failed to instantiate entity class [" + this.entityClassName + "].", e); - } - - try { - this.entityManager.persist(entity); - } catch (final Exception e) { - if (this.transaction != null && this.transaction.isActive()) { - this.transaction.rollback(); - this.transaction = null; - } - throw new AppenderLoggingException("Failed to insert record for log event in JPA manager: " + - e.getMessage(), e); - } - } - - @Override - protected void commitAndClose() { - try { - if (this.transaction != null && this.transaction.isActive()) { - this.transaction.commit(); - } - } catch (final Exception e) { - if (this.transaction != null && this.transaction.isActive()) { - this.transaction.rollback(); - } - } finally { - this.transaction = null; - try { - if (this.entityManager != null && this.entityManager.isOpen()) { - this.entityManager.close(); - } - } catch (final Exception e) { - logWarn("Failed to close entity manager while logging event or flushing buffer", e); - } finally { - this.entityManager = null; - } - } - } - - /** - * Creates a JPA manager for use within the {@link JpaAppender}, or returns a suitable one if it already exists. - * - * @param name The name of the manager, which should include connection details, entity class name, etc. - * @param bufferSize The size of the log event buffer. - * @param entityClass The fully-qualified class name of the {@link AbstractLogEventWrapperEntity} concrete - * implementation. - * @param entityConstructor The one-arg {@link LogEvent} constructor for the concrete entity class. - * @param persistenceUnitName The name of the JPA persistence unit that should be used for persisting log events. - * @return a new or existing JPA manager as applicable. - */ - public static JpaDatabaseManager getJPADatabaseManager(final String name, final int bufferSize, - final Class<? extends AbstractLogEventWrapperEntity> - entityClass, - final Constructor<? extends AbstractLogEventWrapperEntity> - entityConstructor, - final String persistenceUnitName) { - - return AbstractDatabaseManager.getManager( - name, new FactoryData(bufferSize, entityClass, entityConstructor, persistenceUnitName), FACTORY - ); - } - - /** - * Encapsulates data that {@link JPADatabaseManagerFactory} uses to create managers. - */ - private static final class FactoryData extends AbstractDatabaseManager.AbstractFactoryData { - private final Class<? extends AbstractLogEventWrapperEntity> entityClass; - private final Constructor<? extends AbstractLogEventWrapperEntity> entityConstructor; - private final String persistenceUnitName; - - protected FactoryData(final int bufferSize, final Class<? extends AbstractLogEventWrapperEntity> entityClass, - final Constructor<? extends AbstractLogEventWrapperEntity> entityConstructor, - final String persistenceUnitName) { - super(bufferSize); - - this.entityClass = entityClass; - this.entityConstructor = entityConstructor; - this.persistenceUnitName = persistenceUnitName; - } - } - - /** - * Creates managers. - */ - private static final class JPADatabaseManagerFactory implements ManagerFactory<JpaDatabaseManager, FactoryData> { - @Override - public JpaDatabaseManager createManager(final String name, final FactoryData data) { - return new JpaDatabaseManager( - name, data.getBufferSize(), data.entityClass, data.entityConstructor, data.persistenceUnitName - ); - } - } -} +/* + * 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; + +import java.lang.reflect.Constructor; + +import javax.persistence.EntityManager; +import javax.persistence.EntityManagerFactory; +import javax.persistence.EntityTransaction; +import javax.persistence.Persistence; + +import org.apache.logging.log4j.core.LogEvent; +import org.apache.logging.log4j.core.appender.AppenderLoggingException; +import org.apache.logging.log4j.core.appender.ManagerFactory; +import org.apache.logging.log4j.core.appender.db.AbstractDatabaseManager; + +/** + * An {@link AbstractDatabaseManager} implementation for relational databases accessed via JPA. + */ +public final class JpaDatabaseManager extends AbstractDatabaseManager { + private static final JPADatabaseManagerFactory FACTORY = new JPADatabaseManagerFactory(); + + private final String entityClassName; + private final Constructor<? extends AbstractLogEventWrapperEntity> entityConstructor; + private final String persistenceUnitName; + + private EntityManagerFactory entityManagerFactory; + + private EntityManager entityManager; + private EntityTransaction transaction; + + private JpaDatabaseManager(final String name, final int bufferSize, + final Class<? extends AbstractLogEventWrapperEntity> entityClass, + final Constructor<? extends AbstractLogEventWrapperEntity> entityConstructor, + final String persistenceUnitName) { + super(name, bufferSize); + this.entityClassName = entityClass.getName(); + this.entityConstructor = entityConstructor; + this.persistenceUnitName = persistenceUnitName; + } + + @Override + protected void startupInternal() { + this.entityManagerFactory = Persistence.createEntityManagerFactory(this.persistenceUnitName); + } + + @Override + protected boolean shutdownInternal() { + boolean closed = true; + if (this.entityManager != null || this.transaction != null) { + closed &= this.commitAndClose(); + } + if (this.entityManagerFactory != null && this.entityManagerFactory.isOpen()) { + this.entityManagerFactory.close(); + } + return closed; + } + + @Override + protected void connectAndStart() { + try { + this.entityManager = this.entityManagerFactory.createEntityManager(); + this.transaction = this.entityManager.getTransaction(); + this.transaction.begin(); + } catch (final Exception e) { + throw new AppenderLoggingException( + "Cannot write logging event or flush buffer; manager cannot create EntityManager or transaction.", e + ); + } + } + + @Override + protected void writeInternal(final LogEvent event) { + if (!this.isRunning() || this.entityManagerFactory == null || this.entityManager == null + || this.transaction == null) { + throw new AppenderLoggingException( + "Cannot write logging event; JPA manager not connected to the database."); + } + + AbstractLogEventWrapperEntity entity; + try { + entity = this.entityConstructor.newInstance(event); + } catch (final Exception e) { + throw new AppenderLoggingException("Failed to instantiate entity class [" + this.entityClassName + "].", e); + } + + try { + this.entityManager.persist(entity); + } catch (final Exception e) { + if (this.transaction != null && this.transaction.isActive()) { + this.transaction.rollback(); + this.transaction = null; + } + throw new AppenderLoggingException("Failed to insert record for log event in JPA manager: " + + e.getMessage(), e); + } + } + + @Override + protected boolean commitAndClose() { + boolean closed = true; + try { + if (this.transaction != null && this.transaction.isActive()) { + this.transaction.commit(); + } + } catch (final Exception e) { + if (this.transaction != null && this.transaction.isActive()) { + this.transaction.rollback(); + } + } finally { + this.transaction = null; + try { + if (this.entityManager != null && this.entityManager.isOpen()) { + this.entityManager.close(); + } + } catch (final Exception e) { + logWarn("Failed to close entity manager while logging event or flushing buffer", e); + closed = false; + } finally { + this.entityManager = null; + } + } + return closed; + } + + /** + * Creates a JPA manager for use within the {@link JpaAppender}, or returns a suitable one if it already exists. + * + * @param name The name of the manager, which should include connection details, entity class name, etc. + * @param bufferSize The size of the log event buffer. + * @param entityClass The fully-qualified class name of the {@link AbstractLogEventWrapperEntity} concrete + * implementation. + * @param entityConstructor The one-arg {@link LogEvent} constructor for the concrete entity class. + * @param persistenceUnitName The name of the JPA persistence unit that should be used for persisting log events. + * @return a new or existing JPA manager as applicable. + */ + public static JpaDatabaseManager getJPADatabaseManager(final String name, final int bufferSize, + final Class<? extends AbstractLogEventWrapperEntity> + entityClass, + final Constructor<? extends AbstractLogEventWrapperEntity> + entityConstructor, + final String persistenceUnitName) { + + return AbstractDatabaseManager.getManager( + name, new FactoryData(bufferSize, entityClass, entityConstructor, persistenceUnitName), FACTORY + ); + } + + /** + * Encapsulates data that {@link JPADatabaseManagerFactory} uses to create managers. + */ + private static final class FactoryData extends AbstractDatabaseManager.AbstractFactoryData { + private final Class<? extends AbstractLogEventWrapperEntity> entityClass; + private final Constructor<? extends AbstractLogEventWrapperEntity> entityConstructor; + private final String persistenceUnitName; + + protected FactoryData(final int bufferSize, final Class<? extends AbstractLogEventWrapperEntity> entityClass, + final Constructor<? extends AbstractLogEventWrapperEntity> entityConstructor, + final String persistenceUnitName) { + super(bufferSize); + + this.entityClass = entityClass; + this.entityConstructor = entityConstructor; + this.persistenceUnitName = persistenceUnitName; + } + } + + /** + * Creates managers. + */ + private static final class JPADatabaseManagerFactory implements ManagerFactory<JpaDatabaseManager, FactoryData> { + @Override + public JpaDatabaseManager createManager(final String name, final FactoryData data) { + return new JpaDatabaseManager( + name, data.getBufferSize(), data.entityClass, data.entityConstructor, data.persistenceUnitName + ); + } + } +} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/78fe32c0/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/mom/JmsAppender.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/mom/JmsAppender.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/mom/JmsAppender.java index 8e21de8..7794b0d 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/mom/JmsAppender.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/mom/JmsAppender.java @@ -1,215 +1,215 @@ -/* - * 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.mom; - -import java.io.Serializable; -import java.util.concurrent.TimeUnit; - -import javax.jms.JMSException; -import javax.jms.Message; -import javax.jms.MessageProducer; - -import org.apache.logging.log4j.core.Appender; -import org.apache.logging.log4j.core.Filter; -import org.apache.logging.log4j.core.Layout; -import org.apache.logging.log4j.core.LogEvent; -import org.apache.logging.log4j.core.appender.AbstractAppender; -import org.apache.logging.log4j.core.appender.AppenderLoggingException; -import org.apache.logging.log4j.core.config.Node; -import org.apache.logging.log4j.core.config.plugins.Plugin; -import org.apache.logging.log4j.core.config.plugins.PluginAliases; -import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute; -import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory; -import org.apache.logging.log4j.core.config.plugins.PluginElement; -import org.apache.logging.log4j.core.config.plugins.validation.constraints.Required; -import org.apache.logging.log4j.core.layout.SerializedLayout; -import org.apache.logging.log4j.core.net.JndiManager; - -/** - * Generic JMS Appender plugin for both queues and topics. This Appender replaces the previous split ones. However, - * configurations set up for the 2.0 version of the JMS appenders will still work. - */ -@Plugin(name = "JMS", category = Node.CATEGORY, elementType = Appender.ELEMENT_TYPE, printObject = true) -@PluginAliases({"JMSQueue", "JMSTopic"}) -public class JmsAppender extends AbstractAppender { - - private final JmsManager manager; - private final MessageProducer producer; - - protected JmsAppender(final String name, final Filter filter, final Layout<? extends Serializable> layout, - final boolean ignoreExceptions, final JmsManager manager) - throws JMSException { - super(name, filter, layout, ignoreExceptions); - this.manager = manager; - this.producer = this.manager.createMessageProducer(); - } - - @Override - public void append(final LogEvent event) { - try { - final Message message = this.manager.createMessage(getLayout().toSerializable(event)); - message.setJMSTimestamp(event.getTimeMillis()); - this.producer.send(message); - } catch (final JMSException e) { - throw new AppenderLoggingException(e); - } - } - - @Override - public boolean stop(final long timeout, final TimeUnit timeUnit) { - setStopping(); - super.stop(timeout, timeUnit, false); - this.manager.stop(timeout, timeUnit); - setStopped(); - return true; - } - - @PluginBuilderFactory - public static Builder newBuilder() { - return new Builder(); - } - - public static class Builder implements org.apache.logging.log4j.core.util.Builder<JmsAppender> { - - @PluginBuilderAttribute - @Required(message = "A name for the JmsAppender must be specified") - private String name; - - @PluginBuilderAttribute - private String factoryName; - - @PluginBuilderAttribute - private String providerUrl; - - @PluginBuilderAttribute - private String urlPkgPrefixes; - - @PluginBuilderAttribute - private String securityPrincipalName; - - @PluginBuilderAttribute(sensitive = true) - private String securityCredentials; - - @PluginBuilderAttribute - @Required(message = "A javax.jms.ConnectionFactory JNDI name must be specified") - private String factoryBindingName; - - @PluginBuilderAttribute - @PluginAliases({"queueBindingName", "topicBindingName"}) - @Required(message = "A javax.jms.Destination JNDI name must be specified") - private String destinationBindingName; - - @PluginBuilderAttribute - private String username; - - @PluginBuilderAttribute(sensitive = true) - private String password; - - @PluginElement("Layout") - private Layout<? extends Serializable> layout = SerializedLayout.createLayout(); - - @PluginElement("Filter") - private Filter filter; - - @PluginBuilderAttribute - private boolean ignoreExceptions = true; - - private Builder() { - } - - public Builder setName(final String name) { - this.name = name; - return this; - } - - public Builder setFactoryName(final String factoryName) { - this.factoryName = factoryName; - return this; - } - - public Builder setProviderUrl(final String providerUrl) { - this.providerUrl = providerUrl; - return this; - } - - public Builder setUrlPkgPrefixes(final String urlPkgPrefixes) { - this.urlPkgPrefixes = urlPkgPrefixes; - return this; - } - - public Builder setSecurityPrincipalName(final String securityPrincipalName) { - this.securityPrincipalName = securityPrincipalName; - return this; - } - - public Builder setSecurityCredentials(final String securityCredentials) { - this.securityCredentials = securityCredentials; - return this; - } - - public Builder setFactoryBindingName(final String factoryBindingName) { - this.factoryBindingName = factoryBindingName; - return this; - } - - public Builder setDestinationBindingName(final String destinationBindingName) { - this.destinationBindingName = destinationBindingName; - return this; - } - - public Builder setUsername(final String username) { - this.username = username; - return this; - } - - public Builder setPassword(final String password) { - this.password = password; - return this; - } - - public Builder setLayout(final Layout<? extends Serializable> layout) { - this.layout = layout; - return this; - } - - public Builder setFilter(final Filter filter) { - this.filter = filter; - return this; - } - - public Builder setIgnoreExceptions(final boolean ignoreExceptions) { - this.ignoreExceptions = ignoreExceptions; - return this; - } - - @Override - public JmsAppender build() { - final JndiManager jndiManager = JndiManager.getJndiManager(factoryName, providerUrl, urlPkgPrefixes, - securityPrincipalName, securityCredentials, null); - final JmsManager jmsManager = JmsManager.getJmsManager(name, jndiManager, factoryBindingName, - destinationBindingName, username, password); - try { - return new JmsAppender(name, filter, layout, ignoreExceptions, jmsManager); - } catch (final JMSException e) { - LOGGER.error("Error creating JmsAppender [{}].", name, e); - return null; - } - } - } - -} +/* + * 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.mom; + +import java.io.Serializable; +import java.util.concurrent.TimeUnit; + +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageProducer; + +import org.apache.logging.log4j.core.Appender; +import org.apache.logging.log4j.core.Filter; +import org.apache.logging.log4j.core.Layout; +import org.apache.logging.log4j.core.LogEvent; +import org.apache.logging.log4j.core.appender.AbstractAppender; +import org.apache.logging.log4j.core.appender.AppenderLoggingException; +import org.apache.logging.log4j.core.config.Node; +import org.apache.logging.log4j.core.config.plugins.Plugin; +import org.apache.logging.log4j.core.config.plugins.PluginAliases; +import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute; +import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory; +import org.apache.logging.log4j.core.config.plugins.PluginElement; +import org.apache.logging.log4j.core.config.plugins.validation.constraints.Required; +import org.apache.logging.log4j.core.layout.SerializedLayout; +import org.apache.logging.log4j.core.net.JndiManager; + +/** + * Generic JMS Appender plugin for both queues and topics. This Appender replaces the previous split ones. However, + * configurations set up for the 2.0 version of the JMS appenders will still work. + */ +@Plugin(name = "JMS", category = Node.CATEGORY, elementType = Appender.ELEMENT_TYPE, printObject = true) +@PluginAliases({"JMSQueue", "JMSTopic"}) +public class JmsAppender extends AbstractAppender { + + private final JmsManager manager; + private final MessageProducer producer; + + protected JmsAppender(final String name, final Filter filter, final Layout<? extends Serializable> layout, + final boolean ignoreExceptions, final JmsManager manager) + throws JMSException { + super(name, filter, layout, ignoreExceptions); + this.manager = manager; + this.producer = this.manager.createMessageProducer(); + } + + @Override + public void append(final LogEvent event) { + try { + final Message message = this.manager.createMessage(getLayout().toSerializable(event)); + message.setJMSTimestamp(event.getTimeMillis()); + this.producer.send(message); + } catch (final JMSException e) { + throw new AppenderLoggingException(e); + } + } + + @Override + public boolean stop(final long timeout, final TimeUnit timeUnit) { + setStopping(); + boolean stopped = super.stop(timeout, timeUnit, false); + stopped &= this.manager.stop(timeout, timeUnit); + setStopped(); + return stopped; + } + + @PluginBuilderFactory + public static Builder newBuilder() { + return new Builder(); + } + + public static class Builder implements org.apache.logging.log4j.core.util.Builder<JmsAppender> { + + @PluginBuilderAttribute + @Required(message = "A name for the JmsAppender must be specified") + private String name; + + @PluginBuilderAttribute + private String factoryName; + + @PluginBuilderAttribute + private String providerUrl; + + @PluginBuilderAttribute + private String urlPkgPrefixes; + + @PluginBuilderAttribute + private String securityPrincipalName; + + @PluginBuilderAttribute(sensitive = true) + private String securityCredentials; + + @PluginBuilderAttribute + @Required(message = "A javax.jms.ConnectionFactory JNDI name must be specified") + private String factoryBindingName; + + @PluginBuilderAttribute + @PluginAliases({"queueBindingName", "topicBindingName"}) + @Required(message = "A javax.jms.Destination JNDI name must be specified") + private String destinationBindingName; + + @PluginBuilderAttribute + private String username; + + @PluginBuilderAttribute(sensitive = true) + private String password; + + @PluginElement("Layout") + private Layout<? extends Serializable> layout = SerializedLayout.createLayout(); + + @PluginElement("Filter") + private Filter filter; + + @PluginBuilderAttribute + private boolean ignoreExceptions = true; + + private Builder() { + } + + public Builder setName(final String name) { + this.name = name; + return this; + } + + public Builder setFactoryName(final String factoryName) { + this.factoryName = factoryName; + return this; + } + + public Builder setProviderUrl(final String providerUrl) { + this.providerUrl = providerUrl; + return this; + } + + public Builder setUrlPkgPrefixes(final String urlPkgPrefixes) { + this.urlPkgPrefixes = urlPkgPrefixes; + return this; + } + + public Builder setSecurityPrincipalName(final String securityPrincipalName) { + this.securityPrincipalName = securityPrincipalName; + return this; + } + + public Builder setSecurityCredentials(final String securityCredentials) { + this.securityCredentials = securityCredentials; + return this; + } + + public Builder setFactoryBindingName(final String factoryBindingName) { + this.factoryBindingName = factoryBindingName; + return this; + } + + public Builder setDestinationBindingName(final String destinationBindingName) { + this.destinationBindingName = destinationBindingName; + return this; + } + + public Builder setUsername(final String username) { + this.username = username; + return this; + } + + public Builder setPassword(final String password) { + this.password = password; + return this; + } + + public Builder setLayout(final Layout<? extends Serializable> layout) { + this.layout = layout; + return this; + } + + public Builder setFilter(final Filter filter) { + this.filter = filter; + return this; + } + + public Builder setIgnoreExceptions(final boolean ignoreExceptions) { + this.ignoreExceptions = ignoreExceptions; + return this; + } + + @Override + public JmsAppender build() { + final JndiManager jndiManager = JndiManager.getJndiManager(factoryName, providerUrl, urlPkgPrefixes, + securityPrincipalName, securityCredentials, null); + final JmsManager jmsManager = JmsManager.getJmsManager(name, jndiManager, factoryBindingName, + destinationBindingName, username, password); + try { + return new JmsAppender(name, filter, layout, ignoreExceptions, jmsManager); + } catch (final JMSException e) { + LOGGER.error("Error creating JmsAppender [{}].", name, e); + return null; + } + } + } + +} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/78fe32c0/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/mom/JmsManager.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/mom/JmsManager.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/mom/JmsManager.java index a36d3fb..8d70000 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/mom/JmsManager.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/mom/JmsManager.java @@ -1,174 +1,177 @@ -/* - * 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.mom; - -import java.io.Serializable; -import java.util.concurrent.TimeUnit; - -import javax.jms.Connection; -import javax.jms.ConnectionFactory; -import javax.jms.Destination; -import javax.jms.JMSException; -import javax.jms.Message; -import javax.jms.MessageConsumer; -import javax.jms.MessageProducer; -import javax.jms.Session; -import javax.naming.NamingException; - -import org.apache.logging.log4j.Logger; -import org.apache.logging.log4j.core.appender.AbstractManager; -import org.apache.logging.log4j.core.appender.ManagerFactory; -import org.apache.logging.log4j.core.net.JndiManager; -import org.apache.logging.log4j.status.StatusLogger; - -/** - * JMS connection and session manager. Can be used to access MessageProducer, MessageConsumer, and Message objects - * involving a configured ConnectionFactory and Destination. - */ -public class JmsManager extends AbstractManager { - - private static final Logger LOGGER = StatusLogger.getLogger(); - - private static final JmsManagerFactory FACTORY = new JmsManagerFactory(); - - private final JndiManager jndiManager; - private final Connection connection; - private final Session session; - private final Destination destination; - - private JmsManager(final String name, final JndiManager jndiManager, final String connectionFactoryName, - final String destinationName, final String username, final String password) - throws NamingException, JMSException { - super(null, name); - this.jndiManager = jndiManager; - final ConnectionFactory connectionFactory = this.jndiManager.lookup(connectionFactoryName); - if (username != null && password != null) { - this.connection = connectionFactory.createConnection(username, password); - } else { - this.connection = connectionFactory.createConnection(); - } - this.session = this.connection.createSession(false, Session.AUTO_ACKNOWLEDGE); - this.destination = this.jndiManager.lookup(destinationName); - this.connection.start(); - } - - /** - * Gets a JmsManager using the specified configuration parameters. - * - * @param name The name to use for this JmsManager. - * @param jndiManager The JndiManager to look up JMS information through. - * @param connectionFactoryName The binding name for the {@link javax.jms.ConnectionFactory}. - * @param destinationName The binding name for the {@link javax.jms.Destination}. - * @param username The username to connect with or {@code null} for no authentication. - * @param password The password to use with the given username or {@code null} for no authentication. - * @return The JmsManager as configured. - */ - public static JmsManager getJmsManager(final String name, final JndiManager jndiManager, - final String connectionFactoryName, final String destinationName, - final String username, final String password) { - final JmsConfiguration configuration = new JmsConfiguration(jndiManager, connectionFactoryName, destinationName, - username, password); - return getManager(name, FACTORY, configuration); - } - - /** - * Creates a MessageConsumer on this Destination using the current Session. - * - * @return A MessageConsumer on this Destination. - * @throws JMSException - */ - public MessageConsumer createMessageConsumer() throws JMSException { - return this.session.createConsumer(this.destination); - } - - /** - * Creates a MessageProducer on this Destination using the current Session. - * - * @return A MessageProducer on this Destination. - * @throws JMSException - */ - public MessageProducer createMessageProducer() throws JMSException { - return this.session.createProducer(this.destination); - } - - /** - * Creates a TextMessage or ObjectMessage from a Serializable object. For instance, when using a text-based - * {@link org.apache.logging.log4j.core.Layout} such as {@link org.apache.logging.log4j.core.layout.PatternLayout}, - * the {@link org.apache.logging.log4j.core.LogEvent} message will be serialized to a String. When using a - * layout such as {@link org.apache.logging.log4j.core.layout.SerializedLayout}, the LogEvent message will be - * serialized as a Java object. - * - * @param object The LogEvent or String message to wrap. - * @return A new JMS message containing the provided object. - * @throws JMSException - */ - public Message createMessage(final Serializable object) throws JMSException { - if (object instanceof String) { - return this.session.createTextMessage((String) object); - } - return this.session.createObjectMessage(object); - } - - @Override - protected void releaseSub(final long timeout, final TimeUnit timeUnit) { - try { - this.session.close(); - } catch (final JMSException ignored) { - // ignore - } - try { - this.connection.close(); - } catch (final JMSException ignored) { - // ignore - } - this.jndiManager.stop(timeout, timeUnit); - } - - private static class JmsConfiguration { - private final JndiManager jndiManager; - private final String connectionFactoryName; - private final String destinationName; - private final String username; - private final String password; - - private JmsConfiguration(final JndiManager jndiManager, final String connectionFactoryName, final String destinationName, - final String username, final String password) { - this.jndiManager = jndiManager; - this.connectionFactoryName = connectionFactoryName; - this.destinationName = destinationName; - this.username = username; - this.password = password; - } - } - - private static class JmsManagerFactory implements ManagerFactory<JmsManager, JmsConfiguration> { - - @Override - public JmsManager createManager(final String name, final JmsConfiguration data) { - try { - return new JmsManager(name, data.jndiManager, data.connectionFactoryName, data.destinationName, - data.username, data.password); - } catch (final Exception e) { - LOGGER.error("Error creating JmsManager using ConnectionFactory [{}] and Destination [{}].", - data.connectionFactoryName, data.destinationName, e); - return null; - } - } - } - -} +/* + * 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.mom; + +import java.io.Serializable; +import java.util.concurrent.TimeUnit; + +import javax.jms.Connection; +import javax.jms.ConnectionFactory; +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.Session; +import javax.naming.NamingException; + +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.core.appender.AbstractManager; +import org.apache.logging.log4j.core.appender.ManagerFactory; +import org.apache.logging.log4j.core.net.JndiManager; +import org.apache.logging.log4j.status.StatusLogger; + +/** + * JMS connection and session manager. Can be used to access MessageProducer, MessageConsumer, and Message objects + * involving a configured ConnectionFactory and Destination. + */ +public class JmsManager extends AbstractManager { + + private static final Logger LOGGER = StatusLogger.getLogger(); + + private static final JmsManagerFactory FACTORY = new JmsManagerFactory(); + + private final JndiManager jndiManager; + private final Connection connection; + private final Session session; + private final Destination destination; + + private JmsManager(final String name, final JndiManager jndiManager, final String connectionFactoryName, + final String destinationName, final String username, final String password) + throws NamingException, JMSException { + super(null, name); + this.jndiManager = jndiManager; + final ConnectionFactory connectionFactory = this.jndiManager.lookup(connectionFactoryName); + if (username != null && password != null) { + this.connection = connectionFactory.createConnection(username, password); + } else { + this.connection = connectionFactory.createConnection(); + } + this.session = this.connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + this.destination = this.jndiManager.lookup(destinationName); + this.connection.start(); + } + + /** + * Gets a JmsManager using the specified configuration parameters. + * + * @param name The name to use for this JmsManager. + * @param jndiManager The JndiManager to look up JMS information through. + * @param connectionFactoryName The binding name for the {@link javax.jms.ConnectionFactory}. + * @param destinationName The binding name for the {@link javax.jms.Destination}. + * @param username The username to connect with or {@code null} for no authentication. + * @param password The password to use with the given username or {@code null} for no authentication. + * @return The JmsManager as configured. + */ + public static JmsManager getJmsManager(final String name, final JndiManager jndiManager, + final String connectionFactoryName, final String destinationName, + final String username, final String password) { + final JmsConfiguration configuration = new JmsConfiguration(jndiManager, connectionFactoryName, destinationName, + username, password); + return getManager(name, FACTORY, configuration); + } + + /** + * Creates a MessageConsumer on this Destination using the current Session. + * + * @return A MessageConsumer on this Destination. + * @throws JMSException + */ + public MessageConsumer createMessageConsumer() throws JMSException { + return this.session.createConsumer(this.destination); + } + + /** + * Creates a MessageProducer on this Destination using the current Session. + * + * @return A MessageProducer on this Destination. + * @throws JMSException + */ + public MessageProducer createMessageProducer() throws JMSException { + return this.session.createProducer(this.destination); + } + + /** + * Creates a TextMessage or ObjectMessage from a Serializable object. For instance, when using a text-based + * {@link org.apache.logging.log4j.core.Layout} such as {@link org.apache.logging.log4j.core.layout.PatternLayout}, + * the {@link org.apache.logging.log4j.core.LogEvent} message will be serialized to a String. When using a + * layout such as {@link org.apache.logging.log4j.core.layout.SerializedLayout}, the LogEvent message will be + * serialized as a Java object. + * + * @param object The LogEvent or String message to wrap. + * @return A new JMS message containing the provided object. + * @throws JMSException + */ + public Message createMessage(final Serializable object) throws JMSException { + if (object instanceof String) { + return this.session.createTextMessage((String) object); + } + return this.session.createObjectMessage(object); + } + + @Override + protected boolean releaseSub(final long timeout, final TimeUnit timeUnit) { + boolean closed = true; + try { + this.session.close(); + } catch (final JMSException ignored) { + // ignore + closed = false; + } + try { + this.connection.close(); + } catch (final JMSException ignored) { + // ignore + closed = false; + } + return closed && this.jndiManager.stop(timeout, timeUnit); + } + + private static class JmsConfiguration { + private final JndiManager jndiManager; + private final String connectionFactoryName; + private final String destinationName; + private final String username; + private final String password; + + private JmsConfiguration(final JndiManager jndiManager, final String connectionFactoryName, final String destinationName, + final String username, final String password) { + this.jndiManager = jndiManager; + this.connectionFactoryName = connectionFactoryName; + this.destinationName = destinationName; + this.username = username; + this.password = password; + } + } + + private static class JmsManagerFactory implements ManagerFactory<JmsManager, JmsConfiguration> { + + @Override + public JmsManager createManager(final String name, final JmsConfiguration data) { + try { + return new JmsManager(name, data.jndiManager, data.connectionFactoryName, data.destinationName, + data.username, data.password); + } catch (final Exception e) { + LOGGER.error("Error creating JmsManager using ConnectionFactory [{}] and Destination [{}].", + data.connectionFactoryName, data.destinationName, e); + return null; + } + } + } + +}