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