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;
020
021import javax.persistence.Inheritance;
022import javax.persistence.InheritanceType;
023import javax.persistence.MappedSuperclass;
024import javax.persistence.Transient;
025
026import org.apache.logging.log4j.Level;
027import org.apache.logging.log4j.Marker;
028import org.apache.logging.log4j.ThreadContext;
029import org.apache.logging.log4j.core.AbstractLogEvent;
030import org.apache.logging.log4j.core.LogEvent;
031import org.apache.logging.log4j.message.Message;
032
033/**
034 * <p>
035 * Users of the JPA appender MUST extend this class, using JPA annotations on the concrete class and all of its
036 * accessor methods (as needed) to map them to the proper table and columns. Accessors you do not want persisted should
037 * be annotated with {@link Transient @Transient}. All accessors should call {@link #getWrappedEvent()} and delegate the
038 * call to the underlying event. Users may want to instead extend {@link BasicLogEventEntity}, which takes care of all
039 * of this for you.
040 * </p>
041 * <p>
042 * The concrete class must have two constructors: a public no-arg constructor to convince the JPA provider that it's a
043 * valid entity, and a public constructor that takes a single {@link LogEvent event} and passes it to the parent class
044 * with {@link #AbstractLogEventWrapperEntity(LogEvent) super(event)}. Furthermore, the concrete class must be annotated
045 * {@link javax.persistence.Entity @Entity} and {@link javax.persistence.Table @Table} and must implement a fully
046 * mutable ID property annotated with {@link javax.persistence.Id @Id} and
047 * {@link javax.persistence.GeneratedValue @GeneratedValue} to tell the JPA provider how to calculate an ID for new
048 * events.
049 * </p>
050 * <p>
051 * Many of the return types of {@link LogEvent} methods (e.g., {@link StackTraceElement}, {@link Message},
052 * {@link Marker}, {@link Throwable}, 
053 * {@link org.apache.logging.log4j.ThreadContext.ContextStack ThreadContext.ContextStack}, and 
054 * {@link Map Map&lt;String, String&gt;}) will not be recognized by the JPA provider. In conjunction with 
055 * {@link javax.persistence.Convert @Convert}, you can use the converters in the 
056 * {@link org.apache.logging.log4j.core.appender.db.jpa.converter} package to convert these types to database columns.
057 * If you want to retrieve log events from the database, you can create a true POJO entity and also use these 
058 * converters for extracting persisted values.<br>
059 * </p>
060 * <p>
061 * The mutator methods in this class not specified in {@link LogEvent} are no-op methods, implemented to satisfy the JPA
062 * requirement that accessor methods have matching mutator methods. If you create additional accessor methods, you must
063 * likewise create matching no-op mutator methods.
064 * </p>
065 *
066 * @see BasicLogEventEntity
067 */
068@MappedSuperclass
069@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
070public abstract class AbstractLogEventWrapperEntity implements LogEvent {
071    private static final long serialVersionUID = 1L;
072
073    private final LogEvent wrappedEvent;
074
075    /**
076     * Instantiates this base class. All concrete implementations must have a constructor matching this constructor's
077     * signature. The no-argument constructor is required for a standards-compliant JPA provider to accept this as an
078     * entity.
079     */
080    @SuppressWarnings("unused")
081    protected AbstractLogEventWrapperEntity() {
082        this(new NullLogEvent());
083    }
084
085    /**
086     * Instantiates this base class. All concrete implementations must have a constructor matching this constructor's
087     * signature. This constructor is used for wrapping this entity around a logged event.
088     *
089     * @param wrappedEvent The underlying event from which information is obtained.
090     */
091    protected AbstractLogEventWrapperEntity(final LogEvent wrappedEvent) {
092        if (wrappedEvent == null) {
093            throw new IllegalArgumentException("The wrapped event cannot be null.");
094        }
095        this.wrappedEvent = wrappedEvent;
096    }
097
098    /**
099     * 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}