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 import javax.persistence.EntityManager; 021 import javax.persistence.EntityManagerFactory; 022 import javax.persistence.EntityTransaction; 023 import javax.persistence.Persistence; 024 025 import org.apache.logging.log4j.core.LogEvent; 026 import org.apache.logging.log4j.core.appender.ManagerFactory; 027 import org.apache.logging.log4j.core.appender.db.AbstractDatabaseManager; 028 029 /** 030 * An {@link AbstractDatabaseManager} implementation for relational databases accessed via JPA. 031 */ 032 public final class JPADatabaseManager extends AbstractDatabaseManager { 033 private static final JPADatabaseManagerFactory FACTORY = new JPADatabaseManagerFactory(); 034 035 private final String entityClassName; 036 private final Constructor<? extends AbstractLogEventWrapperEntity> entityConstructor; 037 private final String persistenceUnitName; 038 039 private EntityManager entityManager; 040 private EntityManagerFactory entityManagerFactory; 041 private EntityTransaction transaction; 042 043 private JPADatabaseManager(final String name, final int bufferSize, 044 final Class<? extends AbstractLogEventWrapperEntity> entityClass, 045 final Constructor<? extends AbstractLogEventWrapperEntity> entityConstructor, 046 final String persistenceUnitName) { 047 super(name, bufferSize); 048 this.entityClassName = entityClass.getName(); 049 this.entityConstructor = entityConstructor; 050 this.persistenceUnitName = persistenceUnitName; 051 } 052 053 @Override 054 protected void connectInternal() { 055 this.entityManagerFactory = Persistence.createEntityManagerFactory(this.persistenceUnitName); 056 this.entityManager = this.entityManagerFactory.createEntityManager(); 057 this.transaction = this.entityManager.getTransaction(); 058 } 059 060 @Override 061 protected void disconnectInternal() { 062 this.transaction = null; 063 064 if (this.entityManager != null && this.entityManager.isOpen()) { 065 this.entityManager.close(); 066 } 067 068 if (this.entityManagerFactory != null && this.entityManagerFactory.isOpen()) { 069 this.entityManagerFactory.close(); 070 } 071 } 072 073 @Override 074 protected void writeInternal(final LogEvent event) { 075 if (!this.isConnected() || this.transaction == null || this.entityManager == null 076 || this.entityManagerFactory == null) { 077 LOGGER.error("Cannot write logging event; manager [{}] not connected to the database.", this.getName()); 078 return; 079 } 080 081 AbstractLogEventWrapperEntity entity; 082 try { 083 entity = this.entityConstructor.newInstance(event); 084 } catch (final Exception e) { 085 LOGGER.error("Failed to instantiate entity class {}.", this.entityClassName, e); 086 return; 087 } 088 089 try { 090 this.transaction.begin(); 091 this.entityManager.persist(entity); 092 this.transaction.commit(); 093 } catch (final Exception e) { 094 LOGGER.error("Failed to persist log event entity.", e); 095 this.transaction.rollback(); 096 } 097 } 098 099 /** 100 * Creates a JPA manager for use within the {@link JPAAppender}, or returns a suitable one if it already exists. 101 * 102 * @param name The name of the manager, which should include connection details, entity class name, etc. 103 * @param bufferSize The size of the log event buffer. 104 * @param entityClass The fully-qualified class name of the {@link AbstractLogEventWrapperEntity} concrete 105 * implementation. 106 * @param entityConstructor The one-arg {@link LogEvent} constructor for the concrete entity class. 107 * @param persistenceUnitName The name of the JPA persistence unit that should be used for persisting log events. 108 * @return a new or existing JPA manager as applicable. 109 */ 110 public static JPADatabaseManager getJPADatabaseManager(final String name, final int bufferSize, 111 final Class<? extends AbstractLogEventWrapperEntity> 112 entityClass, 113 final Constructor<? extends AbstractLogEventWrapperEntity> 114 entityConstructor, 115 final String persistenceUnitName) { 116 117 return AbstractDatabaseManager.getManager( 118 name, new FactoryData(bufferSize, entityClass, entityConstructor, persistenceUnitName), FACTORY 119 ); 120 } 121 122 /** 123 * Encapsulates data that {@link JPADatabaseManagerFactory} uses to create managers. 124 */ 125 private static final class FactoryData extends AbstractDatabaseManager.AbstractFactoryData { 126 private final Class<? extends AbstractLogEventWrapperEntity> entityClass; 127 private final Constructor<? extends AbstractLogEventWrapperEntity> entityConstructor; 128 private final String persistenceUnitName; 129 130 protected FactoryData(final int bufferSize, final Class<? extends AbstractLogEventWrapperEntity> entityClass, 131 final Constructor<? extends AbstractLogEventWrapperEntity> entityConstructor, 132 final String persistenceUnitName) { 133 super(bufferSize); 134 135 this.entityClass = entityClass; 136 this.entityConstructor = entityConstructor; 137 this.persistenceUnitName = persistenceUnitName; 138 } 139 } 140 141 /** 142 * Creates managers. 143 */ 144 private static final class JPADatabaseManagerFactory implements ManagerFactory<JPADatabaseManager, FactoryData> { 145 @Override 146 public JPADatabaseManager createManager(final String name, final FactoryData data) { 147 return new JPADatabaseManager( 148 name, data.getBufferSize(), data.entityClass, data.entityConstructor, data.persistenceUnitName 149 ); 150 } 151 } 152 }