View Javadoc

    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one or more
    3    * contributor license agreements. See the NOTICE file distributed with
    4    * this work for additional information regarding copyright ownership.
    5    * The ASF licenses this file to You under the Apache license, Version 2.0
    6    * (the "License"); you may not use this file except in compliance with
    7    * the License. You may obtain a copy of the License at
    8    *
    9    *      http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the license for the specific language governing permissions and
    15   * limitations under the license.
    16   */
    17  package org.apache.logging.log4j.core.appender.db.jpa;
    18
    19  import java.util.Map;
    20
    21  import javax.persistence.Inheritance;
    22  import javax.persistence.InheritanceType;
    23  import javax.persistence.MappedSuperclass;
    24  import javax.persistence.Transient;
    25
    26  import org.apache.logging.log4j.Level;
    27  import org.apache.logging.log4j.Marker;
    28  import org.apache.logging.log4j.ThreadContext;
    29  import org.apache.logging.log4j.core.AbstractLogEvent;
    30  import org.apache.logging.log4j.core.LogEvent;
    31  import org.apache.logging.log4j.message.Message;
    32
    33  /**
    34   * <p>
    35   * Users of the JPA appender MUST extend this class, using JPA annotations on the concrete class and all of its
    36   * accessor methods (as needed) to map them to the proper table and columns. Accessors you do not want persisted should
    37   * be annotated with {@link Transient @Transient}. All accessors should call {@link #getWrappedEvent()} and delegate the
    38   * call to the underlying event. Users may want to instead extend {@link BasicLogEventEntity}, which takes care of all
    39   * of this for you.
    40   * </p>
    41   * <p>
    42   * The concrete class must have two constructors: a public no-arg constructor to convince the JPA provider that it's a
    43   * valid entity, and a public constructor that takes a single {@link LogEvent event} and passes it to the parent class
    44   * with {@link #AbstractLogEventWrapperEntity(LogEvent) super(event)}. Furthermore, the concrete class must be annotated
    45   * {@link javax.persistence.Entity @Entity} and {@link javax.persistence.Table @Table} and must implement a fully
    46   * mutable ID property annotated with {@link javax.persistence.Id @Id} and
    47   * {@link javax.persistence.GeneratedValue @GeneratedValue} to tell the JPA provider how to calculate an ID for new
    48   * events.
    49   * </p>
    50   * <p>
    51   * Many of the return types of {@link LogEvent} methods (e.g., {@link StackTraceElement}, {@link Message},
    52   * {@link Marker}, {@link Throwable},
    53   * {@link org.apache.logging.log4j.ThreadContext.ContextStack ThreadContext.ContextStack}, and
    54   * {@link Map Map&lt;String, String&gt;}) will not be recognized by the JPA provider. In conjunction with
    55   * {@link javax.persistence.Convert @Convert}, you can use the converters in the
    56   * {@link org.apache.logging.log4j.core.appender.db.jpa.converter} package to convert these types to database columns.
    57   * If you want to retrieve log events from the database, you can create a true POJO entity and also use these
    58   * converters for extracting persisted values.<br>
    59   * </p>
    60   * <p>
    61   * The mutator methods in this class not specified in {@link LogEvent} are no-op methods, implemented to satisfy the JPA
    62   * requirement that accessor methods have matching mutator methods. If you create additional accessor methods, you must
    63   * likewise create matching no-op mutator methods.
    64   * </p>
    65   *
    66   * @see BasicLogEventEntity
    67   */
    68  @MappedSuperclass
    69  @Inheritance(strategy = InheritanceType.SINGLE_TABLE)
    70  public abstract class AbstractLogEventWrapperEntity implements LogEvent {
    71      private static final long serialVersionUID = 1L;
    72
    73      private final LogEvent wrappedEvent;
    74
    75      /**
    76       * Instantiates this base class. All concrete implementations must have a constructor matching this constructor's
    77       * signature. The no-argument constructor is required for a standards-compliant JPA provider to accept this as an
    78       * entity.
    79       */
    80      @SuppressWarnings("unused")
    81      protected AbstractLogEventWrapperEntity() {
    82          this(new NullLogEvent());
    83      }
    84
    85      /**
    86       * Instantiates this base class. All concrete implementations must have a constructor matching this constructor's
    87       * signature. This constructor is used for wrapping this entity around a logged event.
    88       *
    89       * @param wrappedEvent The underlying event from which information is obtained.
    90       */
    91      protected AbstractLogEventWrapperEntity(final LogEvent wrappedEvent) {
    92          if (wrappedEvent == null) {
    93              throw new IllegalArgumentException("The wrapped event cannot be null.");
    94          }
    95          this.wrappedEvent = wrappedEvent;
    96      }
    97
    98      /**
    99       * 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 }