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    import javax.persistence.MappedSuperclass;
021    import javax.persistence.Transient;
022    
023    import org.apache.logging.log4j.Level;
024    import org.apache.logging.log4j.Marker;
025    import org.apache.logging.log4j.ThreadContext;
026    import org.apache.logging.log4j.core.LogEvent;
027    import org.apache.logging.log4j.message.Message;
028    
029    /**
030     * Users of the JPA appender MUST extend this class, using JPA annotations on the concrete class and all of its
031     * accessor methods (as needed) to map them to the proper table and columns. Accessors you do not want persisted should
032     * be annotated with {@link Transient @Transient}. All accessors should call {@link #getWrappedEvent()} and delegate the
033     * call to the underlying event. Users may want to instead extend {@link BasicLogEventEntity}, which takes care of all
034     * of this for you.<br>
035     * <br>
036     * The concrete class must have two constructors: a public no-arg constructor to convince the JPA provider that it's a
037     * valid entity, and a public constructor that takes a single {@link LogEvent event} and passes it to the parent class
038     * with {@link #AbstractLogEventWrapperEntity(LogEvent) super(event)}. Furthermore, the concrete class must be annotated
039     * {@link javax.persistence.Entity @Entity} and {@link javax.persistence.Table @Table} and must implement a fully
040     * mutable ID property annotated with {@link javax.persistence.Id @Id} and
041     * {@link javax.persistence.GeneratedValue @GeneratedValue} to tell the JPA provider how to calculate an ID for new
042     * events.<br>
043     * <br>
044     * Many of the return types of {@link LogEvent} methods (e.g., {@link StackTraceElement}, {@link Message},
045     * {@link Marker}, {@link Throwable}, {@link ThreadContext.ContextStack}, and {@link Map Map&lt;String, String&gt}) will
046     * not be recognized by the JPA provider. In conjunction with {@link javax.persistence.Convert @Convert}, you can use
047     * the converters in the {@link org.apache.logging.log4j.core.appender.db.jpa.converter} package to convert these
048     * types to database columns. If you want to retrieve log events from the database, you can create a true POJO entity
049     * and also use these converters for extracting persisted values.<br>
050     * <br>
051     * The mutator methods in this class not specified in {@link LogEvent} are no-op methods, implemented to satisfy the JPA
052     * requirement that accessor methods have matching mutator methods. If you create additional accessor methods, you must
053     * likewise create matching no-op mutator methods.
054     *
055     * @see BasicLogEventEntity
056     */
057    @MappedSuperclass
058    public abstract class AbstractLogEventWrapperEntity implements LogEvent {
059        private static final long serialVersionUID = 1L;
060    
061        private final LogEvent wrappedEvent;
062    
063        /**
064         * Instantiates this base class. All concrete implementations must have a constructor matching this constructor's
065         * signature. The no-argument constructor is required for a standards-compliant JPA provider to accept this as an
066         * entity.
067         */
068        @SuppressWarnings("unused")
069        protected AbstractLogEventWrapperEntity() {
070            this(null);
071        }
072    
073        /**
074         * Instantiates this base class. All concrete implementations must have a constructor matching this constructor's
075         * signature. This constructor is used for wrapping this entity around a logged event.
076         * 
077         * @param wrappedEvent The underlying event from which information is obtained.
078         */
079        protected AbstractLogEventWrapperEntity(final LogEvent wrappedEvent) {
080            this.wrappedEvent = wrappedEvent;
081        }
082    
083        /**
084         * All eventual accessor methods must call this method and delegate the method call to the underlying wrapped event.
085         * Annotated {@link @Transient} so as not to be included in the persisted entity.
086         * 
087         * @return The underlying event from which information is obtained.
088         */
089        @Transient
090        protected final LogEvent getWrappedEvent() {
091            return this.wrappedEvent;
092        }
093    
094        /**
095         * A no-op mutator to satisfy JPA requirements, as this entity is write-only.
096         *
097         * @param level Ignored.
098         */
099        @SuppressWarnings("unused")
100        public void setLevel(final Level level) {
101            // this entity is write-only
102        }
103    
104        /**
105         * A no-op mutator to satisfy JPA requirements, as this entity is write-only.
106         *
107         * @param loggerName Ignored.
108         */
109        @SuppressWarnings("unused")
110        public void setLoggerName(final String loggerName) {
111            // this entity is write-only
112        }
113    
114        /**
115         * A no-op mutator to satisfy JPA requirements, as this entity is write-only.
116         *
117         * @param source Ignored.
118         */
119        @SuppressWarnings("unused")
120        public void setSource(final StackTraceElement source) {
121            // this entity is write-only
122        }
123    
124        /**
125         * A no-op mutator to satisfy JPA requirements, as this entity is write-only.
126         *
127         * @param message Ignored.
128         */
129        @SuppressWarnings("unused")
130        public void setMessage(final Message message) {
131            // this entity is write-only
132        }
133    
134        /**
135         * A no-op mutator to satisfy JPA requirements, as this entity is write-only.
136         *
137         * @param marker Ignored.
138         */
139        @SuppressWarnings("unused")
140        public void setMarker(final Marker marker) {
141            // this entity is write-only
142        }
143    
144        /**
145         * A no-op mutator to satisfy JPA requirements, as this entity is write-only.
146         *
147         * @param threadName Ignored.
148         */
149        @SuppressWarnings("unused")
150        public void setThreadName(final String threadName) {
151            // this entity is write-only
152        }
153    
154        /**
155         * A no-op mutator to satisfy JPA requirements, as this entity is write-only.
156         *
157         * @param millis Ignored.
158         */
159        @SuppressWarnings("unused")
160        public void setMillis(final long millis) {
161            // this entity is write-only
162        }
163    
164        /**
165         * A no-op mutator to satisfy JPA requirements, as this entity is write-only.
166         *
167         * @param throwable Ignored.
168         */
169        @SuppressWarnings("unused")
170        public void setThrown(final Throwable throwable) {
171            // this entity is write-only
172        }
173    
174        /**
175         * A no-op mutator to satisfy JPA requirements, as this entity is write-only.
176         *
177         * @param contextMap Ignored.
178         */
179        @SuppressWarnings("unused")
180        public void setContextMap(final Map<String, String> contextMap) {
181            // this entity is write-only
182        }
183    
184        /**
185         * A no-op mutator to satisfy JPA requirements, as this entity is write-only.
186         *
187         * @param contextStack Ignored.
188         */
189        @SuppressWarnings("unused")
190        public void setContextStack(final ThreadContext.ContextStack contextStack) {
191            // this entity is write-only
192        }
193    
194        /**
195         * A no-op mutator to satisfy JPA requirements, as this entity is write-only.
196         *
197         * @param fqcn Ignored.
198         */
199        @SuppressWarnings("unused")
200        public void setFQCN(final String fqcn) {
201            // this entity is write-only
202        }
203    
204        /**
205         * Indicates whether the source of the logging request is required downstream. Annotated {@link Transient @Transient} so as
206         * not to be included in the persisted entity.
207         *
208         * @return whether the source of the logging request is required downstream.
209         */
210        @Override
211        @Transient
212        public final boolean isIncludeLocation() {
213            return this.getWrappedEvent().isIncludeLocation();
214        }
215    
216        @Override
217        public final void setIncludeLocation(final boolean locationRequired) {
218            this.getWrappedEvent().setIncludeLocation(locationRequired);
219        }
220    
221        /**
222         * Indicates whether this event is the last one in a batch. Annotated {@link Transient @Transient} so as not to be included
223         * in the persisted entity.
224         *
225         * @return whether this event is the last one in a batch.
226         */
227        @Override
228        @Transient
229        public final boolean isEndOfBatch() {
230            return this.getWrappedEvent().isEndOfBatch();
231        }
232    
233        @Override
234        public final void setEndOfBatch(final boolean endOfBatch) {
235            this.getWrappedEvent().setEndOfBatch(endOfBatch);
236        }
237    }