    001    /*
    002     * Licensed to the Apache Software Foundation (ASF) under one or more
    003     * contributor license agreements. See the NOTICE file distributed with
    004     * this work for additional information regarding copyright ownership.
    005     * The ASF licenses this file to You under the Apache license, Version 2.0
    006     * (the "License"); you may not use this file except in compliance with
    007     * the License. You may obtain a copy of the License at
    008     *
    009     *      http://www.apache.org/licenses/LICENSE-2.0
    010     *
    011     * Unless required by applicable law or agreed to in writing, software
    012     * distributed under the License is distributed on an "AS IS" BASIS,
    013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014     * See the license for the specific language governing permissions and
    015     * limitations under the license.
    016     */
    017    package org.apache.logging.log4j.core.appender.db.jpa;
    018
    019    import java.lang.reflect.Constructor;
    020
    021    import javax.persistence.EntityManager;
    022    import javax.persistence.EntityManagerFactory;
    023    import javax.persistence.EntityTransaction;
    024    import javax.persistence.Persistence;
    025
    026    import org.apache.logging.log4j.core.LogEvent;
    027    import org.apache.logging.log4j.core.appender.AppenderLoggingException;
    028    import org.apache.logging.log4j.core.appender.ManagerFactory;
    029    import org.apache.logging.log4j.core.appender.db.AbstractDatabaseManager;
    030
    031    /**
    032     * An {@link AbstractDatabaseManager} implementation for relational databases accessed via JPA.
    033     */
    034    public final class JpaDatabaseManager extends AbstractDatabaseManager {
    035        private static final JPADatabaseManagerFactory FACTORY = new JPADatabaseManagerFactory();
    036
    037        private final String entityClassName;
    038        private final Constructor<? extends AbstractLogEventWrapperEntity> entityConstructor;
    039        private final String persistenceUnitName;
    040
    041        private EntityManagerFactory entityManagerFactory;
    042
    043        private EntityManager entityManager;
    044        private EntityTransaction transaction;
    045
    046        private JpaDatabaseManager(final String name, final int bufferSize,
    047                                   final Class<? extends AbstractLogEventWrapperEntity> entityClass,
    048                                   final Constructor<? extends AbstractLogEventWrapperEntity> entityConstructor,
    049                                   final String persistenceUnitName) {
    050            super(name, bufferSize);
    051            this.entityClassName = entityClass.getName();
    052            this.entityConstructor = entityConstructor;
    053            this.persistenceUnitName = persistenceUnitName;
    054        }
    055
    056        @Override
    057        protected void startupInternal() {
    058            this.entityManagerFactory = Persistence.createEntityManagerFactory(this.persistenceUnitName);
    059        }
    060
    061        @Override
    062        protected void shutdownInternal() {
    063            if (this.entityManager != null || this.transaction != null) {
    064                this.commitAndClose();
    065            }
    066            if (this.entityManagerFactory != null && this.entityManagerFactory.isOpen()) {
    067                this.entityManagerFactory.close();
    068            }
    069        }
    070
    071        @Override
    072        protected void connectAndStart() {
    073            try {
    074                this.entityManager = this.entityManagerFactory.createEntityManager();
    075                this.transaction = this.entityManager.getTransaction();
    076                this.transaction.begin();
    077            } catch (final Exception e) {
    078                throw new AppenderLoggingException(
    079                        "Cannot write logging event or flush buffer; manager cannot create EntityManager or transaction.", e
    080                );
    081            }
    082        }
    083
    084        @Override
    085        protected void writeInternal(final LogEvent event) {
    086            if (!this.isRunning() || this.entityManagerFactory == null || this.entityManager == null
    087                    || this.transaction == null) {
    088                throw new AppenderLoggingException(
    089                        "Cannot write logging event; JPA manager not connected to the database.");
    090            }
    091
    092            AbstractLogEventWrapperEntity entity;
    093            try {
    094                entity = this.entityConstructor.newInstance(event);
    095            } catch (final Exception e) {
    096                throw new AppenderLoggingException("Failed to instantiate entity class [" + this.entityClassName + "].", e);
    097            }
    098
    099            try {
    100                this.entityManager.persist(entity);
    101            } catch (final Exception e) {
    102                if (this.transaction != null && this.transaction.isActive()) {
    103                    this.transaction.rollback();
    104                    this.transaction = null;
    105                }
    106                throw new AppenderLoggingException("Failed to insert record for log event in JPA manager: " +
    107                        e.getMessage(), e);
    108            }
    109        }
    110
    111        @Override
    112        protected void commitAndClose() {
    113            try {
    114                if (this.transaction != null && this.transaction.isActive()) {
    115                    this.transaction.commit();
    116                }
    117            } catch (final Exception e) {
    118                if (this.transaction != null && this.transaction.isActive()) {
    119                    this.transaction.rollback();
    120                }
    121            } finally {
    122                this.transaction = null;
    123                try {
    124                    if (this.entityManager != null && this.entityManager.isOpen()) {
    125                        this.entityManager.close();
    126                    }
    127                } catch (final Exception e) {
    128                    LOGGER.warn("Failed to close entity manager while logging event or flushing buffer.", e);
    129                } finally {
    130                    this.entityManager = null;
    131                }
    132            }
    133        }
    134
    135        /**
    136         * Creates a JPA manager for use within the {@link JpaAppender}, or returns a suitable one if it already exists.
    137         *
    138         * @param name The name of the manager, which should include connection details, entity class name, etc.
    139         * @param bufferSize The size of the log event buffer.
    140         * @param entityClass The fully-qualified class name of the {@link AbstractLogEventWrapperEntity} concrete
    141         *                    implementation.
    142         * @param entityConstructor The one-arg {@link LogEvent} constructor for the concrete entity class.
    143         * @param persistenceUnitName The name of the JPA persistence unit that should be used for persisting log events.
    144         * @return a new or existing JPA manager as applicable.
    145         */
    146        public static JpaDatabaseManager getJPADatabaseManager(final String name, final int bufferSize,
    147                                                               final Class<? extends AbstractLogEventWrapperEntity>
    148                                                                       entityClass,
    149                                                               final Constructor<? extends AbstractLogEventWrapperEntity>
    150                                                                       entityConstructor,
    151                                                               final String persistenceUnitName) {
    152
    153            return AbstractDatabaseManager.getManager(
    154                    name, new FactoryData(bufferSize, entityClass, entityConstructor, persistenceUnitName), FACTORY
    155            );
    156        }
    157
    158        /**
    159         * Encapsulates data that {@link JPADatabaseManagerFactory} uses to create managers.
    160         */
    161        private static final class FactoryData extends AbstractDatabaseManager.AbstractFactoryData {
    162            private final Class<? extends AbstractLogEventWrapperEntity> entityClass;
    163            private final Constructor<? extends AbstractLogEventWrapperEntity> entityConstructor;
    164            private final String persistenceUnitName;
    165
    166            protected FactoryData(final int bufferSize, final Class<? extends AbstractLogEventWrapperEntity> entityClass,
    167                                  final Constructor<? extends AbstractLogEventWrapperEntity> entityConstructor,
    168                                  final String persistenceUnitName) {
    169                super(bufferSize);
    170
    171                this.entityClass = entityClass;
    172                this.entityConstructor = entityConstructor;
    173                this.persistenceUnitName = persistenceUnitName;
    174            }
    175        }
    176
    177        /**
    178         * Creates managers.
    179         */
    180        private static final class JPADatabaseManagerFactory implements ManagerFactory<JpaDatabaseManager, FactoryData> {
    181            @Override
    182            public JpaDatabaseManager createManager(final String name, final FactoryData data) {
    183                return new JpaDatabaseManager(
    184                        name, data.getBufferSize(), data.entityClass, data.entityConstructor, data.persistenceUnitName
    185                );
    186            }
    187        }
    188    }