+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.log4j;
+
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.spi.ErrorHandler;
+import org.apache.log4j.spi.OptionHandler;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.log4j.helpers.OnlyOnceErrorHandler;
+import org.apache.log4j.helpers.LogLog;
+
+
+/**
+ * Abstract superclass of the other appenders in the package.
+ *
+ * This class provides the code for common functionality, such as
+ * support for threshold filtering and support for general filters.
+ *
+ * @since 0.8.1
+ * @author Ceki Gülcü
+ * */
+public abstract class AppenderSkeleton implements Appender, OptionHandler {
+
+ /** The layout variable does not need to be set if the appender
+ implementation has its own layout. */
+ protected Layout layout;
+
+ /** Appenders are named. */
+ protected String name;
+
+ /**
+ There is no level threshold filtering by default. */
+ protected Priority threshold;
+
+ /**
+ It is assumed and enforced that errorHandler is never null.
+ */
+ protected ErrorHandler errorHandler = new OnlyOnceErrorHandler();
+
+ /** The first filter in the filter chain. Set to <code>null</code>
+ initially. */
+ protected Filter headFilter;
+ /** The last filter in the filter chain. */
+ protected Filter tailFilter;
+
+ /**
+ Is this appender closed?
+ */
+ protected boolean closed = false;
+
+ /**
+ * Create new instance.
+ */
+ public AppenderSkeleton() {
+ super();
+ }
+
+ /**
+ * Create new instance.
+ * Provided for compatibility with log4j 1.3.
+ *
+ * @param isActive true if appender is ready for use upon construction.
+ * Not used in log4j 1.2.x.
+ * @since 1.2.15
+ */
+ protected AppenderSkeleton(final boolean isActive) {
+ super();
+ }
+
+
+
+ /**
+ Derived appenders should override this method if option structure
+ requires it. */
+ public
+ void activateOptions() {
+ }
+
+
+ /**
+ Add a filter to end of the filter list.
+
+ @since 0.9.0
+ */
+ public
+ void addFilter(Filter newFilter) {
+ if(headFilter == null) {
+ headFilter = tailFilter = newFilter;
+ } else {
+ tailFilter.setNext(newFilter);
+ tailFilter = newFilter;
+ }
+ }
+
+ /**
+ Subclasses of <code>AppenderSkeleton</code> should implement this
+ method to perform actual logging. See also {@link #doAppend
+ AppenderSkeleton.doAppend} method.
+
+ @since 0.9.0
+ */
+ abstract
+ protected
+ void append(LoggingEvent event);
+
+
+ /**
+ Clear the filters chain.
+
+ @since 0.9.0 */
+ public
+ void clearFilters() {
+ headFilter = tailFilter = null;
+ }
+
+ /**
+ Finalize this appender by calling the derived class'
+ <code>close</code> method.
+
+ @since 0.8.4 */
+ public
+ void finalize() {
+ // An appender might be closed then garbage collected. There is no
+ // point in closing twice.
+ if(this.closed) {
+ return;
+ }
+
+ LogLog.debug("Finalizing appender named ["+name+"].");
+ close();
+ }
+
+
+ /**
+ Return the currently set {@link ErrorHandler} for this
+ Appender.
+
+ @since 0.9.0 */
+ public
+ ErrorHandler getErrorHandler() {
+ return this.errorHandler;
+ }
+
+
+ /**
+ Returns the head Filter.
+
+ @since 1.1
+ */
+ public
+ Filter getFilter() {
+ return headFilter;
+ }
+
+ /**
+ Return the first filter in the filter chain for this
+ Appender. The return value may be <code>null</code> if no is
+ filter is set.
+
+ */
+ public
+ final
+ Filter getFirstFilter() {
+ return headFilter;
+ }
+
+ /**
+ Returns the layout of this appender. The value may be null.
+ */
+ public
+ Layout getLayout() {
+ return layout;
+ }
+
+
+ /**
+ Returns the name of this appender.
+ @return name, may be null.
+ */
+ public
+ final
+ String getName() {
+ return this.name;
+ }
+
+ /**
+ Returns this appenders threshold level. See the {@link
+ #setThreshold} method for the meaning of this option.
+
+ @since 1.1 */
+ public
+ Priority getThreshold() {
+ return threshold;
+ }
+
+
+ /**
+ Check whether the message level is below the appender's
+ threshold. If there is no threshold set, then the return value is
+ always <code>true</code>.
+
+ */
+ public
+ boolean isAsSevereAsThreshold(Priority priority) {
+ return ((threshold == null) || priority.isGreaterOrEqual(threshold));
+ }
+
+
+ /**
+ * This method performs threshold checks and invokes filters before
+ * delegating actual logging to the subclasses specific {@link
+ * AppenderSkeleton#append} method.
+ * */
+ public
+ synchronized
+ void doAppend(LoggingEvent event) {
+ if(closed) {
+ LogLog.error("Attempted to append to closed appender named ["+name+"].");
+ return;
+ }
+
+ if(!isAsSevereAsThreshold(event.getLevel())) {
+ return;
+ }
+
+ Filter f = this.headFilter;
+
+ FILTER_LOOP:
+ while(f != null) {
+ switch(f.decide(event)) {
+ case Filter.DENY: return;
+ case Filter.ACCEPT: break FILTER_LOOP;
+ case Filter.NEUTRAL: f = f.getNext();
+ }
+ }
+
+ this.append(event);
+ }
+
+ /**
+ Set the {@link ErrorHandler} for this Appender.
+ @since 0.9.0
+ */
+ public
+ synchronized
+ void setErrorHandler(ErrorHandler eh) {
+ if(eh == null) {
+ // We do not throw exception here since the cause is probably a
+ // bad config file.
+ LogLog.warn("You have tried to set a null error-handler.");
+ } else {
+ this.errorHandler = eh;
+ }
+ }
+
+ /**
+ Set the layout for this appender. Note that some appenders have
+ their own (fixed) layouts or do not use one. For example, the
+ {@link org.apache.log4j.net.SocketAppender} ignores the layout set
+ here.
+ */
+ public
+ void setLayout(Layout layout) {
+ this.layout = layout;
+ }
+
+
+ /**
+ Set the name of this Appender.
+ */
+ public
+ void setName(String name) {
+ this.name = name;
+ }
+
+
+ /**
+ Set the threshold level. All log events with lower level
+ than the threshold level are ignored by the appender.
+
+ <p>In configuration files this option is specified by setting the
+ value of the <b>Threshold</b> option to a level
+ string, such as "DEBUG", "INFO" and so on.
+
+ @since 0.8.3 */
+ public
+ void setThreshold(Priority threshold) {
+ this.threshold = threshold;
+ }
+}