1 /*
   2  * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.sun.tools.internal.xjc;
  27 
  28 import java.io.IOException;
  29 import java.io.InputStream;
  30 import java.io.Reader;
  31 import java.util.ArrayList;
  32 import java.util.Collections;
  33 import java.util.List;
  34 
  35 import javax.xml.transform.stream.StreamSource;
  36 import javax.xml.validation.Schema;
  37 import javax.xml.validation.SchemaFactory;
  38 import javax.xml.validation.ValidatorHandler;
  39 
  40 import com.sun.xml.internal.bind.v2.util.XmlFactory;
  41 import javax.xml.XMLConstants;
  42 
  43 import org.w3c.dom.ls.LSInput;
  44 import org.w3c.dom.ls.LSResourceResolver;
  45 import org.xml.sax.SAXException;
  46 
  47 import static com.sun.xml.internal.bind.v2.util.XmlFactory.allowExternalAccess;
  48 
  49 /**
  50  * Wraps a JAXP {@link Schema} object and lazily instantiate it.
  51  *
  52  * This object is thread-safe. There should be only one instance of
  53  * this for the whole VM.
  54  *
  55  * @author Kohsuke Kawaguchi
  56  */
  57 public final class SchemaCache {
  58 
  59     private final boolean createResolver;
  60     private final String resourceName;
  61     private final Class<?> clazz;
  62 
  63     private Schema schema;
  64 
  65     public SchemaCache(String resourceName, Class<?> classToResolveResources) {
  66         this(resourceName, classToResolveResources, false);
  67     }
  68 
  69     public SchemaCache(String resourceName, Class<?> classToResolveResources, boolean createResolver) {
  70         this.resourceName = resourceName;
  71         this.createResolver = createResolver;
  72         this.clazz = classToResolveResources;
  73     }
  74 
  75     public ValidatorHandler newValidator() {
  76         if (schema==null) {
  77             synchronized (this) {
  78                 if (schema == null) {
  79 
  80                     ResourceResolver resourceResolver = null;
  81                     try (InputStream is = clazz.getResourceAsStream(resourceName)) {
  82 
  83                         StreamSource source = new StreamSource(is);
  84                         source.setSystemId(resourceName);
  85                         // do not disable secure processing - these are well-known schemas
  86 
  87                         SchemaFactory sf = XmlFactory.createSchemaFactory(XMLConstants.W3C_XML_SCHEMA_NS_URI, false);
  88                         SchemaFactory schemaFactory = allowExternalAccess(sf, "file", false);
  89 
  90                         if (createResolver) {
  91                             resourceResolver = new ResourceResolver(clazz);
  92                             schemaFactory.setResourceResolver(resourceResolver);
  93                         }
  94                         schema = schemaFactory.newSchema(source);
  95 
  96                     } catch (IOException | SAXException e) {
  97                         InternalError ie = new InternalError(e.getMessage());
  98                         ie.initCause(e);
  99                         throw ie;
 100                     } finally {
 101                         if (resourceResolver != null) resourceResolver.closeStreams();
 102                     }
 103                 }
 104             }
 105         }
 106         return schema.newValidatorHandler();
 107     }
 108 
 109     class ResourceResolver implements LSResourceResolver {
 110 
 111         private List<InputStream> streamsToClose = Collections.synchronizedList(new ArrayList<InputStream>());
 112         private Class<?> clazz;
 113 
 114         ResourceResolver(Class<?> clazz) {
 115             this.clazz = clazz;
 116         }
 117 
 118         @Override
 119         public LSInput resolveResource(String type, String namespaceURI, String publicId, String systemId, String baseURI) {
 120             // XSOM passes the namespace URI to the publicID parameter.
 121             // we do the same here .
 122             InputStream is = clazz.getResourceAsStream(systemId);
 123             streamsToClose.add(is);
 124             return new Input(is, publicId, systemId);
 125         }
 126 
 127         void closeStreams() {
 128             for (InputStream is : streamsToClose) {
 129                 if (is != null) {
 130                     try {
 131                         is.close();
 132                     } catch (IOException e) {
 133                         // nothing to do ...
 134                     }
 135                 }
 136             }
 137         }
 138     }
 139 
 140 }
 141 
 142 class Input implements LSInput {
 143 
 144     private InputStream is;
 145     private String publicId;
 146     private String systemId;
 147 
 148     public Input(InputStream is, String publicId, String systemId) {
 149         this.is = is;
 150         this.publicId = publicId;
 151         this.systemId = systemId;
 152     }
 153 
 154     @Override
 155     public Reader getCharacterStream() {
 156         return null;
 157     }
 158 
 159     @Override
 160     public void setCharacterStream(Reader characterStream) {
 161     }
 162 
 163     @Override
 164     public InputStream getByteStream() {
 165         return is;
 166     }
 167 
 168     @Override
 169     public void setByteStream(InputStream byteStream) {
 170     }
 171 
 172     @Override
 173     public String getStringData() {
 174         return null;
 175     }
 176 
 177     @Override
 178     public void setStringData(String stringData) {
 179     }
 180 
 181     @Override
 182     public String getSystemId() {
 183         return systemId;
 184     }
 185 
 186     @Override
 187     public void setSystemId(String systemId) {
 188     }
 189 
 190     @Override
 191     public String getPublicId() {
 192         return publicId;
 193     }
 194 
 195     @Override
 196     public void setPublicId(String publicId) {
 197     }
 198 
 199     @Override
 200     public String getBaseURI() {
 201         return null;
 202     }
 203 
 204     @Override
 205     public void setBaseURI(String baseURI) {
 206     }
 207 
 208     @Override
 209     public String getEncoding() {
 210         return null;
 211     }
 212 
 213     @Override
 214     public void setEncoding(String encoding) {
 215     }
 216 
 217     @Override
 218     public boolean getCertifiedText() {
 219         return false;
 220     }
 221 
 222     @Override
 223     public void setCertifiedText(boolean certifiedText) {
 224     }
 225 }