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 */
017package org.apache.logging.log4j.core.filter;
018
019import java.util.Iterator;
020import java.util.concurrent.TimeUnit;
021
022import org.apache.logging.log4j.core.AbstractLifeCycle;
023import org.apache.logging.log4j.core.Filter;
024import org.apache.logging.log4j.core.LifeCycle2;
025import org.apache.logging.log4j.core.LogEvent;
026import org.apache.logging.log4j.core.config.Property;
027import org.apache.logging.log4j.core.config.plugins.PluginElement;
028
029/**
030 * Enhances a Class by allowing it to contain Filters.
031 */
032public abstract class AbstractFilterable extends AbstractLifeCycle implements Filterable {
033
034    /**
035     * Subclasses can extend this abstract Builder.
036     *
037     * @param <B> The type to build.
038     */
039    public abstract static class Builder<B extends Builder<B>> {
040
041        @PluginElement("Filter")
042        private Filter filter;
043
044        // We are calling this attribute propertyArray because we use the more generic "properties" in several places
045        // with different types: Array, Map and List.
046        @PluginElement("Properties")
047        private Property[] propertyArray;
048
049        @SuppressWarnings("unchecked")
050        public B asBuilder() {
051            return (B) this;
052        }
053
054        public Filter getFilter() {
055            return filter;
056        }
057
058        public Property[] getPropertyArray() {
059            return propertyArray;
060        }
061
062        public B setFilter(final Filter filter) {
063            this.filter = filter;
064            return asBuilder();
065        }
066
067        public B setPropertyArray(final Property[] properties) {
068            this.propertyArray = properties;
069            return asBuilder();
070        }
071
072        /**
073         * Sets the filter.
074         *
075         * @param filter The filter
076         * @return this
077         * @deprecated Use {@link #setFilter(Filter)}.
078         */
079        @Deprecated
080        public B withFilter(final Filter filter) {
081            return setFilter(filter);
082        }
083
084    }
085
086    /**
087     * May be null.
088     */
089    private volatile Filter filter;
090
091    @PluginElement("Properties")
092    private final Property[] propertyArray;
093
094    protected AbstractFilterable() {
095        this(null, Property.EMPTY_ARRAY);
096    }
097
098    protected AbstractFilterable(final Filter filter) {
099        this(filter, Property.EMPTY_ARRAY);
100    }
101
102    /**
103     * @since 2.11.2
104     */
105    protected AbstractFilterable(final Filter filter, final Property[] propertyArray) {
106        this.filter = filter;
107        this.propertyArray = propertyArray == null ? Property.EMPTY_ARRAY : propertyArray;
108    }
109
110    /**
111     * Adds a filter.
112     * @param filter The Filter to add.
113     */
114    @Override
115    public synchronized void addFilter(final Filter filter) {
116        if (filter == null) {
117            return;
118        }
119        if (this.filter == null) {
120            this.filter = filter;
121        } else if (this.filter instanceof CompositeFilter) {
122            this.filter = ((CompositeFilter) this.filter).addFilter(filter);
123        } else {
124            final Filter[] filters = new Filter[] {this.filter, filter};
125            this.filter = CompositeFilter.createFilters(filters);
126        }
127    }
128
129    /**
130     * Returns the Filter.
131     * @return the Filter or null.
132     */
133    @Override
134    public Filter getFilter() {
135        return filter;
136    }
137
138    /**
139     * Determines if a Filter is present.
140     * @return false if no Filter is present.
141     */
142    @Override
143    public boolean hasFilter() {
144        return filter != null;
145    }
146
147    /**
148     * Determine if the LogEvent should be processed or ignored.
149     * @param event The LogEvent.
150     * @return true if the LogEvent should be processed.
151     */
152    @Override
153    public boolean isFiltered(final LogEvent event) {
154        return filter != null && filter.filter(event) == Filter.Result.DENY;
155    }
156
157    /**
158     * Removes a Filter.
159     * @param filter The Filter to remove.
160     */
161    @Override
162    public synchronized void removeFilter(final Filter filter) {
163        if (this.filter == null || filter == null) {
164            return;
165        }
166        if (this.filter == filter || this.filter.equals(filter)) {
167            this.filter = null;
168        } else if (this.filter instanceof CompositeFilter) {
169            CompositeFilter composite = (CompositeFilter) this.filter;
170            composite = composite.removeFilter(filter);
171            if (composite.size() > 1) {
172                this.filter = composite;
173            } else if (composite.size() == 1) {
174                final Iterator<Filter> iter = composite.iterator();
175                this.filter = iter.next();
176            } else {
177                this.filter = null;
178            }
179        }
180    }
181
182    /**
183     * Make the Filter available for use.
184     */
185    @Override
186    public void start() {
187        this.setStarting();
188        if (filter != null) {
189            filter.start();
190        }
191        this.setStarted();
192    }
193
194    /**
195     * Cleanup the Filter.
196     */
197    @Override
198    public boolean stop(final long timeout, final TimeUnit timeUnit) {
199        return stop(timeout, timeUnit, true);
200    }
201
202    /**
203     * Cleanup the Filter.
204     */
205    protected boolean stop(final long timeout, final TimeUnit timeUnit, final boolean changeLifeCycleState) {
206        if (changeLifeCycleState) {
207            this.setStopping();
208        }
209        boolean stopped = true;
210        if (filter != null) {
211            if (filter instanceof LifeCycle2) {
212                stopped = ((LifeCycle2) filter).stop(timeout, timeUnit);
213            } else {
214                filter.stop();
215                stopped = true;
216            }
217        }
218        if (changeLifeCycleState) {
219            this.setStopped();
220        }
221        return stopped;
222    }
223
224    public Property[] getPropertyArray() {
225        return propertyArray;
226    }
227
228}