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

org.openide.filesystems.Repository Maven / Gradle / Ivy

There is a newer version: RELEASE240
Show newest version
/*
 * 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.openide.filesystems;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyVetoException;
import java.beans.VetoableChangeListener;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.net.URL;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.jar.Manifest;
import java.util.logging.Level;
import static org.openide.filesystems.FileSystem.LOG;
import org.openide.util.Exceptions;
import org.openide.util.Lookup;
import org.openide.util.LookupEvent;
import org.openide.util.LookupListener;
import org.openide.util.NbCollections;
import org.openide.util.io.NbMarshalledObject;
import org.openide.util.lookup.ServiceProvider;
import org.openide.util.lookup.ServiceProviders;

/**
 * Holder for NetBeans default (system, configuration) filesystem, used for most
 * of NetBeans' runtime configuration. The default implementation is a merge
 * of all of the XML layer files provided by enabled modules, and
 * the {@code config} subfolder of the userdir on the user's disk.
 * If you just want to modify configuration data use e.g.
 * 
 * FileObject menus = FileUtil.getConfigFile("Menu");
 * // ...
 * 
* Formerly (NB 3.x) contained a list of mounted filesystems. This functionality * is no longer used and is now deprecated. */ public class Repository implements Serializable { /** @GuardedBy(Repository.class) */ private static Repository repository; /** Contributes to content of {@link #getDefaultFileSystem() system file system} * (which influences structure under {@link FileUtil#getConfigRoot()}). The * method {@link #registerLayers(java.util.Collection)} * is called during initialization of {@link Repository} and * implementors (registered via {@link ServiceProvider} annotation) may * add their layers (later processed via {@link XMLFileSystem}) into * the general collection of existing providers. *

* The list of layers as well as their content may be cached. * In a typical NetBeans Platform application, the cache remains until * list of modules * and their enabled state remain the same. While it does, the {@link LayerProvider}s * are not queried again. *

*

You can show a dialog letting the user log in to some server, you can call * {@code LoginProvider.injectLayer(...)} with the URL to an XML layer. * The contents of the layer will become available only after login. *

     * {@code @}{@link ServiceProviders}({
     *     {@code @}{@link ServiceProvider}(service=LoginProvider.class),
     *     {@code @}{@link ServiceProvider}(service=LayerProvider.class)
     * })
     * public final class LoginProvider extends LayerProvider {
     *     private URL layer;
     * 
     *     public void registerLayers(Collection{@code } arr) {
     *         if (layer != null) {
     *             arr.add(layer);
     *         }
     *     }
     * 
     *     public static void injectLayer(URL u) {
     *         LoginProvider lp = {@link Lookup}.getDefault().lookup(LoginProvider.class);
     *         lp.url = u;
     *         lp.refresh();
     *     }
     * }
     * 
* The additional layers are inserted below traditional layers provided * by modules in so called {@link FileSystem fallback mode} - e.g. modules * can override and redefine layers contributed by layer providers. * * @since 7.59 */ public static abstract class LayerProvider { /** Allows providers to add their additions to the structure * beneath {@link FileUtil#getConfigRoot()}. The method is * supposed to collect all additional layers and {@link Collection#add(java.lang.Object) add} * them into the context collection. The provided * layers will be processed by {@link XMLFileSystem}-like manner later. * * @param context the context where to register the additions */ protected abstract void registerLayers(Collection context); /** Method to call when set of URLs returned from the {@link #layers()} * method changed and there is a need to refresh it. Refresh is very likely * a time consuming task - consider invoking it on a background thread and * don't hold any locks while calling the method */ protected final void refresh() { Repository.getDefault().refreshAdditionalLayers(); } } /** Methods that tells {@link Repository} subclasses to refresh list of * URLs provided by {@link LayerProvider}s. * @since 7.59 */ protected void refreshAdditionalLayers() { if (getDefaultFileSystem() instanceof MainFS) { ((MainFS)getDefaultFileSystem()).refreshLayers(); } } /** Allows subclasses registered as {@link Repository#getDefault()} to * find out list of URLs for a given provider. The method just calls * {@link LayerProvider#registerLayers(java.util.Collection)}. * * @param p the provider. * @return ordered list of URLs * @since 7.59 */ protected final List findLayers(LayerProvider p) { if (this != Repository.getDefault()) { return Collections.emptyList(); } List urls = new ArrayList(); p.registerLayers(urls); return urls; } /** * Allows subclasses to accept layer contributions from {@link Repository.LayerProvider}s. * Layer XMLs will be collected from locations specified in manifests accessible by the * passed ClassLoader, including generated layers. * Finally {@link LayerProvider}s will be consulted to append their * URLs to the list. If the passed classloader is {@code null}, the classloader which * loaded the Repository class will be used. * @param layerUrls out: collection which receives the URLs. * @throws IOException propagated if some I/O error occurs. * * @since 9.5 */ protected static final void provideLayers(ClassLoader l, List layerUrls) throws IOException { if (l == null) { l = Repository.class.getClassLoader(); } for (URL manifest : NbCollections.iterable(l.getResources("META-INF/MANIFEST.MF"))) { // NOI18N InputStream is = manifest.openStream(); try { Manifest mani = new Manifest(is); String layerLoc = mani.getMainAttributes().getValue("OpenIDE-Module-Layer"); // NOI18N if (layerLoc != null) { URL layer = l.getResource(layerLoc); if (layer != null) { layerUrls.add(layer); } else { LOG.warning("No such layer: " + layerLoc); } } } finally { is.close(); } } for (URL generatedLayer : NbCollections.iterable(l.getResources("META-INF/generated-layer.xml"))) { // NOI18N layerUrls.add(generatedLayer); } for (LayerProvider p : Lookup.getDefault().lookupAll(LayerProvider.class)) { List newURLs = new ArrayList(); p.registerLayers(newURLs); layerUrls.addAll(newURLs); } } private static final class MainFS extends MultiFileSystem implements LookupListener { private static final Lookup.Result ALL = Lookup.getDefault().lookupResult(FileSystem.class); private static final FileSystem MEMORY = FileUtil.createMemoryFileSystem(); private static final XMLFileSystem layers = new XMLFileSystem(); public MainFS() { ALL.addLookupListener(this); refreshLayers(); } final void refreshLayers() { List layerUrls = new ArrayList(); try { provideLayers(Thread.currentThread().getContextClassLoader(), layerUrls); layers.setXmlUrls(layerUrls.toArray(new URL[layerUrls.size()])); LOG.log(Level.FINE, "Loading classpath layers: {0}", layerUrls); } catch (Exception x) { LOG.log(Level.WARNING, "Setting layer URLs: " + layerUrls, x); } resultChanged(null); // run after add listener - see PN1 in #26338 } private static FileSystem[] computeDelegates() { List arr = new ArrayList(); arr.add(MEMORY); for (FileSystem f : ALL.allInstances()) { if (Boolean.TRUE.equals(f.getRoot().getAttribute("fallback"))) { // NOI18N continue; } arr.add(f); } arr.add(layers); for (FileSystem f : ALL.allInstances()) { if (Boolean.TRUE.equals(f.getRoot().getAttribute("fallback"))) { // NOI18N arr.add(f); } } return arr.toArray(new FileSystem[0]); } public void resultChanged(LookupEvent ev) { synchronized (Repository.class) { setDelegates(computeDelegates()); } } } // end of MainFS static final long serialVersionUID = -6344768369160069704L; /** list of filesystems (FileSystem) */ private ArrayList fileSystems; private transient List fileSystemsClone = Collections.emptyList(); /** the system filesystem */ private FileSystem system; /** hashtable that maps system names to FileSystems */ private Hashtable names; private transient FCLSupport fclSupport; // [PENDING] access to this hashtable is apparently not propertly synched // should use e.g. Collections.synchronizedSet, or just synch methods using it /** hashtable for listeners on changes in the filesystem. * Its elements are of type (RepositoryListener, RepositoryListener) */ private Hashtable listeners = new Hashtable(); /** vetoable listener on systemName property of filesystem */ private VetoableChangeListener vetoListener = new VetoableChangeListener() { /** @param ev event with changes */ public void vetoableChange(PropertyChangeEvent ev) throws PropertyVetoException { if (ev.getPropertyName().equals("systemName")) { final String ov = (String) ev.getOldValue(); final String nv = (String) ev.getNewValue(); if (names.get(nv) != null) { throw new PropertyVetoException("system name already exists: " + ov + " -> " + nv, ev); // NOI18N } } } }; /** property listener on systemName property of filesystem */ private PropertyChangeListener propListener = new PropertyChangeListener() { /** @param ev event with changes */ public void propertyChange(PropertyChangeEvent ev) { if (ev.getPropertyName().equals("systemName")) { // assign the property to new name String ov = (String) ev.getOldValue(); String nv = (String) ev.getNewValue(); FileSystem fs = (FileSystem) ev.getSource(); if (fs.isValid()) { // when a filesystem is valid then it is attached to a name names.remove(ov); } // register name of the filesystem names.put(nv, fs); // the filesystem becomes valid fs.setValid(true); } } }; /** Creates new instance of filesystem pool and * registers it as the default one. Also registers the default filesystem. * * @param def the default filesystem */ public Repository(FileSystem def) { this.system = def; init(); } /** Initialazes the pool. */ private void init() { // empties the pool fileSystems = new ArrayList(); names = new Hashtable(); if (addFileSystemDelayed(system)) { addFileSystem(system); } } /** Access method to get default instance of repository in the system. * The instance is either taken as a result of * org.openide.util.Lookup.getDefault ().lookup (Repository.class) * or (if the lookup query returns null) a default instance is created. *

* In a contextual environment, the method remembers the result of the default Lookup * query, and will return the same instance as a system-wide Repository instance. * Instances provided by Lookup.getDefault().lookup(Repository.class) may vary * depending on the Lookup's implementation and context - be aware that multiple Repository * instances may exist, possibly one for each contextual Lookup craeted. *

* * @return default repository for the system * @since 9.5 support for multiple contexts */ public static Repository getDefault() { final Lookup lkp = Lookup.getDefault(); synchronized (Repository.class) { if (repository != null) { return repository; } } try { Repository newRepo = delayFileSystemAttachImpl(new Callable() { public Repository call() throws Exception { Repository r = lkp.lookup(Repository.class); if (r == null) { // if not provided use default one r = new Repository(new MainFS()); } return r; } }); synchronized (Repository.class) { if (repository == null) { repository = newRepo; } return repository; } } catch (IOException ex) { throw new IllegalStateException(ex); } } /** * Last known Lookup instance, for which the {@link #lastLocalProvider} holds a value. */ private static Reference lastDefLookup = new WeakReference<>(null); /** * LocalProvider associated with the {@link #lastDefLookup}. * The Provider is cached rather than the Repository instance, to avoid * several locations where Repository is cached - testsuites may recycle instances * and would have to be updated if another cache is created. */ private static LocalProvider lastLocalProvider = null; /** * Provides a safe initialization for repository in a context Lookup. * Because of delayed FS initialization guarded by {@link #ADD_FS}, it's not safe * to directly lookup Repository.class in the default Lookup; if an instance does not * exist yet and will be created within the lookup() call, it would obscure possible * existing ADD_FS value. In fact, the Repository instance is usually instantiated twice, * because ctor initialization indirectly invokes Repository.getDefault(), resulting in another * initialization. *

* Performance note: during startup, the default Lookup changes frequently because of modules * activations. The lkp.lookup(LocalProvider.class) query blocks on Lookup computation * locks, adding up to 20% to startup time. The last discovered value of LocalProvider is therefore cached * ({@link #lastLocalProvider}) and the default Lookup in effect is remembered in {@link #lastDefLookup}. * If {@code getLocalRepository} is called again with the default Lookup instance unchanged (= the same execution context), * no {@code lookup()} query will be executed and the cached {@code LocalProvider} instance will be * used to return the repository instance. *

* In multi-user/execution environment, this cache is beneficial during system startup; after that, the * default Lookup changes with the execution context frequently, but contents of individual Lookups are rather * stable, so performance should be acceptable. * * @return Repository instance */ /* package private */ static Repository getLocalRepository() { Lookup lkp = Lookup.getDefault(); Reference c; LocalProvider p = null; LocalProvider q; synchronized (Repository.class) { c = lastDefLookup; Lookup l = c.get(); if (lkp == l) { p = lastLocalProvider; } q = p; } if (p == null) { q = lkp.lookup(LocalProvider.class); if (q == null) { q = NO_PROVIDER; } } Repository inst = null; try { inst = q.getRepository(); } catch (IOException ex) { Exceptions.printStackTrace(ex); return null; } if (p == null) { synchronized (Repository.class) { if (lastDefLookup == c) { lastLocalProvider = q; lastDefLookup = new WeakReference(lkp); } } } return inst; } private static final LocalProvider NO_PROVIDER = new LocalProvider() { @Override public Repository getRepository() throws IOException { return getDefault(); } }; static synchronized void reset() { repository = null; lastLocalProvider = null; lastDefLookup = new WeakReference(null); } private static final ThreadLocal ADD_FS = new ThreadLocal(); private static boolean addFileSystemDelayed(FileSystem fs) { FileSystem[] store = ADD_FS.get(); if (store != null) { assert store[0] == null; store[0] = fs; return false; } else { return true; } } private boolean addFileSystemImpl(FileSystem fs) { Thread.holdsLock(this); // if the filesystem is not assigned yet if (!fs.assigned && !fileSystems.contains(fs)) { // new filesystem fs.setRepository(this); fileSystems.add(fs); fileSystemsClone = new ArrayList(fileSystems); String systemName = fs.getSystemName(); boolean isReg = names.get(systemName) == null; if (isReg && !systemName.equals("")) { // NOI18N // filesystem with the same name is not there => then it is valid names.put(systemName, fs); fs.setValid(true); } else { // there is another filesystem with the same name => it is invalid fs.setValid(false); } // mark the filesystem as being assigned fs.assigned = true; // mark as a listener on changes in the filesystem fs.addPropertyChangeListener(propListener); fs.addVetoableChangeListener(vetoListener); // notify filesystem itself that it has been added fs.addNotify(); // fire info about new filesystem return true; } else { return false; } } /** * Gets the NetBeans default (system, configuration) filesystem. * @return the default filesystem * @deprecated Please use {@link FileUtil#getConfigFile(String)} or * {@link FileUtil#getConfigRoot()} instead. */ @Deprecated public final FileSystem getDefaultFileSystem() { return system; } /** Adds new filesystem to the pool. * Note that a filesystem cannot be assigned to more than one file * system pool at one time (though currently there is only one pool anyway). * At any given time, no two filesystems in the pool may share the same {@link FileSystem#getSystemName name} * (unless all but one are {@link FileSystem#isValid invalid}). To be sure, that * filesystem was really added in Repository, then test that FileSystem * is valid. * @param fs filesystem to add * @deprecated Please use the ClassPath API instead. */ @Deprecated public final void addFileSystem(FileSystem fs) { boolean fireIt; synchronized (this) { fireIt = addFileSystemImpl(fs); } // postponed firing after synchronized block to prevent deadlock if (fireIt) { fireFileSystem(fs, true); } } /** Removes a filesystem from the pool. * @param fs filesystem to remove * @deprecated Please use the ClassPath API instead. */ @Deprecated public final void removeFileSystem(FileSystem fs) { boolean fireIt = false; synchronized (this) { if (fs.isDefault()) { return; } if (fireIt = fileSystems.remove(fs)) { fs.setRepository(null); fileSystemsClone = new ArrayList(fileSystems); // the filesystem realy was here if (fs.isValid()) { // if the filesystem is valid then is in names hashtable names.remove(fs.getSystemName()); fs.setValid(false); } // in all cases remove it from listeners fs.removePropertyChangeListener(propListener); fs.removeVetoableChangeListener(vetoListener); // notify filesystem itself that it has been removed fs.removeNotify(); } // unassign the filesystem fs.assigned = false; } // postponed firing after synchronized block to prevent deadlock if (fireIt) { fireFileSystem(fs, false); } } /** Reorders {@link FileSystem}s by given permutation. * For example, if there are three filesystems, new int[] {2, 0, 1} cycles the filesystems forwards. * @param perm an array of integers * @throws IllegalArgumentException if the array is not a permutation, or is not the same length as the current number of filesystems in the pool * @deprecated Please use the ClassPath API instead. */ @Deprecated public final void reorder(int[] perm) { synchronized (this) { if (perm == null) { throw new IllegalArgumentException("null permutation"); // NOI18N } else if (perm.length != fileSystems.size()) { throw new IllegalArgumentException( "permutation is wrong size: " + perm.length + " elements but should be " + fileSystems.size() ); // NOI18N } else if (!isPermutation(perm)) { StringBuffer message = new StringBuffer("permutation is not really a permutation:"); // NOI18N for (int i = 0; i < perm.length; i++) { message.append(' '); message.append(perm[i]); } throw new IllegalArgumentException(message.toString()); } ArrayList newList = new ArrayList(fileSystems.size()); int len = perm.length; for (int i = 0; i < len; i++) { newList.add(fileSystems.get(perm[i])); } fileSystems = newList; fileSystemsClone = new ArrayList(fileSystems); } fireFileSystemReordered(perm); } /** @return true if the parameter describes a permutation */ private static boolean isPermutation(int[] perm) { final int len = perm.length; boolean[] bool = new boolean[len]; try { for (int i = 0; i < len; i++) { if (bool[perm[i]]) { return false; } else { bool[perm[i]] = true; } } return true; } catch (IndexOutOfBoundsException e) { return false; } } /** Returns enumeration of all filesystems. * @return enumeration of type {@link FileSystem} * @deprecated Please use the ClassPath API instead. */ @Deprecated public final Enumeration getFileSystems() { return Collections.enumeration(fileSystemsClone); } /** Returns enumeration of all filesystems. * @return enumeration of type {@link FileSystem} * @deprecated Please use the ClassPath API instead. */ @Deprecated public final Enumeration fileSystems() { return getFileSystems(); } /** * Returns a sorted array of filesystems. * @return a sorted array of filesystems * @deprecated Please use the ClassPath API instead. */ @Deprecated public final FileSystem[] toArray() { List tempFileSystems = fileSystemsClone; FileSystem[] fss = new FileSystem[tempFileSystems.size()]; tempFileSystems.toArray(fss); return fss; } /** Finds filesystem when only its system name is known. * @param systemName {@link FileSystem#getSystemName name} of the filesystem * @return the filesystem or null if there is no such * filesystem * @deprecated Please use the ClassPath API instead. */ @Deprecated public final FileSystem findFileSystem(String systemName) { FileSystem fs = names.get(systemName); return fs; } /** Saves pool to stream by saving all filesystems. * The default (system) filesystem, or any persistent filesystems, are skipped. * * @param oos object output stream * @exception IOException if an error occures * @deprecated Unused. */ @Deprecated public final synchronized void writeExternal(ObjectOutput oos) throws IOException { Iterator iter = fileSystems.iterator(); while (iter.hasNext()) { FileSystem fs = (FileSystem) iter.next(); if (!fs.isDefault()) { oos.writeObject(new NbMarshalledObject(fs)); } } oos.writeObject(null); } /** Reads object from stream. * Reads all filesystems. Persistent and system filesystems are untouched; all others are removed and possibly reread. * @param ois object input stream * @exception IOException if an error occures * @exception ClassNotFoundException if read class is not found * @deprecated Unused. */ @Deprecated public final synchronized void readExternal(ObjectInput ois) throws IOException, ClassNotFoundException { ArrayList temp = new ArrayList(10); for (;;) { Object obj = ois.readObject(); if (obj == null) { // all system has been read in break; } FileSystem fs; if (obj instanceof FileSystem) { fs = (FileSystem) obj; } else { try { NbMarshalledObject mar = (NbMarshalledObject) obj; fs = (FileSystem) mar.get(); } catch (IOException ex) { ExternalUtil.exception(ex); fs = null; } catch (ClassNotFoundException ex) { ExternalUtil.exception(ex); fs = null; } } if (fs != null) { // add the new filesystem temp.add(fs); } } Enumeration ee = getFileSystems(); FileSystem fs; while (ee.hasMoreElements()) { fs = ee.nextElement(); if (!fs.isDefault()) { removeFileSystem(fs); } } // in init assigned is checked and we force 'system' to be added again system.assigned = false; init(); // all is successfuly read for (Iterator iter = temp.iterator(); iter.hasNext();) addFileSystem((FileSystem) iter.next()); } /** Finds file when its name is provided. It scans in the list of * filesystems and asks them for the specified file by a call to * {@link FileSystem#find find}. The first object that is found is returned or null * if none of the filesystems contain such a file. * * @param aPackage package name where each package is separated by a dot * @param name name of the file (without dots) or null if * one wants to obtain the name of a package and not a file in it * @param ext extension of the file or null if one needs * a package and not a file name * * @return {@link FileObject} that represents file with given name or * null if the file does not exist * @deprecated Please use the ClassPath API instead. */ @Deprecated public final FileObject find(String aPackage, String name, String ext) { assert false : "Deprecated."; Enumeration en = getFileSystems(); while (en.hasMoreElements()) { FileSystem fs = en.nextElement(); FileObject fo = fs.find(aPackage, name, ext); if (fo != null) { // object found return fo; } } return null; } /** Searches for the given resource among all filesystems. * @see FileSystem#findResource * @param name a name of the resource * @return file object or null if the resource can not be found * @deprecated Please use the ClassPath API instead. */ @Deprecated public final FileObject findResource(String name) { assert false : "Deprecated."; Enumeration en = getFileSystems(); while (en.hasMoreElements()) { FileSystem fs = en.nextElement(); FileObject fo = fs.findResource(name); if (fo != null) { // object found return fo; } } return null; } /** Searches for the given resource among all filesystems, returning all matches. * @param name name of the resource * @return enumeration of {@link FileObject}s * @deprecated Please use the ClassPath API instead. */ @Deprecated public final Enumeration findAllResources(String name) { assert false : "Deprecated."; Vector v = new Vector(8); Enumeration en = getFileSystems(); while (en.hasMoreElements()) { FileSystem fs = en.nextElement(); FileObject fo = fs.findResource(name); if (fo != null) { v.addElement(fo); } } return v.elements(); } /** Finds all files among all filesystems matching a given name, returning all matches. * All filesystems are queried with {@link FileSystem#find}. * * @param aPackage package name where each package is separated by a dot * @param name name of the file (without dots) or null if * one wants to obtain the name of a package and not a file in it * @param ext extension of the file or null if one needs * a package and not a file name * * @return enumeration of {@link FileObject}s * @deprecated Please use the ClassPath API instead. */ @Deprecated public final Enumeration findAll(String aPackage, String name, String ext) { assert false : "Deprecated."; Enumeration en = getFileSystems(); Vector ret = new Vector(); while (en.hasMoreElements()) { FileSystem fs = (FileSystem) en.nextElement(); FileObject fo = fs.find(aPackage, name, ext); if (fo != null) { ret.addElement(fo); } } return ret.elements(); } /** Fire info about changes in the filesystem pool. * @param fs filesystem * @param add true if the filesystem is added, * false if it is removed */ private void fireFileSystem(FileSystem fs, boolean add) { RepositoryEvent ev = new RepositoryEvent(this, fs, add); for (RepositoryListener list : new HashSet(listeners.values())) { if (add) { list.fileSystemAdded(ev); } else { list.fileSystemRemoved(ev); } } } /** Fires info about reordering * @param perm */ private void fireFileSystemReordered(int[] perm) { RepositoryReorderedEvent ev = new RepositoryReorderedEvent(this, perm); for (RepositoryListener list : new HashSet(listeners.values())) { list.fileSystemPoolReordered(ev); } } /** Adds new listener. * @param list the listener * @deprecated Please use the ClassPath API instead. */ @Deprecated public final void addRepositoryListener(RepositoryListener list) { listeners.put(list, list); } /** Removes listener. * @param list the listener * @deprecated Please use the ClassPath API instead. */ @Deprecated public final void removeRepositoryListener(RepositoryListener list) { listeners.remove(list); } /** Writes the object to the stream. */ private Object writeReplace() { return new Replacer(); } final FCLSupport getFCLSupport() { synchronized (FCLSupport.class) { if (fclSupport == null) { fclSupport = new FCLSupport(); } } return fclSupport; } /** Add new listener to this object. * @param fcl the listener * @since 2.8 * @deprecated useless because there is no filesystem but only * default filesystem in Repository. Add new listener directly to * default filesystem {@link #getDefaultFileSystem}. */ @Deprecated public final void addFileChangeListener(FileChangeListener fcl) { getFCLSupport().addFileChangeListener(fcl); } /** Remove listener from this object. * @param fcl the listener * @since 2.8 * @deprecated useless because there is no filesystem but only * default filesystem in Repository. Add new listener directly to * default filesystem {@link #getDefaultFileSystem}. */ @Deprecated public final void removeFileChangeListener(FileChangeListener fcl) { getFCLSupport().removeFileChangeListener(fcl); } private static class Replacer implements java.io.Serializable { /** serial version UID */ static final long serialVersionUID = -3814531276726840241L; Replacer() { } private void writeObject(ObjectOutputStream oos) throws IOException { getDefault().writeExternal(oos); } private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { getDefault().readExternal(ois); } /** @return the default pool */ public Object readResolve() { return getDefault(); } } private static Repository delayFileSystemAttachImpl(Callable init) throws IOException { FileSystem[] previous = ADD_FS.get(); try { FileSystem[] addLater = new FileSystem[1]; ADD_FS.set(addLater); Repository newRepo = init.call(); if (newRepo == null) { return null; } synchronized (newRepo) { if (addLater[0] instanceof FileSystem) { newRepo.addFileSystemImpl(addLater[0]); } } return newRepo; } catch (IOException ex) { throw ex; } catch (Exception ex) { throw new IOException(ex); } finally { ADD_FS.set(previous); } } /** * Provides local repositories, depending on the current execution environment. * The caller may use {@link Lookup#executeWith} to temporarily modify the default * Lookup contents and route Repository requests to a Repository private to some task. The * {@code LocalProvider} is responsible for caching and lifecycle of the Repository instance; * it can expire the Repository if the Provider's execution context terminates. Implementations * must be prepared to handle requests for Repository already shutdown etc. *

* @since 9.5 */ public static abstract class LocalProvider { /** * Returns the local repository instance. The method can return {@code null} to indicate * the main Repository should be used. The method must return the very same instance * of Repository for the whole duration of the execution context. It must return the same * Repository instance even after the execution context shuts down; the contents of Repository and * its FileSystems may be limited in that case. *

* Implementations must be reentrant (a call to {@link #getLocalRepository} can be made during * construction of the Repository instance) and must not recurse infinitely. * * @return local repository instance. */ public abstract Repository getRepository() throws IOException; /** * Allows to delay attaching filesystems to the Repository. For backwards compatibility, the Repository * constructor still takes the default FileSystem instance. However it is better to actually publish the * FileSystem in the repository only after it is registered, and can be obtained using {@link #getDefault()} or * {@link #getLocalRepository()}. *

* This method wraps the Repository creation so that the default FileSystem is attached after the repository * is fully constructed. The `init' callable must create a new Repository instance. * This method will then add the default FS for newly created repository instances. * * @param init Callable which produces a new Repository instance or {@code null} to abort creation * @return new Repository instance or {@code null}, if no instance was created. * @throws IOException if the Callable fails. All exceptions are wrapped into {@link IOException}. */ protected final Repository delayFilesystemAttach(Callable init) throws IOException { // just a trampoline to the real implementation return delayFileSystemAttachImpl(init); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy