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