2
* Licensed to the Apache Software Foundation (ASF) under one or more
3
* contributor license agreements. See the NOTICE file distributed with
4
* this work for additional information regarding copyright ownership.
5
* The ASF licenses this file to You under the Apache License, Version 2.0
6
* (the "License"); you may not use this file except in compliance with
7
* the License. You may obtain a copy of the License at
9
* http://www.apache.org/licenses/LICENSE-2.0
11
* Unless required by applicable law or agreed to in writing, software
12
* distributed under the License is distributed on an "AS IS" BASIS,
13
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
* See the License for the specific language governing permissions and
15
* limitations under the License.
17
package org.apache.commons.configuration.resolver;
19
import java.io.IOException;
20
import java.io.InputStream;
21
import java.net.FileNameMap;
23
import java.net.URLConnection;
24
import java.util.Vector;
26
import org.apache.commons.configuration.ConfigurationException;
27
import org.apache.commons.configuration.ConfigurationUtils;
28
import org.apache.commons.configuration.FileSystem;
29
import org.apache.commons.lang.text.StrSubstitutor;
30
import org.apache.commons.logging.Log;
31
import org.apache.commons.logging.LogFactory;
32
import org.apache.xml.resolver.CatalogException;
33
import org.apache.xml.resolver.readers.CatalogReader;
34
import org.xml.sax.EntityResolver;
35
import org.xml.sax.InputSource;
36
import org.xml.sax.SAXException;
39
* Thin wrapper around xml commons CatalogResolver to allow list of catalogs
42
* href="http://commons.apache.org/configuration/team-list.html">Commons
43
* Configuration team</a>
45
* @version $Id: CatalogResolver.java 1301991 2012-03-17 20:18:02Z sebb $
47
public class CatalogResolver implements EntityResolver
52
private static final int DEBUG_ALL = 9;
55
* Normal debug setting.
57
private static final int DEBUG_NORMAL = 4;
62
private static final int DEBUG_NONE = 0;
67
protected CatalogManager manager = new CatalogManager();
70
* The FileSystem in use.
72
protected FileSystem fs = FileSystem.getDefaultFileSystem();
77
private org.apache.xml.resolver.tools.CatalogResolver resolver;
85
* Constructs the CatalogResolver
87
public CatalogResolver()
89
manager.setIgnoreMissingProperties(true);
90
manager.setUseStaticCatalog(false);
91
manager.setFileSystem(fs);
96
* Set the list of catalog file names
98
* @param catalogs The delimited list of catalog files.
100
public void setCatalogFiles(String catalogs)
102
manager.setCatalogFiles(catalogs);
106
* Set the FileSystem.
107
* @param fileSystem The FileSystem.
109
public void setFileSystem(FileSystem fileSystem)
111
this.fs = fileSystem;
112
manager.setFileSystem(fileSystem);
117
* @param baseDir The base path String.
119
public void setBaseDir(String baseDir)
121
manager.setBaseDir(baseDir);
125
* Set the StrSubstitutor.
126
* @param substitutor The StrSubstitutor.
128
public void setSubstitutor(StrSubstitutor substitutor)
130
manager.setSubstitutor(substitutor);
134
* Enables debug logging of xml-commons Catalog processing.
135
* @param debug True if debugging should be enabled, false otherwise.
137
public void setDebug(boolean debug)
141
manager.setVerbosity(DEBUG_ALL);
145
manager.setVerbosity(DEBUG_NONE);
150
* Implements the {@code resolveEntity} method
151
* for the SAX interface.
153
* <p>Presented with an optional public identifier and a system
154
* identifier, this function attempts to locate a mapping in the
157
* <p>If such a mapping is found, the resolver attempts to open
158
* the mapped value as an InputSource and return it. Exceptions are
159
* ignored and null is returned if the mapped value cannot be opened
160
* as an input source.</p>
162
* <p>If no mapping is found (or an error occurs attempting to open
163
* the mapped value as an input source), null is returned and the system
164
* will use the specified system identifier as if no entityResolver
167
* @param publicId The public identifier for the entity in question.
169
* @param systemId The system identifier for the entity in question.
170
* XML requires a system identifier on all external entities, so this
171
* value is always specified.
172
* @return An InputSource for the mapped identifier, or null.
173
* @throws SAXException if an error occurs.
175
public InputSource resolveEntity(String publicId, String systemId)
178
String resolved = getResolver().getResolvedEntity(publicId, systemId);
180
if (resolved != null)
182
String badFilePrefix = "file://";
183
String correctFilePrefix = "file:///";
185
// Java 5 has a bug when constructing file URLS
186
if (resolved.startsWith(badFilePrefix) && !resolved.startsWith(correctFilePrefix))
188
resolved = correctFilePrefix + resolved.substring(badFilePrefix.length());
193
InputStream is = fs.getInputStream(null, resolved);
194
InputSource iSource = new InputSource(resolved);
195
iSource.setPublicId(publicId);
196
iSource.setByteStream(is);
201
log.warn("Failed to create InputSource for " + resolved + " ("
202
+ e.toString() + ")");
211
* Returns the logger used by this configuration object.
215
public Log getLogger()
221
* Allows to set the logger to be used by this configuration object. This
222
* method makes it possible for clients to exactly control logging behavior.
223
* Per default a logger is set that will ignore all log messages. Derived
224
* classes that want to enable logging should call this method during their
225
* initialization with the logger to be used.
227
* @param log the new logger
229
public void setLogger(Log log)
231
this.log = (log != null) ? log : LogFactory.getLog(CatalogResolver.class);
234
private synchronized org.apache.xml.resolver.tools.CatalogResolver getResolver()
236
if (resolver == null)
238
resolver = new org.apache.xml.resolver.tools.CatalogResolver(manager);
244
* Extend the CatalogManager to make the FileSystem and base directory accessible.
246
public static class CatalogManager extends org.apache.xml.resolver.CatalogManager
248
/** The static catalog used by this manager. */
249
private static org.apache.xml.resolver.Catalog staticCatalog;
251
/** The FileSystem */
252
private FileSystem fs;
254
/** The base directory */
255
private String baseDir = System.getProperty("user.dir");
257
/** The String Substitutor */
258
private StrSubstitutor substitutor;
262
* @param fileSystem The FileSystem in use.
264
public void setFileSystem(FileSystem fileSystem)
266
this.fs = fileSystem;
270
* Retrieve the FileSystem.
271
* @return The FileSystem.
273
public FileSystem getFileSystem()
279
* Set the base directory.
280
* @param baseDir The base directory.
282
public void setBaseDir(String baseDir)
286
this.baseDir = baseDir;
291
* Return the base directory.
292
* @return The base directory.
294
public String getBaseDir()
299
public void setSubstitutor(StrSubstitutor substitutor)
301
this.substitutor = substitutor;
304
public StrSubstitutor getStrSubstitutor()
306
return this.substitutor;
311
* Get a new catalog instance. This method is only overridden because xml-resolver
312
* might be in a parent ClassLoader and will be incapable of loading our Catalog
315
* This method always returns a new instance of the underlying catalog class.
316
* @return the Catalog.
319
public org.apache.xml.resolver.Catalog getPrivateCatalog()
321
org.apache.xml.resolver.Catalog catalog = staticCatalog;
323
if (catalog == null || !getUseStaticCatalog())
327
catalog = new Catalog();
328
catalog.setCatalogManager(this);
329
catalog.setupReaders();
330
catalog.loadSystemCatalogs();
334
ex.printStackTrace();
337
if (getUseStaticCatalog())
339
staticCatalog = catalog;
347
* Get a catalog instance.
349
* If this manager uses static catalogs, the same static catalog will
350
* always be returned. Otherwise a new catalog will be returned.
351
* @return The Catalog.
354
public org.apache.xml.resolver.Catalog getCatalog()
356
return getPrivateCatalog();
361
* Overrides the Catalog implementation to use the underlying FileSystem.
363
public static class Catalog extends org.apache.xml.resolver.Catalog
365
/** The FileSystem */
366
private FileSystem fs;
368
/** FileNameMap to determine the mime type */
369
private FileNameMap fileNameMap = URLConnection.getFileNameMap();
373
* @throws IOException if an error occurs.
376
public void loadSystemCatalogs() throws IOException
378
fs = ((CatalogManager) catalogManager).getFileSystem();
379
String base = ((CatalogManager) catalogManager).getBaseDir();
381
// This is safe because the catalog manager returns a vector of strings.
382
@SuppressWarnings("unchecked")
383
Vector<String> catalogs = catalogManager.getCatalogFiles();
384
if (catalogs != null)
386
for (int count = 0; count < catalogs.size(); count++)
388
String fileName = catalogs.elementAt(count);
391
InputStream is = null;
395
url = ConfigurationUtils.locate(fs, base, fileName);
398
is = fs.getInputStream(url);
401
catch (ConfigurationException ce)
403
String name = (url == null) ? fileName : url.toString();
404
// Ignore the exception.
405
catalogManager.debug.message(DEBUG_ALL,
406
"Unable to get input stream for " + name + ". " + ce.getMessage());
410
String mimeType = fileNameMap.getContentTypeFor(fileName);
413
if (mimeType != null)
415
parseCatalog(mimeType, is);
421
// Ignore the exception.
422
catalogManager.debug.message(DEBUG_ALL,
423
"Exception caught parsing input stream for " + fileName + ". "
431
parseCatalog(base, fileName);
438
* Parse the specified catalog file.
439
* @param baseDir The base directory, if not included in the file name.
440
* @param fileName The catalog file. May be a full URI String.
441
* @throws IOException If an error occurs.
443
public void parseCatalog(String baseDir, String fileName) throws IOException
445
base = ConfigurationUtils.locate(fs, baseDir, fileName);
447
default_override = catalogManager.getPreferPublic();
448
catalogManager.debug.message(DEBUG_NORMAL, "Parse catalog: " + fileName);
450
boolean parsed = false;
452
for (int count = 0; !parsed && count < readerArr.size(); count++)
454
CatalogReader reader = (CatalogReader) readerArr.get(count);
455
InputStream inStream;
459
inStream = fs.getInputStream(base);
463
catalogManager.debug.message(DEBUG_NORMAL, "Unable to access " + base
470
reader.readCatalog(this, inStream);
473
catch (CatalogException ce)
475
catalogManager.debug.message(DEBUG_NORMAL, "Parse failed for " + fileName
477
if (ce.getExceptionType() == CatalogException.PARSE_FAILED)
493
catch (IOException ioe)
495
// Ignore the exception.
503
parsePendingCatalogs();
508
* Perform character normalization on a URI reference.
510
* @param uriref The URI reference
511
* @return The normalized URI reference.
514
protected String normalizeURI(String uriref)
516
StrSubstitutor substitutor = ((CatalogManager) catalogManager).getStrSubstitutor();
517
String resolved = substitutor != null ? substitutor.replace(uriref) : uriref;
518
return super.normalizeURI(resolved);