    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    }