Coverage Report - org.kuali.rice.kew.xml.ClassLoaderEntityResolver
 
Classes in this File Line Coverage Branch Coverage Complexity
ClassLoaderEntityResolver
0%
0/25
0%
0/12
3.667
 
 1  
 /*
 2  
  * Copyright 2005-2008 The Kuali Foundation
 3  
  * 
 4  
  * 
 5  
  * Licensed under the Educational Community License, Version 2.0 (the "License");
 6  
  * you may not use this file except in compliance with the License.
 7  
  * You may obtain a copy of the License at
 8  
  * 
 9  
  * http://www.opensource.org/licenses/ecl2.php
 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.kuali.rice.kew.xml;
 18  
 
 19  
 import java.io.IOException;
 20  
 import java.io.InputStream;
 21  
 
 22  
 import org.apache.log4j.Logger;
 23  
 import org.xml.sax.EntityResolver;
 24  
 import org.xml.sax.InputSource;
 25  
 import org.xml.sax.SAXException;
 26  
 
 27  
 /**
 28  
  * Internal workflow EntityResolver which resolves system ids with the
 29  
  * "resource:" prefix to ClassLoader resources
 30  
  * 
 31  
  * TODO: maybe prefix should be changed from "resource:" to "internal:" or just "workflow:"
 32  
  * given that it can be resolved in arbitrary ways other than ClassLoader "resources"
 33  
  * 
 34  
  * @author Kuali Rice Team (rice.collab@kuali.org)
 35  
  */
 36  
 public class ClassLoaderEntityResolver implements EntityResolver {
 37  0
     private static final Logger LOG = Logger.getLogger(ClassLoaderEntityResolver.class);
 38  
 
 39  
     /**
 40  
      * This contains definitions for items in the core "xml" schema, i.e. base, id, lang, and space attributes. 
 41  
      */
 42  
     private static final String XML_NAMESPACE_SCHEMA = "http://www.w3.org/2001/xml.xsd";
 43  
     private static final String XSD_NAMESPACE_SCHEMA = "http://www.w3.org/2001/XMLSchema.xsd";
 44  
     
 45  
     private final String base;
 46  0
     public ClassLoaderEntityResolver() {
 47  0
         this.base = "schema";
 48  0
     }
 49  0
     public ClassLoaderEntityResolver(String base) {
 50  0
         this.base = base;
 51  0
     }
 52  
     public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
 53  0
         LOG.debug("Resolving '" + publicId + "' / '" + systemId + "'");
 54  0
         String path = "";
 55  0
         if (systemId.equals(XML_NAMESPACE_SCHEMA)) {
 56  0
             path = base + "/xml.xsd";
 57  0
         } else if (systemId.equals(XSD_NAMESPACE_SCHEMA)) {
 58  0
             path = base + "/XMLSchema.xsd";
 59  0
         } else if (systemId.startsWith("resource")) {
 60  
             /* It turns out that the stock XMLSchema.xsd refers to XMLSchema.dtd in a relative
 61  
                fashion which results in the parser qualifying it to some local file:// path
 62  
                which breaks our detection here.
 63  
                So I have made a small mod to the stock XMLSchema.xsd so that it instead refers to
 64  
                resource:XMLSchema.dtd which can be looked up locally.
 65  
                The same is true for XMLSchema.dtd with regard to datatypes.dtd, so I have also
 66  
                modified XMLSchema.dtd to refer to resource:datatypes.dtd.
 67  
                An alternative would be to rely on publicId, however that would essentially hard code
 68  
                the lookup to always be in the classpath and rule out being able to redirect the location
 69  
                of the physical resource through the systemId, which is useful.
 70  
             */
 71  
 
 72  
             // TODO: revisit making this more sophisticated than just the classloader
 73  
             // of this class (thread context classloader? plugin classloader?)
 74  0
             path = base + "/" + systemId.substring("resource:".length());
 75  
             // ok, if the path does not itself end in .xsd or .dtd, it is bare/abstract
 76  
             // so realize it by appending .xsd
 77  
             // this allows us to support looking up files ending with ".dtd" through resource: without
 78  
             // having extra logic to attempt to look up both suffixes for every single resource:
 79  
             // (all of which except XMLSchema.dtd and datatypes.dtd at this point are .xsd files)
 80  0
             if (!(systemId.endsWith(".xsd") || systemId.endsWith(".dtd"))) {
 81  0
                 path += ".xsd";
 82  
             }
 83  
         } else {
 84  0
             LOG.error("Unable to resolve system id '" + systemId + "' locally...delegating to default resolution strategy.");
 85  0
             return null;
 86  
         }
 87  0
         InputStream is = getClass().getClassLoader().getResourceAsStream(path);
 88  0
         if (is == null) {
 89  0
             String message = "Unable to find schema (" + path + ") for: " + systemId;
 90  0
             LOG.error(message);
 91  0
             throw new SAXException(message);
 92  
         }
 93  0
         return new InputSource(is);
 94  
     }
 95  
 }