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.util;
018
019import java.lang.reflect.Constructor;
020import java.lang.reflect.InvocationTargetException;
021import java.util.List;
022import java.util.Map;
023import java.util.Objects;
024
025import org.apache.logging.log4j.Logger;
026import org.apache.logging.log4j.core.config.Configuration;
027import org.apache.logging.log4j.core.config.ConfigurationFileWatcher;
028import org.apache.logging.log4j.core.config.ConfigurationListener;
029import org.apache.logging.log4j.core.config.Reconfigurable;
030import org.apache.logging.log4j.core.config.plugins.util.PluginManager;
031import org.apache.logging.log4j.core.config.plugins.util.PluginType;
032import org.apache.logging.log4j.status.StatusLogger;
033
034/**
035 * Creates Watchers of various types.
036 */
037public class WatcherFactory {
038
039    private static Logger LOGGER = StatusLogger.getLogger();
040    private static PluginManager pluginManager = new PluginManager(Watcher.CATEGORY);
041
042    private static volatile WatcherFactory factory = null;
043
044    private final Map<String, PluginType<?>> plugins;
045
046    private WatcherFactory(List<String> packages) {
047        pluginManager.collectPlugins(packages);
048        plugins = pluginManager.getPlugins();
049    }
050
051    public static WatcherFactory getInstance(List<String> packages) {
052        if (factory == null) {
053            synchronized (pluginManager) {
054                if (factory == null) {
055                    factory = new WatcherFactory(packages);
056                }
057            }
058        }
059        return factory;
060    }
061
062    @SuppressWarnings("unchecked")
063    public Watcher newWatcher(Source source, final Configuration configuration, final Reconfigurable reconfigurable,
064        final List<ConfigurationListener> configurationListeners, long lastModifiedMillis) {
065        if (source.getFile() != null) {
066            return new ConfigurationFileWatcher(configuration, reconfigurable, configurationListeners,
067                lastModifiedMillis);
068        } else {
069            String name = source.getURI().getScheme();
070            PluginType<?> pluginType = plugins.get(name);
071            if (pluginType != null) {
072                return instantiate(name, (Class<? extends Watcher>) pluginType.getPluginClass(), configuration,
073                    reconfigurable, configurationListeners, lastModifiedMillis);
074            }
075            LOGGER.info("No Watcher plugin is available for protocol '{}'", name);
076            return null;
077        }
078    }
079
080    public static <T extends Watcher> T instantiate(String name, final Class<T> clazz,
081        final Configuration configuration, final Reconfigurable reconfigurable,
082        final List<ConfigurationListener> listeners, long lastModifiedMillis) {
083        Objects.requireNonNull(clazz, "No class provided");
084        try {
085            Constructor<T> constructor = clazz
086                .getConstructor(Configuration.class, Reconfigurable.class, List.class, long.class);
087            return constructor.newInstance(configuration, reconfigurable, listeners, lastModifiedMillis);
088        } catch (NoSuchMethodException ex) {
089            throw new IllegalArgumentException("No valid constructor for Watcher plugin " + name, ex);
090        } catch (final LinkageError | InstantiationException e) {
091            // LOG4J2-1051
092            // On platforms like Google App Engine and Android, some JRE classes are not supported: JMX, JNDI, etc.
093            throw new IllegalArgumentException(e);
094        } catch (final IllegalAccessException e) {
095            throw new IllegalStateException(e);
096        } catch (final InvocationTargetException e) {
097            Throwables.rethrow(e.getCause());
098            throw new InternalError("Unreachable");
099        }
100    }
101}