1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.vfs.impl;
18
19 import org.apache.commons.logging.Log;
20 import org.apache.commons.logging.LogFactory;
21 import org.apache.commons.vfs.FileSystemException;
22 import org.apache.commons.vfs.VfsLog;
23 import org.apache.commons.vfs.operations.FileOperationProvider;
24 import org.apache.commons.vfs.provider.FileProvider;
25 import org.apache.commons.vfs.util.Messages;
26 import org.w3c.dom.Element;
27 import org.w3c.dom.NodeList;
28
29 import javax.xml.parsers.DocumentBuilder;
30 import javax.xml.parsers.DocumentBuilderFactory;
31 import javax.xml.parsers.ParserConfigurationException;
32 import java.io.File;
33 import java.io.IOException;
34 import java.io.InputStream;
35 import java.net.MalformedURLException;
36 import java.net.URL;
37 import java.util.ArrayList;
38 import java.util.StringTokenizer;
39 import java.util.Enumeration;
40 import java.util.jar.JarEntry;
41 import java.util.jar.JarFile;
42
43 /***
44 * A {@link org.apache.commons.vfs.FileSystemManager} that configures itself
45 * from an XML (Default: providers.xml) configuration file.<br>
46 * Certain providers are only loaded and available if the dependend library is in your
47 * classpath. You have to configure your debugging facility to log "debug" messages to see
48 * if a provider was skipped due to "unresolved externals".
49 *
50 * @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
51 * @version $Revision: 480428 $ $Date: 2006-11-29 07:15:24 +0100 (Mi, 29 Nov 2006) $
52 */
53 public class StandardFileSystemManager
54 extends DefaultFileSystemManager
55 {
56 private Log log = LogFactory.getLog(StandardFileSystemManager.class);
57
58 private static final String CONFIG_RESOURCE = "providers.xml";
59 private static final String PLUGIN_CONFIG_RESOURCE = "META-INF/vfs-providers.xml";
60
61 private URL configUri;
62 private ClassLoader classLoader;
63
64 /***
65 * Sets the configuration file for this manager.
66 */
67 public void setConfiguration(final String configUri)
68 {
69 try
70 {
71 setConfiguration(new URL(configUri));
72 }
73 catch (MalformedURLException e)
74 {
75 log.warn(e.getLocalizedMessage(), e);
76 }
77 }
78
79 /***
80 * Sets the configuration file for this manager.
81 */
82 public void setConfiguration(final URL configUri)
83 {
84 this.configUri = configUri;
85 }
86
87 /***
88 * Sets the ClassLoader to use to load the providers. Default is to
89 * use the ClassLoader that loaded this class.
90 */
91 public void setClassLoader(final ClassLoader classLoader)
92 {
93 this.classLoader = classLoader;
94 }
95
96 /***
97 * Initializes this manager. Adds the providers and replicator.
98 */
99 public void init() throws FileSystemException
100 {
101
102 final DefaultFileReplicator replicator = createDefaultFileReplicator();
103 setReplicator(new PrivilegedFileReplicator(replicator));
104 setTemporaryFileStore(replicator);
105
106 if (classLoader == null)
107 {
108
109 classLoader = getClass().getClassLoader();
110 }
111 if (configUri == null)
112 {
113
114 final URL url = getClass().getResource(CONFIG_RESOURCE);
115 if (url == null)
116 {
117 throw new FileSystemException("vfs.impl/find-config-file.error", CONFIG_RESOURCE);
118 }
119 configUri = url;
120 }
121
122
123 configure(configUri);
124
125
126 configurePlugins();
127
128
129 super.init();
130 }
131
132 /***
133 * Scans the classpath to find any droped plugin.<br />
134 * The plugin-description has to be in /META-INF/vfs-providers.xml
135 */
136 protected void configurePlugins() throws FileSystemException
137 {
138 ClassLoader cl = findClassLoader();
139
140 Enumeration enumResources = null;
141 try
142 {
143 enumResources = cl.getResources(PLUGIN_CONFIG_RESOURCE);
144 }
145 catch (IOException e)
146 {
147 throw new FileSystemException(e);
148 }
149
150 while (enumResources.hasMoreElements())
151 {
152 URL url = (URL) enumResources.nextElement();
153 configure(url);
154 }
155 }
156
157 private ClassLoader findClassLoader()
158 {
159 ClassLoader cl = Thread.currentThread().getContextClassLoader();
160 if (cl == null)
161 {
162 cl = getClass().getClassLoader();
163 }
164
165 return cl;
166 }
167
168 protected DefaultFileReplicator createDefaultFileReplicator()
169 {
170 return new DefaultFileReplicator();
171 }
172
173 /***
174 * Configures this manager from an XML configuration file.
175 */
176 private void configure(final URL configUri) throws FileSystemException
177 {
178 InputStream configStream = null;
179 try
180 {
181
182
183 final DocumentBuilder builder = createDocumentBuilder();
184 configStream = configUri.openStream();
185 final Element config = builder.parse(configStream).getDocumentElement();
186
187 configure(config);
188 }
189 catch (final Exception e)
190 {
191 throw new FileSystemException("vfs.impl/load-config.error", configUri.toString(), e);
192 }
193 finally
194 {
195 if (configStream != null)
196 {
197 try
198 {
199 configStream.close();
200 }
201 catch (IOException e)
202 {
203 log.warn(e.getLocalizedMessage(), e);
204 }
205 }
206 }
207 }
208
209 /***
210 * Configures this manager from an XML configuration file.
211 */
212 private void configure(final String configUri, final InputStream configStream) throws FileSystemException
213 {
214 try
215 {
216
217
218 final DocumentBuilder builder = createDocumentBuilder();
219 final Element config = builder.parse(configStream).getDocumentElement();
220
221 configure(config);
222
223 }
224 catch (final Exception e)
225 {
226 throw new FileSystemException("vfs.impl/load-config.error", configUri, e);
227 }
228 }
229
230 /***
231 * Configure and create a DocumentBuilder
232 */
233 private DocumentBuilder createDocumentBuilder() throws ParserConfigurationException
234 {
235 final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
236 factory.setIgnoringElementContentWhitespace(true);
237 factory.setIgnoringComments(true);
238 factory.setExpandEntityReferences(true);
239 final DocumentBuilder builder = factory.newDocumentBuilder();
240 return builder;
241 }
242
243 /***
244 * Configures this manager from an parsed XML configuration file
245 */
246 private void configure(final Element config) throws FileSystemException
247 {
248
249 final NodeList providers = config.getElementsByTagName("provider");
250 final int count = providers.getLength();
251 for (int i = 0; i < count; i++)
252 {
253 final Element provider = (Element) providers.item(i);
254 addProvider(provider, false);
255 }
256
257
258 final NodeList operationProviders = config.getElementsByTagName("operationProvider");
259 for (int i = 0; i < operationProviders.getLength(); i++)
260 {
261 final Element operationProvider = (Element) operationProviders.item(i);
262 addOperationProvider(operationProvider);
263 }
264
265
266 final NodeList defProviders = config.getElementsByTagName("default-provider");
267 if (defProviders.getLength() > 0)
268 {
269 final Element provider = (Element) defProviders.item(0);
270 addProvider(provider, true);
271 }
272
273
274 final NodeList mimeTypes = config.getElementsByTagName("mime-type-map");
275 for (int i = 0; i < mimeTypes.getLength(); i++)
276 {
277 final Element map = (Element) mimeTypes.item(i);
278 addMimeTypeMap(map);
279 }
280
281
282 final NodeList extensions = config.getElementsByTagName("extension-map");
283 for (int i = 0; i < extensions.getLength(); i++)
284 {
285 final Element map = (Element) extensions.item(i);
286 addExtensionMap(map);
287 }
288 }
289
290 /***
291 * Adds an extension map.
292 */
293 private void addExtensionMap(final Element map)
294 {
295 final String extension = map.getAttribute("extension");
296 final String scheme = map.getAttribute("scheme");
297 if (scheme != null && scheme.length() > 0)
298 {
299 addExtensionMap(extension, scheme);
300 }
301 }
302
303 /***
304 * Adds a mime-type map.
305 */
306 private void addMimeTypeMap(final Element map)
307 {
308 final String mimeType = map.getAttribute("mime-type");
309 final String scheme = map.getAttribute("scheme");
310 addMimeTypeMap(mimeType, scheme);
311 }
312
313 /***
314 * Adds a provider from a provider definition.
315 */
316 private void addProvider(final Element providerDef, final boolean isDefault)
317 throws FileSystemException
318 {
319 final String classname = providerDef.getAttribute("class-name");
320
321
322 final String[] requiredSchemes = getRequiredSchemes(providerDef);
323 for (int i = 0; i < requiredSchemes.length; i++)
324 {
325 final String requiredScheme = requiredSchemes[i];
326 if (!hasProvider(requiredScheme))
327 {
328 final String msg = Messages.getString("vfs.impl/skipping-provider-scheme.debug",
329 new String[]{classname, requiredScheme});
330 VfsLog.debug(getLogger(), log, msg);
331 return;
332 }
333 }
334
335
336 final String[] requiredClasses = getRequiredClasses(providerDef);
337 for (int i = 0; i < requiredClasses.length; i++)
338 {
339 final String requiredClass = requiredClasses[i];
340 if (!findClass(requiredClass))
341 {
342 final String msg = Messages.getString("vfs.impl/skipping-provider.debug",
343 new String[]{classname, requiredClass});
344 VfsLog.debug(getLogger(), log, msg);
345 return;
346 }
347 }
348
349
350 final FileProvider provider = (FileProvider) createInstance(classname);
351 final String[] schemas = getSchemas(providerDef);
352 if (schemas.length > 0)
353 {
354 addProvider(schemas, provider);
355 }
356
357
358 if (isDefault)
359 {
360 setDefaultProvider(provider);
361 }
362 }
363
364 /***
365 * Adds a operationProvider from a operationProvider definition.
366 */
367 private void addOperationProvider(final Element providerDef) throws FileSystemException
368 {
369 final String classname = providerDef.getAttribute("class-name");
370
371
372 final String[] schemas = getSchemas(providerDef);
373 for (int i = 0; i < schemas.length; i++)
374 {
375 final String schema = schemas[i];
376 if (hasProvider(schema))
377 {
378 final FileOperationProvider operationProvider = (FileOperationProvider) createInstance(classname);
379 addOperationProvider(schema, operationProvider);
380 }
381 }
382 }
383
384 /***
385 * Tests if a class is available.
386 */
387 private boolean findClass(final String className)
388 {
389 try
390 {
391 classLoader.loadClass(className);
392 return true;
393 }
394 catch (final ClassNotFoundException e)
395 {
396 return false;
397 }
398 }
399
400 /***
401 * Extracts the required classes from a provider definition.
402 */
403 private String[] getRequiredClasses(final Element providerDef)
404 {
405 final ArrayList classes = new ArrayList();
406 final NodeList deps = providerDef.getElementsByTagName("if-available");
407 final int count = deps.getLength();
408 for (int i = 0; i < count; i++)
409 {
410 final Element dep = (Element) deps.item(i);
411 String className = dep.getAttribute("class-name");
412 if (className != null && className.length() > 0)
413 {
414 classes.add(className);
415 }
416 }
417 return (String[]) classes.toArray(new String[classes.size()]);
418 }
419
420 /***
421 * Extracts the required schemes from a provider definition.
422 */
423 private String[] getRequiredSchemes(final Element providerDef)
424 {
425 final ArrayList schemes = new ArrayList();
426 final NodeList deps = providerDef.getElementsByTagName("if-available");
427 final int count = deps.getLength();
428 for (int i = 0; i < count; i++)
429 {
430 final Element dep = (Element) deps.item(i);
431 String scheme = dep.getAttribute("scheme");
432 if (scheme != null && scheme.length() > 0)
433 {
434 schemes.add(scheme);
435 }
436 }
437 return (String[]) schemes.toArray(new String[schemes.size()]);
438 }
439
440 /***
441 * Extracts the schema names from a provider definition.
442 */
443 private String[] getSchemas(final Element provider)
444 {
445 final ArrayList schemas = new ArrayList();
446 final NodeList schemaElements = provider.getElementsByTagName("scheme");
447 final int count = schemaElements.getLength();
448 for (int i = 0; i < count; i++)
449 {
450 final Element scheme = (Element) schemaElements.item(i);
451 schemas.add(scheme.getAttribute("name"));
452 }
453 return (String[]) schemas.toArray(new String[schemas.size()]);
454 }
455
456 /***
457 * Creates a provider.
458 */
459 private Object createInstance(final String className)
460 throws FileSystemException
461 {
462 try
463 {
464 final Class clazz = classLoader.loadClass(className);
465 return clazz.newInstance();
466 }
467 catch (final Exception e)
468 {
469 throw new FileSystemException("vfs.impl/create-provider.error", className, e);
470 }
471 }
472 }