All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.schlichtherle.io.GlobalArchiveDriverRegistry Maven / Gradle / Ivy

/*
 * Copyright (C) 2007-2010 Schlichtherle IT Services
 *
 * Licensed 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 de.schlichtherle.io;

import de.schlichtherle.io.util.SuffixSet;
import de.schlichtherle.util.ClassLoaders;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.net.URL;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * A global registry for archive file suffixes and archive drivers which is
 * configured by the set of all configuration files on the class path.
 * This registry does not have a delegate, so it can only be used as the tail
 * in a {@link ArchiveDriverRegistry registry chain}.
 * 

* When this class is instantiated, it enumerate all instances of the relative * path META-INF/services/de.schlichtherle.io.registry.properties * on the class path (this ensures that TrueZIP is compatible with JNLP as used * by Java Web Start and can be safely added to the Extension Class Path). *

* The configuration files are processed in arbitrary order. * However, configuration files which contain the entry * {@code DRIVER=true} have lesser priority and will be overruled by * any other configuration files which do not contain this entry. * This is used by the default configuration file in TrueZIP's JAR: * It contains this entry in order to allow any client application provided * configuration file to overrule it. *

* This class may appear to be a singleton (there's not much point in * having multiple instances of this class, all with the same configuration). * However, it actually isn't a true singleton because it's * {@link Serializable} in order to support serialization of {@link File} * instances. * This implies that a JVM can send an instance of this class to another JVM, * which's own global archive driver registry instance may be configured * completely different by its local configuration files. * This requires that the * {@link de.schlichtherle.io.archive.spi.ArchiveDriver}s used by the global * archive driver registry are serializable, too. *

* Note that it's actually discouraged to serialize {@link File} instances. * It's only supported due to the implementation of this feature in its base * class {@code java.io.File}. * Instead of serializing {@link File} instances, a client application * should serialize path names instead, which are plain strings. * * @author Christian Schlichtherle * @version $Id: GlobalArchiveDriverRegistry.java,v 1.7 2010/08/25 17:08:14 christian_schlichtherle Exp $ * @since TrueZIP 6.5 */ final class GlobalArchiveDriverRegistry extends ArchiveDriverRegistry { private static final long serialVersionUID = 1579600190374703884L; private static final String CLASS_NAME = "de.schlichtherle.io.GlobalArchiveDriverRegistry"; private static final Logger logger = Logger.getLogger(CLASS_NAME, CLASS_NAME); private static final String KWD_NULL = "NULL"; // NOI18N private static final String KWD_ALL = "ALL"; // NOI18N private static final String PROP_KEY_DEFAULT_SUFFIXES = "de.schlichtherle.io.default"; private static final String PROP_KEY_REGISTRY = "de.schlichtherle.io.registry"; /** The (pseudo) singleton instance. */ public static final GlobalArchiveDriverRegistry INSTANCE = new GlobalArchiveDriverRegistry(); /** * The canonical list of archive file suffixes in the global registry * which have been configured to be recognized by default. */ public final String defaultSuffixes; /** * The canonical list of all archive file suffixes in the global registry. */ public final String allSuffixes; static { logger.config("banner"); // NOI18N } /** * Creates a new {@code GlobalArchiveDriverRegistry}. * This constructor logs some configuration messages at * {@code Level.CONFIG}. * If an exception occurs during processing of the configuration resource * files or no archive drivers are registered, then one or more warnings * messages are logged at {@code Level.WARNING}, but otherwise the * constructor terminates normally. * This is to ensure that TrueZIP can be used without throwing exceptions * in static initializers just because of a bug in a configuration * resource file. */ private GlobalArchiveDriverRegistry() { registerArchiveDrivers(); // Initialize suffix lists. // Note that retrieval of the default suffix list must be done first // in order to remove the DEFAULT key from the drivers map if present. // The driver lookup would throw an exception on this entry otherwise. defaultSuffixes = defaultSuffixes().toString(); allSuffixes = suffixes().toString(); logConfiguration(); } /** * Returns the ordered list of relative path names for configuration files. * Prior elements take precedence. */ private static String[] getServices() { return System.getProperty(PROP_KEY_REGISTRY, "META-INF/services/de.schlichtherle.io.registry.properties" // since TrueZIP 6.5.2 - NOI18N + File.pathSeparator + "META-INF/services/" + CLASS_NAME + ".properties" // deprecated - NOI18N + File.pathSeparator + "META-INF/services/de.schlichtherle.io.archive.spi.ArchiveDriver.properties") .split("\\" + File.pathSeparator); // deprecated - NOI18N } private void registerArchiveDrivers() { final ArchiveDriverRegistry clientRegistry = new ArchiveDriverRegistry(); final String[] services = getServices(); for (int i = services.length; --i >= 0; ) registerArchiveDrivers(services[i], this, clientRegistry); putAll(clientRegistry); } /** * Enumerates all resource URLs for {@code service} on the class * path and calls * {@link #registerArchiveDrivers(URL, ArchiveDriverRegistry, ArchiveDriverRegistry)} * on each instance. *

* Ensures that configuration files specified by client * applications always override configuration files specified * by driver implementations. */ private static void registerArchiveDrivers( final String service, final ArchiveDriverRegistry driverRegistry, final ArchiveDriverRegistry clientRegistry) { assert service != null; assert driverRegistry != null; assert clientRegistry != null; final Enumeration urls; try { urls = ClassLoaders.getResources(service, GlobalArchiveDriverRegistry.class); } catch (IOException ex) { logger.log(Level.WARNING, "lookup.ex", ex); // NOI18N return; } while (urls.hasMoreElements()) { final URL url = (URL) urls.nextElement(); registerArchiveDrivers(url, driverRegistry, clientRegistry); } } /** * Loads and processes the given {@code url} in order to register * the archive drivers in its config resource file. */ private static void registerArchiveDrivers( final URL url, final ArchiveDriverRegistry driverRegistry, final ArchiveDriverRegistry clientRegistry) { assert url != null; assert driverRegistry != null; assert clientRegistry != null; // Load the configuration map from the properties file. logger.log(Level.CONFIG, "loading", url); // NOI18N final Properties config = new Properties(); try { final InputStream in = url.openStream(); try { config.load(in); registerArchiveDrivers(config, driverRegistry, clientRegistry); } finally { in.close(); } } catch (IOException ex) { logger.log(Level.WARNING, "loading.ex", ex); // NOI18N // Continue normally. } } /** * Processes the given {@code config} in order to register * its archive drivers. * * @throws NullPointerException If any archive driver ID in the * configuration is {@code null}. */ private static void registerArchiveDrivers( final Map config, final ArchiveDriverRegistry driverRegistry, final ArchiveDriverRegistry clientRegistry) { assert config != null; assert driverRegistry != null; assert clientRegistry != null; // Consume and process DRIVER entry. final String driver = (String) config.remove(KWD_DRIVER); final boolean isDriver = Boolean.TRUE.equals(Boolean.valueOf(driver)); // Select registry and register drivers. (isDriver ? driverRegistry : clientRegistry).registerArchiveDrivers( config, false); } /** * Consumes and processes the entry for the keyword {@code DEFAULT} * in the map. * If a suffix is specified for which no driver is registered, then a * warning is logged and the suffix is removed from the return value. * * @return The set of suffixes processed by evaluating the value of the * entry with the key {@code DEFAULT} in the map of drivers. * May be empty, but never {@code null}. */ private SuffixSet defaultSuffixes() { final SuffixSet set; final String defaultSuffixesProperty = System.getProperty(PROP_KEY_DEFAULT_SUFFIXES); if (defaultSuffixesProperty != null) { set = new SuffixSet(defaultSuffixesProperty); } else { set = (SuffixSet) remove(KWD_DEFAULT); if (set == null) return new SuffixSet(); } final SuffixSet all = suffixes(); boolean clear = false; boolean addAll = false; for (final Iterator i = set.originalIterator(); i.hasNext(); ) { final String suffix = (String) i.next(); if (KWD_NULL.equals(suffix)) { i.remove(); clear = true; } else if (KWD_ALL.equals(suffix)) { i.remove(); addAll = true; } else if (!all.contains(suffix)) { i.remove(); logger.log(Level.WARNING, "unknownSuffix", suffix); // NOI18N } } if (clear) set.clear(); else if (addAll) set.addAll(all); return set; } private void logConfiguration() { final Iterator i = entrySet().iterator(); if (i.hasNext()) { do { final Map.Entry entry = (Map.Entry) i.next(); logger.log(Level.CONFIG, "driverRegistered", // NOI18N new Object[] { entry.getKey(), entry.getValue() }); } while (i.hasNext()); logger.log(Level.CONFIG, "allSuffixList", allSuffixes); // NOI18N if (defaultSuffixes.length() > 0) logger.log(Level.CONFIG, "defaultSuffixList", defaultSuffixes); // NOI18N else logger.config("noDefaultSuffixes"); // NOI18N } else { logger.warning("noDriversRegistered"); // NOI18N } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy