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 @Override
88 protected void writeInternal(final LogEvent event, final Serializable serializable) {
89 if (!this.isRunning() || this.entityManagerFactory == null || this.entityManager == null
90 || this.transaction == null) {
91 throw new AppenderLoggingException(
92 "Cannot write logging event; JPA manager not connected to the database.");
93 }
94
95 AbstractLogEventWrapperEntity entity;
96 try {
97 entity = this.entityConstructor.newInstance(event);
98 } catch (final Exception e) {
99 throw new AppenderLoggingException("Failed to instantiate entity class [" + this.entityClassName + "].", e);
100 }
101
102 try {
103 this.entityManager.persist(entity);
104 } catch (final Exception e) {
105 if (this.transaction != null && this.transaction.isActive()) {
106 this.transaction.rollback();
107 this.transaction = null;
108 }
109 throw new AppenderLoggingException("Failed to insert record for log event in JPA manager: " +
110 e.getMessage(), e);
111 }
112 }
113
114 @Override
115 protected boolean commitAndClose() {
116 boolean closed = true;
117 try {
118 if (this.transaction != null && this.transaction.isActive()) {
119 this.transaction.commit();
120 }
121 } catch (final Exception e) {
122 if (this.transaction != null && this.transaction.isActive()) {
123 this.transaction.rollback();
124 }
125 } finally {
126 this.transaction = null;
127 try {
128 if (this.entityManager != null && this.entityManager.isOpen()) {
129 this.entityManager.close();
130 }
131 } catch (final Exception e) {
132 logWarn("Failed to close entity manager while logging event or flushing buffer", e);
133 closed = false;
134 } finally {
135 this.entityManager = null;
136 }
137 }
138 return closed;
139 }
140
141
142
143
144
145
146
147
148
149
150
151
152 public static JpaDatabaseManager getJPADatabaseManager(final String name, final int bufferSize,
153 final Class<? extends AbstractLogEventWrapperEntity>
154 entityClass,
155 final Constructor<? extends AbstractLogEventWrapperEntity>
156 entityConstructor,
157 final String persistenceUnitName) {
158
159 return AbstractDatabaseManager.getManager(
160 name, new FactoryData(bufferSize, entityClass, entityConstructor, persistenceUnitName), FACTORY
161 );
162 }
163
164
165
166
167 private static final class FactoryData extends AbstractDatabaseManager.AbstractFactoryData {
168 private final Class<? extends AbstractLogEventWrapperEntity> entityClass;
169 private final Constructor<? extends AbstractLogEventWrapperEntity> entityConstructor;
170 private final String persistenceUnitName;
171
172 protected FactoryData(final int bufferSize, final Class<? extends AbstractLogEventWrapperEntity> entityClass,
173 final Constructor<? extends AbstractLogEventWrapperEntity> entityConstructor,
174 final String persistenceUnitName) {
175 super(bufferSize, null);
176
177 this.entityClass = entityClass;
178 this.entityConstructor = entityConstructor;
179 this.persistenceUnitName = persistenceUnitName;
180 }
181 }
182
183
184
185
186 private static final class JPADatabaseManagerFactory implements ManagerFactory<JpaDatabaseManager, FactoryData> {
187 @Override
188 public JpaDatabaseManager createManager(final String name, final FactoryData data) {
189 return new JpaDatabaseManager(
190 name, data.getBufferSize(), data.entityClass, data.entityConstructor, data.persistenceUnitName
191 );
192 }
193 }
194 }