View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements. See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache license, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License. You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the license for the specific language governing permissions and
15   * limitations under the license.
16   */
17  package org.apache.logging.log4j.core.appender.db.jpa;
18  
19  import java.util.Map;
20  
21  import javax.persistence.Inheritance;
22  import javax.persistence.InheritanceType;
23  import javax.persistence.MappedSuperclass;
24  import javax.persistence.Transient;
25  
26  import org.apache.logging.log4j.Level;
27  import org.apache.logging.log4j.Marker;
28  import org.apache.logging.log4j.ThreadContext;
29  import org.apache.logging.log4j.core.AbstractLogEvent;
30  import org.apache.logging.log4j.core.LogEvent;
31  import org.apache.logging.log4j.message.Message;
32  
33  /**
34   * <p>
35   * Users of the JPA appender MUST extend this class, using JPA annotations on the concrete class and all of its
36   * accessor methods (as needed) to map them to the proper table and columns. Accessors you do not want persisted should
37   * be annotated with {@link Transient @Transient}. All accessors should call {@link #getWrappedEvent()} and delegate the
38   * call to the underlying event. Users may want to instead extend {@link BasicLogEventEntity}, which takes care of all
39   * of this for you.
40   * </p>
41   * <p>
42   * The concrete class must have two constructors: a public no-arg constructor to convince the JPA provider that it's a
43   * valid entity, and a public constructor that takes a single {@link LogEvent event} and passes it to the parent class
44   * with {@link #AbstractLogEventWrapperEntity(LogEvent) super(event)}. Furthermore, the concrete class must be annotated
45   * {@link javax.persistence.Entity @Entity} and {@link javax.persistence.Table @Table} and must implement a fully
46   * mutable ID property annotated with {@link javax.persistence.Id @Id} and
47   * {@link javax.persistence.GeneratedValue @GeneratedValue} to tell the JPA provider how to calculate an ID for new
48   * events.
49   * </p>
50   * <p>
51   * Many of the return types of {@link LogEvent} methods (e.g., {@link StackTraceElement}, {@link Message},
52   * {@link Marker}, {@link Throwable}, 
53   * {@link org.apache.logging.log4j.ThreadContext.ContextStack ThreadContext.ContextStack}, and 
54   * {@link Map Map&lt;String, String&gt;}) will not be recognized by the JPA provider. In conjunction with 
55   * {@link javax.persistence.Convert @Convert}, you can use the converters in the 
56   * {@link org.apache.logging.log4j.core.appender.db.jpa.converter} package to convert these types to database columns.
57   * If you want to retrieve log events from the database, you can create a true POJO entity and also use these 
58   * converters for extracting persisted values.<br>
59   * </p>
60   * <p>
61   * The mutator methods in this class not specified in {@link LogEvent} are no-op methods, implemented to satisfy the JPA
62   * requirement that accessor methods have matching mutator methods. If you create additional accessor methods, you must
63   * likewise create matching no-op mutator methods.
64   * </p>
65   *
66   * @see BasicLogEventEntity
67   */
68  @MappedSuperclass
69  @Inheritance(strategy = InheritanceType.SINGLE_TABLE)
70  public abstract class AbstractLogEventWrapperEntity implements LogEvent {
71      private static final long serialVersionUID = 1L;
72  
73      private final LogEvent wrappedEvent;
74  
75      /**
76       * Instantiates this base class. All concrete implementations must have a constructor matching this constructor's
77       * signature. The no-argument constructor is required for a standards-compliant JPA provider to accept this as an
78       * entity.
79       */
80      @SuppressWarnings("unused")
81      protected AbstractLogEventWrapperEntity() {
82          this(new NullLogEvent());
83      }
84  
85      /**
86       * Instantiates this base class. All concrete implementations must have a constructor matching this constructor's
87       * signature. This constructor is used for wrapping this entity around a logged event.
88       *
89       * @param wrappedEvent The underlying event from which information is obtained.
90       */
91      protected AbstractLogEventWrapperEntity(final LogEvent wrappedEvent) {
92          if (wrappedEvent == null) {
93              throw new IllegalArgumentException("The wrapped event cannot be null.");
94          }
95          this.wrappedEvent = wrappedEvent;
96      }
97  
98      /**
99       * All eventual accessor methods must call this method and delegate the method call to the underlying wrapped event.
100      * Annotated {@link Transient} so as not to be included in the persisted entity.
101      *
102      * @return The underlying event from which information is obtained.
103      */
104     @Transient
105     protected final LogEvent getWrappedEvent() {
106         return this.wrappedEvent;
107     }
108 
109     /**
110      * A no-op mutator to satisfy JPA requirements, as this entity is write-only.
111      *
112      * @param level Ignored.
113      */
114     @SuppressWarnings("unused")
115     public void setLevel(final Level level) {
116         // this entity is write-only
117     }
118 
119     /**
120      * A no-op mutator to satisfy JPA requirements, as this entity is write-only.
121      *
122      * @param loggerName Ignored.
123      */
124     @SuppressWarnings("unused")
125     public void setLoggerName(final String loggerName) {
126         // this entity is write-only
127     }
128 
129     /**
130      * A no-op mutator to satisfy JPA requirements, as this entity is write-only.
131      *
132      * @param source Ignored.
133      */
134     @SuppressWarnings("unused")
135     public void setSource(final StackTraceElement source) {
136         // this entity is write-only
137     }
138 
139     /**
140      * A no-op mutator to satisfy JPA requirements, as this entity is write-only.
141      *
142      * @param message Ignored.
143      */
144     @SuppressWarnings("unused")
145     public void setMessage(final Message message) {
146         // this entity is write-only
147     }
148 
149     /**
150      * A no-op mutator to satisfy JPA requirements, as this entity is write-only.
151      *
152      * @param marker Ignored.
153      */
154     @SuppressWarnings("unused")
155     public void setMarker(final Marker marker) {
156         // this entity is write-only
157     }
158 
159     /**
160      * A no-op mutator to satisfy JPA requirements, as this entity is write-only.
161      *
162      * @param threadName Ignored.
163      */
164     @SuppressWarnings("unused")
165     public void setThreadName(final String threadName) {
166         // this entity is write-only
167     }
168 
169     /**
170      * A no-op mutator to satisfy JPA requirements, as this entity is write-only.
171      *
172      * @param millis Ignored.
173      */
174     @SuppressWarnings("unused")
175     public void setTimeMillis(final long millis) {
176         // this entity is write-only
177     }
178 
179     /**
180      * A no-op mutator to satisfy JPA requirements, as this entity is write-only.
181      *
182      * @param throwable Ignored.
183      */
184     @SuppressWarnings("unused")
185     public void setThrown(final Throwable throwable) {
186         // this entity is write-only
187     }
188 
189     /**
190      * A no-op mutator to satisfy JPA requirements, as this entity is write-only.
191      *
192      * @param contextMap Ignored.
193      */
194     @SuppressWarnings("unused")
195     public void setContextMap(final Map<String, String> contextMap) {
196         // this entity is write-only
197     }
198 
199     /**
200      * A no-op mutator to satisfy JPA requirements, as this entity is write-only.
201      *
202      * @param contextStack Ignored.
203      */
204     @SuppressWarnings("unused")
205     public void setContextStack(final ThreadContext.ContextStack contextStack) {
206         // this entity is write-only
207     }
208 
209     /**
210      * A no-op mutator to satisfy JPA requirements, as this entity is write-only.
211      *
212      * @param fqcn Ignored.
213      */
214     @SuppressWarnings("unused")
215     public void setLoggerFqcn(final String fqcn) {
216         // this entity is write-only
217     }
218 
219     /**
220      * Indicates whether the source of the logging request is required downstream. Annotated
221      * {@link Transient @Transient} so as to not be included in the persisted entity.
222      *
223      * @return whether the source of the logging request is required downstream.
224      */
225     @Override
226     @Transient
227     public final boolean isIncludeLocation() {
228         return this.getWrappedEvent().isIncludeLocation();
229     }
230 
231     @Override
232     public final void setIncludeLocation(final boolean locationRequired) {
233         this.getWrappedEvent().setIncludeLocation(locationRequired);
234     }
235 
236     /**
237      * Indicates whether this event is the last one in a batch. Annotated {@link Transient @Transient} so as to not be
238      * included in the persisted entity.
239      *
240      * @return whether this event is the last one in a batch.
241      */
242     @Override
243     @Transient
244     public final boolean isEndOfBatch() {
245         return this.getWrappedEvent().isEndOfBatch();
246     }
247 
248     @Override
249     public final void setEndOfBatch(final boolean endOfBatch) {
250         this.getWrappedEvent().setEndOfBatch(endOfBatch);
251     }
252 
253     /**
254      * A no-op log event class to prevent {@code NullPointerException}s. O/RMs tend to create instances of entities in
255      * order to "play around" with them.
256      */
257     private static class NullLogEvent extends AbstractLogEvent {
258 
259         private static final long serialVersionUID = 1L;
260     	// Inherits everything
261     }
262 }