1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.logging.log4j.core.appender.db.jpa;
18
19 import java.lang.reflect.Constructor;
20 import javax.persistence.EntityManager;
21 import javax.persistence.EntityManagerFactory;
22 import javax.persistence.EntityTransaction;
23 import javax.persistence.Persistence;
24
25 import org.apache.logging.log4j.core.LogEvent;
26 import org.apache.logging.log4j.core.appender.AppenderLoggingException;
27 import org.apache.logging.log4j.core.appender.ManagerFactory;
28 import org.apache.logging.log4j.core.appender.db.AbstractDatabaseManager;
29
30
31
32
33 public final class JPADatabaseManager extends AbstractDatabaseManager {
34 private static final JPADatabaseManagerFactory FACTORY = new JPADatabaseManagerFactory();
35
36 private final String entityClassName;
37 private final Constructor<? extends AbstractLogEventWrapperEntity> entityConstructor;
38 private final String persistenceUnitName;
39
40 private EntityManagerFactory entityManagerFactory;
41
42 private EntityManager entityManager;
43 private EntityTransaction transaction;
44
45 private JPADatabaseManager(final String name, final int bufferSize,
46 final Class<? extends AbstractLogEventWrapperEntity> entityClass,
47 final Constructor<? extends AbstractLogEventWrapperEntity> entityConstructor,
48 final String persistenceUnitName) {
49 super(name, bufferSize);
50 this.entityClassName = entityClass.getName();
51 this.entityConstructor = entityConstructor;
52 this.persistenceUnitName = persistenceUnitName;
53 }
54
55 @Override
56 protected void startupInternal() {
57 this.entityManagerFactory = Persistence.createEntityManagerFactory(this.persistenceUnitName);
58 }
59
60 @Override
61 protected void shutdownInternal() {
62 if (this.entityManager != null || this.transaction != null) {
63 this.commitAndClose();
64 }
65 if (this.entityManagerFactory != null && this.entityManagerFactory.isOpen()) {
66 this.entityManagerFactory.close();
67 }
68 }
69
70 @Override
71 protected void connectAndStart() {
72 try {
73 this.entityManager = this.entityManagerFactory.createEntityManager();
74 this.transaction = this.entityManager.getTransaction();
75 this.transaction.begin();
76 } catch (Exception e) {
77 throw new AppenderLoggingException(
78 "Cannot write logging event or flush buffer; manager cannot create EntityManager or transaction.", e
79 );
80 }
81 }
82
83 @Override
84 protected void writeInternal(final LogEvent event) {
85 if (!this.isRunning() || this.entityManagerFactory == null || this.entityManager == null
86 || this.transaction == null) {
87 throw new AppenderLoggingException(
88 "Cannot write logging event; JPA manager not connected to the database.");
89 }
90
91 AbstractLogEventWrapperEntity entity;
92 try {
93 entity = this.entityConstructor.newInstance(event);
94 } catch (final Exception e) {
95 throw new AppenderLoggingException("Failed to instantiate entity class [" + this.entityClassName + "].", e);
96 }
97
98 try {
99 this.entityManager.persist(entity);
100 } catch (final Exception e) {
101 if (this.transaction != null && this.transaction.isActive()) {
102 this.transaction.rollback();
103 this.transaction = null;
104 }
105 throw new AppenderLoggingException("Failed to insert record for log event in JPA manager: " +
106 e.getMessage(), e);
107 }
108 }
109
110 @Override
111 protected void commitAndClose() {
112 try {
113 if (this.transaction != null && this.transaction.isActive()) {
114 this.transaction.commit();
115 }
116 } catch (Exception e) {
117 if (this.transaction != null && this.transaction.isActive()) {
118 this.transaction.rollback();
119 }
120 } finally {
121 this.transaction = null;
122 try {
123 if (this.entityManager != null && this.entityManager.isOpen()) {
124 this.entityManager.close();
125 }
126 } catch (Exception e) {
127 LOGGER.warn("Failed to close entity manager while logging event or flushing buffer.", e);
128 } finally {
129 this.entityManager = null;
130 }
131 }
132 }
133
134
135
136
137
138
139
140
141
142
143
144
145 public static JPADatabaseManager getJPADatabaseManager(final String name, final int bufferSize,
146 final Class<? extends AbstractLogEventWrapperEntity>
147 entityClass,
148 final Constructor<? extends AbstractLogEventWrapperEntity>
149 entityConstructor,
150 final String persistenceUnitName) {
151
152 return AbstractDatabaseManager.getManager(
153 name, new FactoryData(bufferSize, entityClass, entityConstructor, persistenceUnitName), FACTORY
154 );
155 }
156
157
158
159
160 private static final class FactoryData extends AbstractDatabaseManager.AbstractFactoryData {
161 private final Class<? extends AbstractLogEventWrapperEntity> entityClass;
162 private final Constructor<? extends AbstractLogEventWrapperEntity> entityConstructor;
163 private final String persistenceUnitName;
164
165 protected FactoryData(final int bufferSize, final Class<? extends AbstractLogEventWrapperEntity> entityClass,
166 final Constructor<? extends AbstractLogEventWrapperEntity> entityConstructor,
167 final String persistenceUnitName) {
168 super(bufferSize);
169
170 this.entityClass = entityClass;
171 this.entityConstructor = entityConstructor;
172 this.persistenceUnitName = persistenceUnitName;
173 }
174 }
175
176
177
178
179 private static final class JPADatabaseManagerFactory implements ManagerFactory<JPADatabaseManager, FactoryData> {
180 @Override
181 public JPADatabaseManager createManager(final String name, final FactoryData data) {
182 return new JPADatabaseManager(
183 name, data.getBufferSize(), data.entityClass, data.entityConstructor, data.persistenceUnitName
184 );
185 }
186 }
187 }