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     */
017    package org.apache.logging.log4j.core.appender.db.jpa;
018    
019    import java.util.Map;
020    
021    import javax.persistence.Inheritance;
022    import javax.persistence.InheritanceType;
023    import javax.persistence.MappedSuperclass;
024    import javax.persistence.Transient;
025    
026    import org.apache.logging.log4j.Level;
027    import org.apache.logging.log4j.Marker;
028    import org.apache.logging.log4j.ThreadContext;
029    import org.apache.logging.log4j.core.AbstractLogEvent;
030    import org.apache.logging.log4j.core.LogEvent;
031    import 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)
070    public 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    }