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