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: */
017: package org.apache.commons.vfs.impl;
018:
019: import org.apache.commons.logging.Log;
020: import org.apache.commons.logging.LogFactory;
021: import org.apache.commons.vfs.FileSystemException;
022: import org.apache.commons.vfs.VfsLog;
023: import org.apache.commons.vfs.operations.FileOperationProvider;
024: import org.apache.commons.vfs.provider.FileProvider;
025: import org.apache.commons.vfs.util.Messages;
026: import org.w3c.dom.Element;
027: import org.w3c.dom.NodeList;
028:
029: import javax.xml.parsers.DocumentBuilder;
030: import javax.xml.parsers.DocumentBuilderFactory;
031: import javax.xml.parsers.ParserConfigurationException;
032: import java.io.File;
033: import java.io.IOException;
034: import java.io.InputStream;
035: import java.net.MalformedURLException;
036: import java.net.URL;
037: import java.util.ArrayList;
038: import java.util.StringTokenizer;
039: import java.util.Enumeration;
040: import java.util.jar.JarEntry;
041: import java.util.jar.JarFile;
042:
043: /**
044: * A {@link org.apache.commons.vfs.FileSystemManager} that configures itself
045: * from an XML (Default: providers.xml) configuration file.<br>
046: * Certain providers are only loaded and available if the dependend library is in your
047: * classpath. You have to configure your debugging facility to log "debug" messages to see
048: * if a provider was skipped due to "unresolved externals".
049: *
050: * @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
051: * @version $Revision: 537717 $ $Date: 2007-05-13 23:00:47 -0700 (Sun, 13 May 2007) $
052: */
053: public class StandardFileSystemManager extends DefaultFileSystemManager {
054: private Log log = LogFactory
055: .getLog(StandardFileSystemManager.class);
056:
057: private static final String CONFIG_RESOURCE = "providers.xml";
058: private static final String PLUGIN_CONFIG_RESOURCE = "META-INF/vfs-providers.xml";
059:
060: private URL configUri;
061: private ClassLoader classLoader;
062:
063: /**
064: * Sets the configuration file for this manager.
065: */
066: public void setConfiguration(final String configUri) {
067: try {
068: setConfiguration(new URL(configUri));
069: } catch (MalformedURLException e) {
070: log.warn(e.getLocalizedMessage(), e);
071: }
072: }
073:
074: /**
075: * Sets the configuration file for this manager.
076: */
077: public void setConfiguration(final URL configUri) {
078: this .configUri = configUri;
079: }
080:
081: /**
082: * Sets the ClassLoader to use to load the providers. Default is to
083: * use the ClassLoader that loaded this class.
084: */
085: public void setClassLoader(final ClassLoader classLoader) {
086: this .classLoader = classLoader;
087: }
088:
089: /**
090: * Initializes this manager. Adds the providers and replicator.
091: */
092: public void init() throws FileSystemException {
093: // Set the replicator and temporary file store (use the same component)
094: final DefaultFileReplicator replicator = createDefaultFileReplicator();
095: setReplicator(new PrivilegedFileReplicator(replicator));
096: setTemporaryFileStore(replicator);
097:
098: /* replaced by findClassLoader
099: if (classLoader == null)
100: {
101: // Use default classloader
102: classLoader = getClass().getClassLoader();
103: }
104: */
105:
106: if (configUri == null) {
107: // Use default config
108: final URL url = getClass().getResource(CONFIG_RESOURCE);
109: if (url == null) {
110: throw new FileSystemException(
111: "vfs.impl/find-config-file.error",
112: CONFIG_RESOURCE);
113: }
114: configUri = url;
115: }
116:
117: // Configure
118: configure(configUri);
119:
120: // Configure Plugins
121: configurePlugins();
122:
123: // Initialise super-class
124: super .init();
125: }
126:
127: /**
128: * Scans the classpath to find any droped plugin.<br />
129: * The plugin-description has to be in /META-INF/vfs-providers.xml
130: */
131: protected void configurePlugins() throws FileSystemException {
132: ClassLoader cl = findClassLoader();
133:
134: Enumeration enumResources = null;
135: try {
136: enumResources = cl.getResources(PLUGIN_CONFIG_RESOURCE);
137: } catch (IOException e) {
138: throw new FileSystemException(e);
139: }
140:
141: while (enumResources.hasMoreElements()) {
142: URL url = (URL) enumResources.nextElement();
143: configure(url);
144: }
145: }
146:
147: private ClassLoader findClassLoader() {
148: if (classLoader != null) {
149: return classLoader;
150: }
151:
152: ClassLoader cl = Thread.currentThread().getContextClassLoader();
153: if (cl == null) {
154: cl = getClass().getClassLoader();
155: }
156:
157: return cl;
158: }
159:
160: protected DefaultFileReplicator createDefaultFileReplicator() {
161: return new DefaultFileReplicator();
162: }
163:
164: /**
165: * Configures this manager from an XML configuration file.
166: */
167: private void configure(final URL configUri)
168: throws FileSystemException {
169: InputStream configStream = null;
170: try {
171: // Load up the config
172: // TODO - validate
173: final DocumentBuilder builder = createDocumentBuilder();
174: configStream = configUri.openStream();
175: final Element config = builder.parse(configStream)
176: .getDocumentElement();
177:
178: configure(config);
179: } catch (final Exception e) {
180: throw new FileSystemException("vfs.impl/load-config.error",
181: configUri.toString(), e);
182: } finally {
183: if (configStream != null) {
184: try {
185: configStream.close();
186: } catch (IOException e) {
187: log.warn(e.getLocalizedMessage(), e);
188: }
189: }
190: }
191: }
192:
193: /**
194: * Configures this manager from an XML configuration file.
195: */
196: private void configure(final String configUri,
197: final InputStream configStream) throws FileSystemException {
198: try {
199: // Load up the config
200: // TODO - validate
201: final DocumentBuilder builder = createDocumentBuilder();
202: final Element config = builder.parse(configStream)
203: .getDocumentElement();
204:
205: configure(config);
206:
207: } catch (final Exception e) {
208: throw new FileSystemException("vfs.impl/load-config.error",
209: configUri, e);
210: }
211: }
212:
213: /**
214: * Configure and create a DocumentBuilder
215: */
216: private DocumentBuilder createDocumentBuilder()
217: throws ParserConfigurationException {
218: final DocumentBuilderFactory factory = DocumentBuilderFactory
219: .newInstance();
220: factory.setIgnoringElementContentWhitespace(true);
221: factory.setIgnoringComments(true);
222: factory.setExpandEntityReferences(true);
223: final DocumentBuilder builder = factory.newDocumentBuilder();
224: return builder;
225: }
226:
227: /**
228: * Configures this manager from an parsed XML configuration file
229: */
230: private void configure(final Element config)
231: throws FileSystemException {
232: // Add the providers
233: final NodeList providers = config
234: .getElementsByTagName("provider");
235: final int count = providers.getLength();
236: for (int i = 0; i < count; i++) {
237: final Element provider = (Element) providers.item(i);
238: addProvider(provider, false);
239: }
240:
241: // Add the operation providers
242: final NodeList operationProviders = config
243: .getElementsByTagName("operationProvider");
244: for (int i = 0; i < operationProviders.getLength(); i++) {
245: final Element operationProvider = (Element) operationProviders
246: .item(i);
247: addOperationProvider(operationProvider);
248: }
249:
250: // Add the default provider
251: final NodeList defProviders = config
252: .getElementsByTagName("default-provider");
253: if (defProviders.getLength() > 0) {
254: final Element provider = (Element) defProviders.item(0);
255: addProvider(provider, true);
256: }
257:
258: // Add the mime-type maps
259: final NodeList mimeTypes = config
260: .getElementsByTagName("mime-type-map");
261: for (int i = 0; i < mimeTypes.getLength(); i++) {
262: final Element map = (Element) mimeTypes.item(i);
263: addMimeTypeMap(map);
264: }
265:
266: // Add the extension maps
267: final NodeList extensions = config
268: .getElementsByTagName("extension-map");
269: for (int i = 0; i < extensions.getLength(); i++) {
270: final Element map = (Element) extensions.item(i);
271: addExtensionMap(map);
272: }
273: }
274:
275: /**
276: * Adds an extension map.
277: */
278: private void addExtensionMap(final Element map) {
279: final String extension = map.getAttribute("extension");
280: final String scheme = map.getAttribute("scheme");
281: if (scheme != null && scheme.length() > 0) {
282: addExtensionMap(extension, scheme);
283: }
284: }
285:
286: /**
287: * Adds a mime-type map.
288: */
289: private void addMimeTypeMap(final Element map) {
290: final String mimeType = map.getAttribute("mime-type");
291: final String scheme = map.getAttribute("scheme");
292: addMimeTypeMap(mimeType, scheme);
293: }
294:
295: /**
296: * Adds a provider from a provider definition.
297: */
298: private void addProvider(final Element providerDef,
299: final boolean isDefault) throws FileSystemException {
300: final String classname = providerDef.getAttribute("class-name");
301:
302: // Make sure all required schemes are available
303: final String[] requiredSchemes = getRequiredSchemes(providerDef);
304: for (int i = 0; i < requiredSchemes.length; i++) {
305: final String requiredScheme = requiredSchemes[i];
306: if (!hasProvider(requiredScheme)) {
307: final String msg = Messages.getString(
308: "vfs.impl/skipping-provider-scheme.debug",
309: new String[] { classname, requiredScheme });
310: VfsLog.debug(getLogger(), log, msg);
311: return;
312: }
313: }
314:
315: // Make sure all required classes are in classpath
316: final String[] requiredClasses = getRequiredClasses(providerDef);
317: for (int i = 0; i < requiredClasses.length; i++) {
318: final String requiredClass = requiredClasses[i];
319: if (!findClass(requiredClass)) {
320: final String msg = Messages.getString(
321: "vfs.impl/skipping-provider.debug",
322: new String[] { classname, requiredClass });
323: VfsLog.debug(getLogger(), log, msg);
324: return;
325: }
326: }
327:
328: // Create and register the provider
329: final FileProvider provider = (FileProvider) createInstance(classname);
330: final String[] schemas = getSchemas(providerDef);
331: if (schemas.length > 0) {
332: addProvider(schemas, provider);
333: }
334:
335: // Set as default, if required
336: if (isDefault) {
337: setDefaultProvider(provider);
338: }
339: }
340:
341: /**
342: * Adds a operationProvider from a operationProvider definition.
343: */
344: private void addOperationProvider(final Element providerDef)
345: throws FileSystemException {
346: final String classname = providerDef.getAttribute("class-name");
347:
348: // Attach only to available schemas
349: final String[] schemas = getSchemas(providerDef);
350: for (int i = 0; i < schemas.length; i++) {
351: final String schema = schemas[i];
352: if (hasProvider(schema)) {
353: final FileOperationProvider operationProvider = (FileOperationProvider) createInstance(classname);
354: addOperationProvider(schema, operationProvider);
355: }
356: }
357: }
358:
359: /**
360: * Tests if a class is available.
361: */
362: private boolean findClass(final String className) {
363: try {
364: findClassLoader().loadClass(className);
365: return true;
366: } catch (final ClassNotFoundException e) {
367: return false;
368: }
369: }
370:
371: /**
372: * Extracts the required classes from a provider definition.
373: */
374: private String[] getRequiredClasses(final Element providerDef) {
375: final ArrayList classes = new ArrayList();
376: final NodeList deps = providerDef
377: .getElementsByTagName("if-available");
378: final int count = deps.getLength();
379: for (int i = 0; i < count; i++) {
380: final Element dep = (Element) deps.item(i);
381: String className = dep.getAttribute("class-name");
382: if (className != null && className.length() > 0) {
383: classes.add(className);
384: }
385: }
386: return (String[]) classes.toArray(new String[classes.size()]);
387: }
388:
389: /**
390: * Extracts the required schemes from a provider definition.
391: */
392: private String[] getRequiredSchemes(final Element providerDef) {
393: final ArrayList schemes = new ArrayList();
394: final NodeList deps = providerDef
395: .getElementsByTagName("if-available");
396: final int count = deps.getLength();
397: for (int i = 0; i < count; i++) {
398: final Element dep = (Element) deps.item(i);
399: String scheme = dep.getAttribute("scheme");
400: if (scheme != null && scheme.length() > 0) {
401: schemes.add(scheme);
402: }
403: }
404: return (String[]) schemes.toArray(new String[schemes.size()]);
405: }
406:
407: /**
408: * Extracts the schema names from a provider definition.
409: */
410: private String[] getSchemas(final Element provider) {
411: final ArrayList schemas = new ArrayList();
412: final NodeList schemaElements = provider
413: .getElementsByTagName("scheme");
414: final int count = schemaElements.getLength();
415: for (int i = 0; i < count; i++) {
416: final Element scheme = (Element) schemaElements.item(i);
417: schemas.add(scheme.getAttribute("name"));
418: }
419: return (String[]) schemas.toArray(new String[schemas.size()]);
420: }
421:
422: /**
423: * Creates a provider.
424: */
425: private Object createInstance(final String className)
426: throws FileSystemException {
427: try {
428: final Class clazz = findClassLoader().loadClass(className);
429: return clazz.newInstance();
430: } catch (final Exception e) {
431: throw new FileSystemException(
432: "vfs.impl/create-provider.error", className, e);
433: }
434: }
435: }
|