Coverage Report - org.apache.commons.configuration.resolver.CatalogResolver
 
Classes in this File Line Coverage Branch Coverage Complexity
CatalogResolver
88%
37/42
75%
9/12
2,429
CatalogResolver$Catalog
76%
43/56
54%
12/22
2,429
CatalogResolver$CatalogManager
88%
22/25
37%
3/8
2,429
 
 1  
 /*
 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
 8  
  *
 9  
  *     http://www.apache.org/licenses/LICENSE-2.0
 10  
  *
 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.
 16  
  */
 17  
 package org.apache.commons.configuration.resolver;
 18  
 
 19  
 import java.io.IOException;
 20  
 import java.io.InputStream;
 21  
 import java.net.FileNameMap;
 22  
 import java.net.URL;
 23  
 import java.net.URLConnection;
 24  
 import java.util.Vector;
 25  
 
 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;
 37  
 
 38  
 /**
 39  
  * Thin wrapper around xml commons CatalogResolver to allow list of catalogs
 40  
  * to be provided.
 41  
  * @author <a
 42  
  * href="http://commons.apache.org/configuration/team-list.html">Commons
 43  
  * Configuration team</a>
 44  
  * @since 1.7
 45  
  * @version $Id: CatalogResolver.java 1301991 2012-03-17 20:18:02Z sebb $
 46  
  */
 47  
 public class CatalogResolver implements EntityResolver
 48  
 {
 49  
     /**
 50  
      * Debug everything.
 51  
      */
 52  
     private static final int DEBUG_ALL = 9;
 53  
 
 54  
     /**
 55  
      * Normal debug setting.
 56  
      */
 57  
     private static final int DEBUG_NORMAL = 4;
 58  
 
 59  
     /**
 60  
      * Debug nothing.
 61  
      */
 62  
     private static final int DEBUG_NONE = 0;
 63  
 
 64  
     /**
 65  
      * The CatalogManager
 66  
      */
 67  25
     protected CatalogManager manager = new CatalogManager();
 68  
 
 69  
     /**
 70  
      * The FileSystem in use.
 71  
      */
 72  25
     protected FileSystem fs = FileSystem.getDefaultFileSystem();
 73  
 
 74  
     /**
 75  
      * The CatalogResolver
 76  
      */
 77  
     private org.apache.xml.resolver.tools.CatalogResolver resolver;
 78  
 
 79  
     /**
 80  
      * Stores the logger.
 81  
      */
 82  
     private Log log;
 83  
 
 84  
     /**
 85  
      * Constructs the CatalogResolver
 86  
      */
 87  
     public CatalogResolver()
 88  25
     {
 89  25
         manager.setIgnoreMissingProperties(true);
 90  25
         manager.setUseStaticCatalog(false);
 91  25
         manager.setFileSystem(fs);
 92  25
         setLogger(null);
 93  25
     }
 94  
 
 95  
     /**
 96  
      * Set the list of catalog file names
 97  
      *
 98  
      * @param catalogs The delimited list of catalog files.
 99  
      */
 100  
     public void setCatalogFiles(String catalogs)
 101  
     {
 102  25
         manager.setCatalogFiles(catalogs);
 103  25
     }
 104  
 
 105  
     /**
 106  
      * Set the FileSystem.
 107  
      * @param fileSystem The FileSystem.
 108  
      */
 109  
     public void setFileSystem(FileSystem fileSystem)
 110  
     {
 111  18
         this.fs = fileSystem;
 112  18
         manager.setFileSystem(fileSystem);
 113  18
     }
 114  
 
 115  
     /**
 116  
      * Set the base path.
 117  
      * @param baseDir The base path String.
 118  
      */
 119  
     public void setBaseDir(String baseDir)
 120  
     {
 121  18
         manager.setBaseDir(baseDir);
 122  18
     }
 123  
 
 124  
     /**
 125  
      * Set the StrSubstitutor.
 126  
      * @param substitutor The StrSubstitutor.
 127  
      */
 128  
     public void setSubstitutor(StrSubstitutor substitutor)
 129  
     {
 130  18
         manager.setSubstitutor(substitutor);
 131  18
     }
 132  
 
 133  
     /**
 134  
      * Enables debug logging of xml-commons Catalog processing.
 135  
      * @param debug True if debugging should be enabled, false otherwise.
 136  
      */
 137  
     public void setDebug(boolean debug)
 138  
     {
 139  1
         if (debug)
 140  
         {
 141  1
             manager.setVerbosity(DEBUG_ALL);
 142  
         }
 143  
         else
 144  
         {
 145  0
             manager.setVerbosity(DEBUG_NONE);
 146  
         }
 147  1
     }
 148  
 
 149  
     /**
 150  
      * Implements the {@code resolveEntity} method
 151  
      * for the SAX interface.
 152  
      * <p/>
 153  
      * <p>Presented with an optional public identifier and a system
 154  
      * identifier, this function attempts to locate a mapping in the
 155  
      * catalogs.</p>
 156  
      * <p/>
 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>
 161  
      * <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
 165  
      * was specified.</p>
 166  
      *
 167  
      * @param publicId The public identifier for the entity in question.
 168  
      *                 This may be null.
 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.
 174  
      */
 175  
     public InputSource resolveEntity(String publicId, String systemId)
 176  
             throws SAXException
 177  
     {
 178  1368
         String resolved = getResolver().getResolvedEntity(publicId, systemId);
 179  
 
 180  1392
         if (resolved != null)
 181  
         {
 182  1422
             String badFilePrefix = "file://";
 183  1423
             String correctFilePrefix = "file:///";
 184  
 
 185  
             // Java 5 has a bug when constructing file URLS
 186  1420
             if (resolved.startsWith(badFilePrefix) && !resolved.startsWith(correctFilePrefix))
 187  
             {
 188  1
                 resolved = correctFilePrefix + resolved.substring(badFilePrefix.length());
 189  
             }
 190  
 
 191  
             try
 192  
             {
 193  1359
                 InputStream is = fs.getInputStream(null, resolved);
 194  1431
                 InputSource iSource = new InputSource(resolved);
 195  1431
                 iSource.setPublicId(publicId);
 196  1426
                 iSource.setByteStream(is);
 197  1425
                 return iSource;
 198  
             }
 199  0
             catch (Exception e)
 200  
             {
 201  0
                 log.warn("Failed to create InputSource for " + resolved + " ("
 202  
                                 + e.toString() + ")");
 203  0
                 return null;
 204  
             }
 205  
         }
 206  
 
 207  0
         return null;
 208  
     }
 209  
 
 210  
     /**
 211  
      * Returns the logger used by this configuration object.
 212  
      *
 213  
      * @return the logger
 214  
      */
 215  
     public Log getLogger()
 216  
     {
 217  2
         return log;
 218  
     }
 219  
 
 220  
     /**
 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.
 226  
      *
 227  
      * @param log the new logger
 228  
      */
 229  
     public void setLogger(Log log)
 230  
     {
 231  26
         this.log = (log != null) ? log : LogFactory.getLog(CatalogResolver.class);
 232  26
     }
 233  
 
 234  
     private synchronized org.apache.xml.resolver.tools.CatalogResolver getResolver()
 235  
     {
 236  1431
         if (resolver == null)
 237  
         {
 238  22
             resolver = new org.apache.xml.resolver.tools.CatalogResolver(manager);
 239  
         }
 240  1431
         return resolver;
 241  
     }
 242  
 
 243  
     /**
 244  
      * Extend the CatalogManager to make the FileSystem and base directory accessible.
 245  
      */
 246  25
     public static class CatalogManager extends org.apache.xml.resolver.CatalogManager
 247  
     {
 248  
         /** The static catalog used by this manager. */
 249  
         private static org.apache.xml.resolver.Catalog staticCatalog;
 250  
 
 251  
         /** The FileSystem */
 252  
         private FileSystem fs;
 253  
 
 254  
         /** The base directory */
 255  25
         private String baseDir = System.getProperty("user.dir");
 256  
 
 257  
         /** The String Substitutor */
 258  
         private StrSubstitutor substitutor;
 259  
 
 260  
         /**
 261  
          * Set the FileSystem
 262  
          * @param fileSystem The FileSystem in use.
 263  
          */
 264  
         public void setFileSystem(FileSystem fileSystem)
 265  
         {
 266  43
             this.fs = fileSystem;
 267  43
         }
 268  
 
 269  
         /**
 270  
          * Retrieve the FileSystem.
 271  
          * @return The FileSystem.
 272  
          */
 273  
         public FileSystem getFileSystem()
 274  
         {
 275  22
             return this.fs;
 276  
         }
 277  
 
 278  
         /**
 279  
          * Set the base directory.
 280  
          * @param baseDir The base directory.
 281  
          */
 282  
         public void setBaseDir(String baseDir)
 283  
         {
 284  18
             if (baseDir != null)
 285  
             {
 286  18
                 this.baseDir = baseDir;
 287  
             }
 288  18
         }
 289  
 
 290  
         /**
 291  
          * Return the base directory.
 292  
          * @return The base directory.
 293  
          */
 294  
         public String getBaseDir()
 295  
         {
 296  22
             return this.baseDir;
 297  
         }
 298  
 
 299  
         public void setSubstitutor(StrSubstitutor substitutor)
 300  
         {
 301  18
             this.substitutor = substitutor;
 302  18
         }
 303  
 
 304  
         public StrSubstitutor getStrSubstitutor()
 305  
         {
 306  1502
             return this.substitutor;
 307  
         }
 308  
 
 309  
 
 310  
         /**
 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
 313  
          * implementation.
 314  
          *
 315  
          * This method always returns a new instance of the underlying catalog class.
 316  
          * @return the Catalog.
 317  
          */
 318  
         @Override
 319  
         public org.apache.xml.resolver.Catalog getPrivateCatalog()
 320  
         {
 321  22
             org.apache.xml.resolver.Catalog catalog = staticCatalog;
 322  
 
 323  22
             if (catalog == null || !getUseStaticCatalog())
 324  
             {
 325  
                 try
 326  
                 {
 327  22
                     catalog = new Catalog();
 328  22
                     catalog.setCatalogManager(this);
 329  22
                     catalog.setupReaders();
 330  22
                     catalog.loadSystemCatalogs();
 331  
                 }
 332  0
                 catch (Exception ex)
 333  
                 {
 334  0
                     ex.printStackTrace();
 335  22
                 }
 336  
 
 337  22
                 if (getUseStaticCatalog())
 338  
                 {
 339  0
                     staticCatalog = catalog;
 340  
                 }
 341  
             }
 342  
 
 343  22
             return catalog;
 344  
         }
 345  
 
 346  
         /**
 347  
          * Get a catalog instance.
 348  
          *
 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.
 352  
          */
 353  
         @Override
 354  
         public org.apache.xml.resolver.Catalog getCatalog()
 355  
         {
 356  22
             return getPrivateCatalog();
 357  
         }
 358  
     }
 359  
 
 360  
     /**
 361  
      * Overrides the Catalog implementation to use the underlying FileSystem.
 362  
      */
 363  22
     public static class Catalog extends org.apache.xml.resolver.Catalog
 364  
     {
 365  
         /** The FileSystem */
 366  
         private FileSystem fs;
 367  
 
 368  
         /** FileNameMap to determine the mime type */
 369  22
         private FileNameMap fileNameMap = URLConnection.getFileNameMap();
 370  
 
 371  
         /**
 372  
          * Load the catalogs.
 373  
          * @throws IOException if an error occurs.
 374  
          */
 375  
         @Override
 376  
         public void loadSystemCatalogs() throws IOException
 377  
         {
 378  22
             fs = ((CatalogManager) catalogManager).getFileSystem();
 379  22
             String base = ((CatalogManager) catalogManager).getBaseDir();
 380  
 
 381  
             // This is safe because the catalog manager returns a vector of strings.
 382  
             @SuppressWarnings("unchecked")
 383  22
             Vector<String> catalogs = catalogManager.getCatalogFiles();
 384  22
             if (catalogs != null)
 385  
             {
 386  44
                 for (int count = 0; count < catalogs.size(); count++)
 387  
                 {
 388  22
                     String fileName = catalogs.elementAt(count);
 389  
 
 390  22
                     URL url = null;
 391  22
                     InputStream is = null;
 392  
 
 393  
                     try
 394  
                     {
 395  22
                         url = ConfigurationUtils.locate(fs, base, fileName);
 396  22
                         if (url != null)
 397  
                         {
 398  22
                             is = fs.getInputStream(url);
 399  
                         }
 400  
                     }
 401  0
                     catch (ConfigurationException ce)
 402  
                     {
 403  0
                         String name = (url == null) ? fileName : url.toString();
 404  
                         // Ignore the exception.
 405  0
                         catalogManager.debug.message(DEBUG_ALL,
 406  
                             "Unable to get input stream for " + name + ". " + ce.getMessage());
 407  22
                     }
 408  22
                     if (is != null)
 409  
                     {
 410  22
                         String mimeType = fileNameMap.getContentTypeFor(fileName);
 411  
                         try
 412  
                         {
 413  22
                             if (mimeType != null)
 414  
                             {
 415  22
                                 parseCatalog(mimeType, is);
 416  
                                 continue;
 417  
                             }
 418  
                         }
 419  22
                         catch (Exception ex)
 420  
                         {
 421  
                             // Ignore the exception.
 422  22
                             catalogManager.debug.message(DEBUG_ALL,
 423  
                                 "Exception caught parsing input stream for " + fileName + ". "
 424  
                                 + ex.getMessage());
 425  
                         }
 426  
                         finally
 427  
                         {
 428  22
                             is.close();
 429  22
                         }
 430  
                     }
 431  22
                     parseCatalog(base, fileName);
 432  
                 }
 433  
             }
 434  
 
 435  22
         }
 436  
 
 437  
         /**
 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.
 442  
          */
 443  
         public void parseCatalog(String baseDir, String fileName) throws IOException
 444  
         {
 445  22
             base = ConfigurationUtils.locate(fs, baseDir, fileName);
 446  22
             catalogCwd = base;
 447  22
             default_override = catalogManager.getPreferPublic();
 448  22
             catalogManager.debug.message(DEBUG_NORMAL, "Parse catalog: " + fileName);
 449  
 
 450  22
             boolean parsed = false;
 451  
 
 452  44
             for (int count = 0; !parsed && count < readerArr.size(); count++)
 453  
             {
 454  22
                 CatalogReader reader = (CatalogReader) readerArr.get(count);
 455  
                 InputStream inStream;
 456  
 
 457  
                 try
 458  
                 {
 459  22
                     inStream = fs.getInputStream(base);
 460  
                 }
 461  0
                 catch (Exception ex)
 462  
                 {
 463  0
                     catalogManager.debug.message(DEBUG_NORMAL, "Unable to access " + base
 464  
                         + ex.getMessage());
 465  0
                     break;
 466  22
                 }
 467  
 
 468  
                 try
 469  
                 {
 470  22
                     reader.readCatalog(this, inStream);
 471  22
                     parsed = true;
 472  
                 }
 473  0
                 catch (CatalogException ce)
 474  
                 {
 475  0
                     catalogManager.debug.message(DEBUG_NORMAL, "Parse failed for " + fileName
 476  
                             + ce.getMessage());
 477  0
                     if (ce.getExceptionType() == CatalogException.PARSE_FAILED)
 478  
                     {
 479  
                         break;
 480  
                     }
 481  
                     else
 482  
                     {
 483  
                         // try again!
 484  
                         continue;
 485  
                     }
 486  
                 }
 487  
                 finally
 488  
                 {
 489  0
                     try
 490  
                     {
 491  22
                         inStream.close();
 492  
                     }
 493  0
                     catch (IOException ioe)
 494  
                     {
 495  
                         // Ignore the exception.
 496  0
                         inStream = null;
 497  22
                     }
 498  0
                 }
 499  
             }
 500  
 
 501  22
             if (parsed)
 502  
             {
 503  22
                 parsePendingCatalogs();
 504  
             }
 505  22
         }
 506  
 
 507  
         /**
 508  
          * Perform character normalization on a URI reference.
 509  
          *
 510  
          * @param uriref The URI reference
 511  
          * @return The normalized URI reference.
 512  
          */
 513  
         @Override
 514  
         protected String normalizeURI(String uriref)
 515  
         {
 516  1525
             StrSubstitutor substitutor = ((CatalogManager) catalogManager).getStrSubstitutor();
 517  1490
             String resolved = substitutor != null ? substitutor.replace(uriref) : uriref;
 518  1485
             return super.normalizeURI(resolved);
 519  
         }
 520  
     }
 521  
 }