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