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