1 /* 2 * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved. 3 * @LastModified: Sep 2017 4 */ 5 /* 6 * Licensed to the Apache Software Foundation (ASF) under one or more 7 * contributor license agreements. See the NOTICE file distributed with 8 * this work for additional information regarding copyright ownership. 9 * The ASF licenses this file to You under the Apache License, Version 2.0 10 * (the "License"); you may not use this file except in compliance with 11 * the License. You may obtain a copy of the License at 12 * 13 * http://www.apache.org/licenses/LICENSE-2.0 14 * 15 * Unless required by applicable law or agreed to in writing, software 16 * distributed under the License is distributed on an "AS IS" BASIS, 17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 * See the License for the specific language governing permissions and 19 * limitations under the License. 20 */ 21 22 package com.sun.org.apache.xml.internal.utils; 23 24 import com.sun.org.apache.xalan.internal.XalanConstants; 25 import com.sun.org.apache.xalan.internal.utils.FactoryImpl; 26 import com.sun.org.apache.xalan.internal.utils.XMLSecurityManager; 27 import java.util.HashMap; 28 import javax.xml.XMLConstants; 29 import javax.xml.catalog.CatalogFeatures; 30 import javax.xml.parsers.FactoryConfigurationError; 31 import javax.xml.parsers.ParserConfigurationException; 32 import javax.xml.parsers.SAXParserFactory; 33 import jdk.xml.internal.JdkXmlFeatures; 34 import jdk.xml.internal.JdkXmlUtils; 35 import jdk.xml.internal.SecuritySupport; 36 import org.xml.sax.SAXException; 37 import org.xml.sax.SAXNotRecognizedException; 38 import org.xml.sax.SAXNotSupportedException; 39 import org.xml.sax.XMLReader; 40 import org.xml.sax.helpers.XMLReaderFactory; 41 42 /** 43 * Creates XMLReader objects and caches them for re-use. 44 * This class follows the singleton pattern. 45 */ 46 @SuppressWarnings("deprecation") //org.xml.sax.helpers.XMLReaderFactory 47 public class XMLReaderManager { 48 49 private static final String NAMESPACES_FEATURE = 50 "http://xml.org/sax/features/namespaces"; 51 private static final String NAMESPACE_PREFIXES_FEATURE = 52 "http://xml.org/sax/features/namespace-prefixes"; 53 private static final XMLReaderManager m_singletonManager = 54 new XMLReaderManager(); 55 private static final String property = "org.xml.sax.driver"; 56 /** 57 * Parser factory to be used to construct XMLReader objects 58 */ 59 private static SAXParserFactory m_parserFactory; 60 61 /** 62 * Cache of XMLReader objects 63 */ 64 private ThreadLocal<XMLReader> m_readers; 65 66 /** 67 * Keeps track of whether an XMLReader object is in use. 68 */ 69 private HashMap<XMLReader, Boolean> m_inUse; 70 71 private boolean m_useServicesMechanism = true; 72 73 private boolean _secureProcessing; 74 /** 75 * protocols allowed for external DTD references in source file and/or stylesheet. 76 */ 77 private String _accessExternalDTD = XalanConstants.EXTERNAL_ACCESS_DEFAULT; 78 79 private XMLSecurityManager _xmlSecurityManager; 80 81 //Catalog Feature 82 private boolean _useCatalog; 83 private CatalogFeatures _catalogFeatures; 84 85 private int _cdataChunkSize; 86 87 /** 88 * Hidden constructor 89 */ 90 private XMLReaderManager() { 91 } 92 93 /** 94 * Retrieves the singleton reader manager 95 */ 96 public static XMLReaderManager getInstance(boolean useServicesMechanism) { 97 m_singletonManager.setServicesMechnism(useServicesMechanism); 98 return m_singletonManager; 99 } 100 101 /** 102 * Retrieves a cached XMLReader for this thread, or creates a new 103 * XMLReader, if the existing reader is in use. When the caller no 104 * longer needs the reader, it must release it with a call to 105 * {@link #releaseXMLReader}. 106 */ 107 public synchronized XMLReader getXMLReader() throws SAXException { 108 XMLReader reader; 109 110 if (m_readers == null) { 111 // When the m_readers.get() method is called for the first time 112 // on a thread, a new XMLReader will automatically be created. 113 m_readers = new ThreadLocal<>(); 114 } 115 116 if (m_inUse == null) { 117 m_inUse = new HashMap<>(); 118 } 119 120 // If the cached reader for this thread is in use, construct a new 121 // one; otherwise, return the cached reader unless it isn't an 122 // instance of the class set in the 'org.xml.sax.driver' property 123 reader = m_readers.get(); 124 boolean threadHasReader = (reader != null); 125 String factory = SecuritySupport.getSystemProperty(property); 126 if (threadHasReader && m_inUse.get(reader) != Boolean.TRUE && 127 ( factory == null || reader.getClass().getName().equals(factory))) { 128 m_inUse.put(reader, Boolean.TRUE); 129 } else { 130 try { 131 try { 132 // According to JAXP 1.2 specification, if a SAXSource 133 // is created using a SAX InputSource the Transformer or 134 // TransformerFactory creates a reader via the 135 // XMLReaderFactory if setXMLReader is not used 136 reader = XMLReaderFactory.createXMLReader(); 137 try { 138 reader.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, _secureProcessing); 139 } catch (SAXNotRecognizedException e) { 140 XMLSecurityManager.printWarning(reader.getClass().getName(), 141 XMLConstants.FEATURE_SECURE_PROCESSING, e); 142 } 143 } catch (SAXException e) { 144 try { 145 // If unable to create an instance, let's try to use 146 // the XMLReader from JAXP 147 if (m_parserFactory == null) { 148 m_parserFactory = FactoryImpl.getSAXFactory(m_useServicesMechanism); 149 m_parserFactory.setNamespaceAware(true); 150 } 151 152 reader = m_parserFactory.newSAXParser().getXMLReader(); 153 } catch (ParserConfigurationException pce) { 154 throw pce; // pass along pce 155 } 156 } 157 try { 158 reader.setFeature(NAMESPACES_FEATURE, true); 159 reader.setFeature(NAMESPACE_PREFIXES_FEATURE, false); 160 } catch (SAXException se) { 161 // Try to carry on if we've got a parser that 162 // doesn't know about namespace prefixes. 163 } 164 } catch (ParserConfigurationException ex) { 165 throw new SAXException(ex); 166 } catch (FactoryConfigurationError ex1) { 167 throw new SAXException(ex1.toString()); 168 } catch (NoSuchMethodError | AbstractMethodError ex2) { 169 } 170 171 // Cache the XMLReader if this is the first time we've created 172 // a reader for this thread. 173 if (!threadHasReader) { 174 m_readers.set(reader); 175 m_inUse.put(reader, Boolean.TRUE); 176 } 177 } 178 179 //reader is cached, but this property might have been reset 180 JdkXmlUtils.setXMLReaderPropertyIfSupport(reader, XMLConstants.ACCESS_EXTERNAL_DTD, 181 _accessExternalDTD, true); 182 183 JdkXmlUtils.setXMLReaderPropertyIfSupport(reader, JdkXmlUtils.CDATA_CHUNK_SIZE, 184 _cdataChunkSize, false); 185 186 String lastProperty = ""; 187 try { 188 if (_xmlSecurityManager != null) { 189 for (XMLSecurityManager.Limit limit : XMLSecurityManager.Limit.values()) { 190 lastProperty = limit.apiProperty(); 191 reader.setProperty(lastProperty, 192 _xmlSecurityManager.getLimitValueAsString(limit)); 193 } 194 if (_xmlSecurityManager.printEntityCountInfo()) { 195 lastProperty = XalanConstants.JDK_ENTITY_COUNT_INFO; 196 reader.setProperty(XalanConstants.JDK_ENTITY_COUNT_INFO, XalanConstants.JDK_YES); 197 } 198 } 199 } catch (SAXException se) { 200 XMLSecurityManager.printWarning(reader.getClass().getName(), lastProperty, se); 201 } 202 203 boolean supportCatalog = true; 204 try { 205 reader.setFeature(JdkXmlUtils.USE_CATALOG, _useCatalog); 206 } 207 catch (SAXNotRecognizedException | SAXNotSupportedException e) { 208 supportCatalog = false; 209 } 210 211 if (supportCatalog && _useCatalog && _catalogFeatures != null) { 212 try { 213 for (CatalogFeatures.Feature f : CatalogFeatures.Feature.values()) { 214 reader.setProperty(f.getPropertyName(), _catalogFeatures.get(f)); 215 } 216 } catch (SAXNotRecognizedException e) { 217 //shall not happen for internal settings 218 } 219 } 220 return reader; 221 } 222 223 /** 224 * Mark the cached XMLReader as available. If the reader was not 225 * actually in the cache, do nothing. 226 * 227 * @param reader The XMLReader that's being released. 228 */ 229 public synchronized void releaseXMLReader(XMLReader reader) { 230 // If the reader that's being released is the cached reader 231 // for this thread, remove it from the m_isUse list. 232 if (m_readers.get() == reader && reader != null) { 233 m_inUse.remove(reader); 234 } 235 } 236 /** 237 * Return the state of the services mechanism feature. 238 */ 239 public boolean useServicesMechnism() { 240 return m_useServicesMechanism; 241 } 242 243 /** 244 * Set the state of the services mechanism feature. 245 */ 246 public void setServicesMechnism(boolean flag) { 247 m_useServicesMechanism = flag; 248 } 249 250 /** 251 * Set feature 252 */ 253 public void setFeature(String name, boolean value) { 254 if (name.equals(XMLConstants.FEATURE_SECURE_PROCESSING)) { 255 _secureProcessing = value; 256 } else if (XMLConstants.USE_CATALOG.equals(name)) { 257 _useCatalog = value; 258 } 259 } 260 261 /** 262 * Get property value 263 */ 264 public Object getProperty(String name) { 265 if (name.equals(XMLConstants.ACCESS_EXTERNAL_DTD)) { 266 return _accessExternalDTD; 267 } else if (name.equals(XalanConstants.SECURITY_MANAGER)) { 268 return _xmlSecurityManager; 269 } 270 return null; 271 } 272 273 /** 274 * Set property. 275 */ 276 public void setProperty(String name, Object value) { 277 if (name.equals(XMLConstants.ACCESS_EXTERNAL_DTD)) { 278 _accessExternalDTD = (String)value; 279 } else if (name.equals(XalanConstants.SECURITY_MANAGER)) { 280 _xmlSecurityManager = (XMLSecurityManager)value; 281 } else if (JdkXmlFeatures.CATALOG_FEATURES.equals(name)) { 282 _catalogFeatures = (CatalogFeatures)value; 283 } else if (JdkXmlUtils.CDATA_CHUNK_SIZE.equals(name)) { 284 _cdataChunkSize = JdkXmlUtils.getValue(value, _cdataChunkSize); 285 } 286 } 287 }