001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache license, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the license for the specific language governing permissions and
015 * limitations under the license.
016 */
017package org.apache.logging.log4j.core.appender.db.jpa;
018
019import java.util.Map;
020import javax.persistence.Inheritance;
021import javax.persistence.InheritanceType;
022import javax.persistence.MappedSuperclass;
023import javax.persistence.Transient;
024
025import org.apache.logging.log4j.Level;
026import org.apache.logging.log4j.Marker;
027import org.apache.logging.log4j.ThreadContext;
028import org.apache.logging.log4j.core.AbstractLogEvent;
029import org.apache.logging.log4j.util.ReadOnlyStringMap;
030import org.apache.logging.log4j.core.LogEvent;
031import org.apache.logging.log4j.core.appender.db.jpa.converter.ContextDataAttributeConverter;
032import org.apache.logging.log4j.core.impl.Log4jLogEvent;
033import org.apache.logging.log4j.message.Message;
034
035/**
036 * <p>
037 * Users of the JPA appender MUST extend this class, using JPA annotations on the concrete class and all of its
038 * accessor methods (as needed) to map them to the proper table and columns. Accessors you do not want persisted should
039 * be annotated with {@link Transient @Transient}. All accessors should call {@link #getWrappedEvent()} and delegate the
040 * call to the underlying event. Users may want to instead extend {@link BasicLogEventEntity}, which takes care of all
041 * of this for you.
042 * </p>
043 * <p>
044 * The concrete class must have two constructors: a public no-arg constructor to convince the JPA provider that it's a
045 * valid entity, and a public constructor that takes a single {@link LogEvent event} and passes it to the parent class
046 * with {@link #AbstractLogEventWrapperEntity(LogEvent) super(event)}. Furthermore, the concrete class must be annotated
047 * {@link javax.persistence.Entity @Entity} and {@link javax.persistence.Table @Table} and must implement a fully
048 * mutable ID property annotated with {@link javax.persistence.Id @Id} and
049 * {@link javax.persistence.GeneratedValue @GeneratedValue} to tell the JPA provider how to calculate an ID for new
050 * events.
051 * </p>
052 * <p>
053 * Many of the return types of {@link LogEvent} methods (e.g., {@link StackTraceElement}, {@link Message},
054 * {@link Marker}, {@link Throwable},
055 * {@link org.apache.logging.log4j.ThreadContext.ContextStack ThreadContext.ContextStack}, and
056 * {@link Map Map&lt;String, String&gt;}) will not be recognized by the JPA provider. In conjunction with
057 * {@link javax.persistence.Convert @Convert}, you can use the converters in the
058 * {@link org.apache.logging.log4j.core.appender.db.jpa.converter} package to convert these types to database columns.
059 * If you want to retrieve log events from the database, you can create a true POJO entity and also use these
060 * converters for extracting persisted values.<br>
061 * </p>
062 * <p>
063 * The mutator methods in this class not specified in {@link LogEvent} are no-op methods, implemented to satisfy the JPA
064 * requirement that accessor methods have matching mutator methods. If you create additional accessor methods, you must
065 * likewise create matching no-op mutator methods.
066 * </p>
067 *
068 * @see BasicLogEventEntity
069 */
070@MappedSuperclass
071@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
072public abstract class AbstractLogEventWrapperEntity implements LogEvent {
073    private static final long serialVersionUID = 1L;
074
075    private final LogEvent wrappedEvent;
076
077    /**
078     * Instantiates this base class. All concrete implementations must have a constructor matching this constructor's
079     * signature. The no-argument constructor is required for a standards-compliant JPA provider to accept this as an
080     * entity.
081     */
082    @SuppressWarnings("unused")
083    protected AbstractLogEventWrapperEntity() {
084        this(new NullLogEvent());
085    }
086
087    /**
088     * Instantiates this base class. All concrete implementations must have a constructor matching this constructor's
089     * signature. This constructor is used for wrapping this entity around a logged event.
090     *
091     * @param wrappedEvent The underlying event from which information is obtained.
092     */
093    protected AbstractLogEventWrapperEntity(final LogEvent wrappedEvent) {
094        if (wrappedEvent == null) {
095            throw new IllegalArgumentException("The wrapped event cannot be null.");
096        }
097        this.wrappedEvent = wrappedEvent;
098    }
099
100    @Override
101    public LogEvent toImmutable() {
102        return Log4jLogEvent.createMemento(this);
103    }
104
105    /**
106     * All eventual accessor methods must call this method and delegate the method call to the underlying wrapped event.
107     * Annotated {@link Transient} so as not to be included in the persisted entity.
108     *
109     * @return The underlying event from which information is obtained.
110     */
111    @Transient
112    protected final LogEvent getWrappedEvent() {
113        return this.wrappedEvent;
114    }
115
116    /**
117     * A no-op mutator to satisfy JPA requirements, as this entity is write-only.
118     *
119     * @param level Ignored.
120     */
121    @SuppressWarnings("unused")
122    public void setLevel(final Level level) {
123        // this entity is write-only
124    }
125
126    /**
127     * A no-op mutator to satisfy JPA requirements, as this entity is write-only.
128     *
129     * @param loggerName Ignored.
130     */
131    @SuppressWarnings("unused")
132    public void setLoggerName(final String loggerName) {
133        // this entity is write-only
134    }
135
136    /**
137     * A no-op mutator to satisfy JPA requirements, as this entity is write-only.
138     *
139     * @param source Ignored.
140     */
141    @SuppressWarnings("unused")
142    public void setSource(final StackTraceElement source) {
143        // this entity is write-only
144    }
145
146    /**
147     * A no-op mutator to satisfy JPA requirements, as this entity is write-only.
148     *
149     * @param message Ignored.
150     */
151    @SuppressWarnings("unused")
152    public void setMessage(final Message message) {
153        // this entity is write-only
154    }
155
156    /**
157     * A no-op mutator to satisfy JPA requirements, as this entity is write-only.
158     *
159     * @param marker Ignored.
160     */
161    @SuppressWarnings("unused")
162    public void setMarker(final Marker marker) {
163        // this entity is write-only
164    }
165
166    /**
167     * A no-op mutator to satisfy JPA requirements, as this entity is write-only.
168     *
169     * @param threadId Ignored.
170     */
171    @SuppressWarnings("unused")
172    public void setThreadId(final long threadId) {
173        // this entity is write-only
174    }
175
176    /**
177     * A no-op mutator to satisfy JPA requirements, as this entity is write-only.
178     *
179     * @param threadName Ignored.
180     */
181    @SuppressWarnings("unused")
182    public void setThreadName(final String threadName) {
183        // this entity is write-only
184    }
185
186    /**
187     * A no-op mutator to satisfy JPA requirements, as this entity is write-only.
188     *
189     * @param threadPriority Ignored.
190     */
191    @SuppressWarnings("unused")
192    public void setThreadPriority(final int threadPriority) {
193        // this entity is write-only
194    }
195
196    /**
197     * A no-op mutator to satisfy JPA requirements, as this entity is write-only.
198     *
199     * @param nanoTime Ignored.
200     */
201    @SuppressWarnings("unused")
202    public void setNanoTime(final long nanoTime) {
203        // this entity is write-only
204    }
205
206    /**
207     * A no-op mutator to satisfy JPA requirements, as this entity is write-only.
208     *
209     * @param millis Ignored.
210     */
211    @SuppressWarnings("unused")
212    public void setTimeMillis(final long millis) {
213        // this entity is write-only
214    }
215
216    /**
217     * A no-op mutator to satisfy JPA requirements, as this entity is write-only.
218     *
219     * @param throwable Ignored.
220     */
221    @SuppressWarnings("unused")
222    public void setThrown(final Throwable throwable) {
223        // this entity is write-only
224    }
225
226    /**
227     * A no-op mutator to satisfy JPA requirements, as this entity is write-only.
228     *
229     * @param contextData Ignored.
230     */
231    @SuppressWarnings("unused")
232    public void setContextData(final ReadOnlyStringMap contextData) {
233        // this entity is write-only
234    }
235
236    /**
237     * A no-op mutator to satisfy JPA requirements, as this entity is write-only.
238     *
239     * @param map Ignored.
240     */
241    @SuppressWarnings("unused")
242    public void setContextMap(final Map<String, String> map) {
243        // this entity is write-only
244    }
245
246    /**
247     * A no-op mutator to satisfy JPA requirements, as this entity is write-only.
248     *
249     * @param contextStack Ignored.
250     */
251    @SuppressWarnings("unused")
252    public void setContextStack(final ThreadContext.ContextStack contextStack) {
253        // this entity is write-only
254    }
255
256    /**
257     * A no-op mutator to satisfy JPA requirements, as this entity is write-only.
258     *
259     * @param fqcn Ignored.
260     */
261    @SuppressWarnings("unused")
262    public void setLoggerFqcn(final String fqcn) {
263        // this entity is write-only
264    }
265
266    /**
267     * Indicates whether the source of the logging request is required downstream. Annotated
268     * {@link Transient @Transient} so as to not be included in the persisted entity.
269     *
270     * @return whether the source of the logging request is required downstream.
271     */
272    @Override
273    @Transient
274    public final boolean isIncludeLocation() {
275        return this.getWrappedEvent().isIncludeLocation();
276    }
277
278    @Override
279    public final void setIncludeLocation(final boolean locationRequired) {
280        this.getWrappedEvent().setIncludeLocation(locationRequired);
281    }
282
283    /**
284     * Indicates whether this event is the last one in a batch. Annotated {@link Transient @Transient} so as to not be
285     * included in the persisted entity.
286     *
287     * @return whether this event is the last one in a batch.
288     */
289    @Override
290    @Transient
291    public final boolean isEndOfBatch() {
292        return this.getWrappedEvent().isEndOfBatch();
293    }
294
295    @Override
296    public final void setEndOfBatch(final boolean endOfBatch) {
297        this.getWrappedEvent().setEndOfBatch(endOfBatch);
298    }
299
300    /**
301     * Gets the context map. Transient, since the String version of the data is obtained via ReadOnlyStringMap.
302     *
303     * @return the context data.
304     * @see ContextDataAttributeConverter
305     * @see org.apache.logging.log4j.core.appender.db.jpa.converter.ContextDataAttributeConverter
306     */
307    @Override
308    @Transient
309    //@Convert(converter = ContextDataAttributeConverter.class)
310    public ReadOnlyStringMap getContextData() {
311        return this.getWrappedEvent().getContextData();
312    }
313
314    /**
315     * A no-op log event class to prevent {@code NullPointerException}s. O/RMs tend to create instances of entities in
316     * order to "play around" with them.
317     */
318    private static class NullLogEvent extends AbstractLogEvent {
319
320        private static final long serialVersionUID = 1L;
321        // Inherits everything
322    }
323}