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.XMLSecurityManager; 26 import java.util.HashMap; 27 import javax.xml.XMLConstants; 28 import javax.xml.catalog.CatalogFeatures; 29 import javax.xml.parsers.FactoryConfigurationError; 30 import javax.xml.parsers.ParserConfigurationException; 31 import javax.xml.parsers.SAXParserFactory; 32 import jdk.xml.internal.JdkXmlFeatures; 33 import jdk.xml.internal.JdkXmlUtils; 34 import jdk.xml.internal.SecuritySupport; 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 * @LastModified: Sep 2017 46 */ 47 @SuppressWarnings("deprecation") //org.xml.sax.helpers.XMLReaderFactory 48 public class XMLReaderManager { 49 50 private static final String NAMESPACES_FEATURE = 51 "http://xml.org/sax/features/namespaces"; 52 private static final String NAMESPACE_PREFIXES_FEATURE = 53 "http://xml.org/sax/features/namespace-prefixes"; 54 private static final XMLReaderManager m_singletonManager = 55 new XMLReaderManager(); 56 private static final String property = "org.xml.sax.driver"; 57 /** 58 * Parser factory to be used to construct XMLReader objects 59 */ 60 private static SAXParserFactory m_parserFactory; 61 62 /** 63 * Cache of XMLReader objects 64 */ 65 private ThreadLocal<XMLReader> m_readers; 66 67 /** 68 * Keeps track of whether an XMLReader object is in use. 69 */ 70 private HashMap<XMLReader, Boolean> m_inUse; 71 72 private boolean m_useServicesMechanism = true; 73 74 private boolean _secureProcessing; 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 //Catalog Feature 83 private boolean _useCatalog; 84 private CatalogFeatures _catalogFeatures; 85 86 private int _cdataChunkSize; 87 88 /** 89 * Hidden constructor 90 */ 91 private XMLReaderManager() { 92 } 93 94 /** 95 * Retrieves the singleton reader manager 96 */ 97 public static XMLReaderManager getInstance(boolean useServicesMechanism) { 98 m_singletonManager.setServicesMechnism(useServicesMechanism); 99 return m_singletonManager; 100 } 101 102 /** 103 * Retrieves a cached XMLReader for this thread, or creates a new 104 * XMLReader, if the existing reader is in use. When the caller no 105 * longer needs the reader, it must release it with a call to 106 * {@link #releaseXMLReader}. 107 */ 108 public synchronized XMLReader getXMLReader() throws SAXException { 109 XMLReader reader; 110 111 if (m_readers == null) { 112 // When the m_readers.get() method is called for the first time 113 // on a thread, a new XMLReader will automatically be created. 114 m_readers = new ThreadLocal<>(); 115 } 116 117 if (m_inUse == null) { 118 m_inUse = new HashMap<>(); 119 } 120 121 // If the cached reader for this thread is in use, construct a new 122 // one; otherwise, return the cached reader unless it isn't an 123 // instance of the class set in the 'org.xml.sax.driver' property 124 reader = m_readers.get(); 125 boolean threadHasReader = (reader != null); 126 String factory = SecuritySupport.getSystemProperty(property); 127 if (threadHasReader && m_inUse.get(reader) != Boolean.TRUE && 128 ( factory == null || reader.getClass().getName().equals(factory))) { 129 m_inUse.put(reader, Boolean.TRUE); 130 } else { 131 try { 132 try { 133 // According to JAXP 1.2 specification, if a SAXSource 134 // is created using a SAX InputSource the Transformer or 135 // TransformerFactory creates a reader via the 136 // XMLReaderFactory if setXMLReader is not used 137 reader = XMLReaderFactory.createXMLReader(); 138 try { 139 reader.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, _secureProcessing); 140 } catch (SAXNotRecognizedException e) { 141 XMLSecurityManager.printWarning(reader.getClass().getName(), 142 XMLConstants.FEATURE_SECURE_PROCESSING, e); 143 } 144 } catch (SAXException e) { 145 try { 146 // If unable to create an instance, let's try to use 147 // the XMLReader from JAXP 148 if (m_parserFactory == null) { 149 m_parserFactory = FactoryImpl.getSAXFactory(m_useServicesMechanism); 150 m_parserFactory.setNamespaceAware(true); 151 } 152 153 reader = m_parserFactory.newSAXParser().getXMLReader(); 154 } catch (ParserConfigurationException pce) { 155 throw pce; // pass along pce 156 } 157 } 158 try { 159 reader.setFeature(NAMESPACES_FEATURE, true); 160 reader.setFeature(NAMESPACE_PREFIXES_FEATURE, false); 161 } catch (SAXException se) { 162 // Try to carry on if we've got a parser that 163 // doesn't know about namespace prefixes. 164 } 165 } catch (ParserConfigurationException ex) { 166 throw new SAXException(ex); 167 } catch (FactoryConfigurationError ex1) { 168 throw new SAXException(ex1.toString()); 169 } catch (NoSuchMethodError | AbstractMethodError ex2) { 170 } 171 172 // Cache the XMLReader if this is the first time we've created 173 // a reader for this thread. 174 if (!threadHasReader) { 175 m_readers.set(reader); 176 m_inUse.put(reader, Boolean.TRUE); 177 } 178 } 179 180 //reader is cached, but this property might have been reset 181 JdkXmlUtils.setXMLReaderPropertyIfSupport(reader, XMLConstants.ACCESS_EXTERNAL_DTD, 182 _accessExternalDTD, true); 183 184 JdkXmlUtils.setXMLReaderPropertyIfSupport(reader, JdkXmlUtils.CDATA_CHUNK_SIZE, 185 _cdataChunkSize, false); 186 187 String lastProperty = ""; 188 try { 189 if (_xmlSecurityManager != null) { 190 for (XMLSecurityManager.Limit limit : XMLSecurityManager.Limit.values()) { 191 lastProperty = limit.apiProperty(); 192 reader.setProperty(lastProperty, 193 _xmlSecurityManager.getLimitValueAsString(limit)); 194 } 195 if (_xmlSecurityManager.printEntityCountInfo()) { 196 lastProperty = XalanConstants.JDK_ENTITY_COUNT_INFO; 197 reader.setProperty(XalanConstants.JDK_ENTITY_COUNT_INFO, XalanConstants.JDK_YES); 198 } 199 } 200 } catch (SAXException se) { 201 XMLSecurityManager.printWarning(reader.getClass().getName(), lastProperty, se); 202 } 203 204 boolean supportCatalog = true; 205 try { 206 reader.setFeature(JdkXmlUtils.USE_CATALOG, _useCatalog); 207 } 208 catch (SAXNotRecognizedException | SAXNotSupportedException e) { 209 supportCatalog = false; 210 } 211 212 if (supportCatalog && _useCatalog && _catalogFeatures != null) { 213 try { 214 for (CatalogFeatures.Feature f : CatalogFeatures.Feature.values()) { 215 reader.setProperty(f.getPropertyName(), _catalogFeatures.get(f)); 216 } 217 } catch (SAXNotRecognizedException e) { 218 //shall not happen for internal settings 219 } 220 } 221 return reader; 222 } 223 224 /** 225 * Mark the cached XMLReader as available. If the reader was not 226 * actually in the cache, do nothing. 227 * 228 * @param reader The XMLReader that's being released. 229 */ 230 public synchronized void releaseXMLReader(XMLReader reader) { 231 // If the reader that's being released is the cached reader 232 // for this thread, remove it from the m_isUse list. 233 if (m_readers.get() == reader && reader != null) { 234 m_inUse.remove(reader); 235 } 236 } 237 /** 238 * Return the state of the services mechanism feature. 239 */ 240 public boolean useServicesMechnism() { 241 return m_useServicesMechanism; 242 } 243 244 /** 245 * Set the state of the services mechanism feature. 246 */ 247 public void setServicesMechnism(boolean flag) { 248 m_useServicesMechanism = flag; 249 } 250 251 /** 252 * Set feature 253 */ 254 public void setFeature(String name, boolean value) { 255 if (name.equals(XMLConstants.FEATURE_SECURE_PROCESSING)) { 256 _secureProcessing = value; 257 } else if (XMLConstants.USE_CATALOG.equals(name)) { 258 _useCatalog = value; 259 } 260 } 261 262 /** 263 * Get property value 264 */ 265 public Object getProperty(String name) { 266 if (name.equals(XMLConstants.ACCESS_EXTERNAL_DTD)) { 267 return _accessExternalDTD; 268 } else if (name.equals(XalanConstants.SECURITY_MANAGER)) { 269 return _xmlSecurityManager; 270 } 271 return null; 272 } 273 274 /** 275 * Set property. 276 */ 277 public void setProperty(String name, Object value) { 278 if (name.equals(XMLConstants.ACCESS_EXTERNAL_DTD)) { 279 _accessExternalDTD = (String)value; 280 } else if (name.equals(XalanConstants.SECURITY_MANAGER)) { 281 _xmlSecurityManager = (XMLSecurityManager)value; 282 } else if (JdkXmlFeatures.CATALOG_FEATURES.equals(name)) { 283 _catalogFeatures = (CatalogFeatures)value; 284 } else if (JdkXmlUtils.CDATA_CHUNK_SIZE.equals(name)) { 285 _cdataChunkSize = JdkXmlUtils.getValue(value, _cdataChunkSize); 286 } 287 } 288 }