001/** 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.camel.builder.xml; 018 019 020import java.util.ArrayList; 021import java.util.List; 022import java.util.Map; 023import java.util.Properties; 024 025import javax.xml.XMLConstants; 026import javax.xml.parsers.DocumentBuilder; 027import javax.xml.parsers.DocumentBuilderFactory; 028import javax.xml.parsers.ParserConfigurationException; 029 030import org.w3c.dom.Document; 031 032import org.xml.sax.ErrorHandler; 033import org.xml.sax.SAXException; 034import org.xml.sax.SAXParseException; 035 036import org.apache.camel.util.ObjectHelper; 037import org.apache.camel.util.StringHelper; 038import org.slf4j.Logger; 039import org.slf4j.LoggerFactory; 040 041/** 042 * XML XPathBuilder support. 043 */ 044public class XPathBuilderSupport { 045 046 private static final ErrorHandler DOCUMENT_BUILDER_LOGGING_ERROR_HANDLER = new DocumentBuilderLoggingErrorHandler(); 047 private static final String DOCUMENT_BUILDER_FACTORY_FEATURE = "org.apache.camel.xmlconverter.documentBuilderFactory.feature"; 048 049 private static final Logger LOG = LoggerFactory.getLogger(XPathBuilderSupport.class); 050 051 private volatile DocumentBuilderFactory documentBuilderFactory; 052 053 054 public DocumentBuilderFactory createDocumentBuilderFactory() { 055 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 056 factory.setNamespaceAware(true); 057 factory.setIgnoringElementContentWhitespace(true); 058 factory.setIgnoringComments(true); 059 try { 060 // Set secure processing 061 factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, Boolean.TRUE); 062 } catch (ParserConfigurationException e) { 063 LOG.warn("DocumentBuilderFactory doesn't support the feature {} with value {}, due to {}.", new Object[] {XMLConstants.FEATURE_SECURE_PROCESSING, true, e}); 064 } 065 try { 066 // Disable the external-general-entities by default 067 factory.setFeature("http://xml.org/sax/features/external-general-entities", false); 068 } catch (ParserConfigurationException e) { 069 LOG.warn("DocumentBuilderFactory doesn't support the feature {} with value {}, due to {}.", 070 new Object[] {"http://xml.org/sax/features/external-general-entities", false, e}); 071 } 072 // setup the SecurityManager by default if it's apache xerces 073 try { 074 Class<?> smClass = ObjectHelper.loadClass("org.apache.xerces.util.SecurityManager"); 075 if (smClass != null) { 076 Object sm = smClass.getDeclaredConstructor().newInstance(); 077 // Here we just use the default setting of the SeurityManager 078 factory.setAttribute("http://apache.org/xml/properties/security-manager", sm); 079 } 080 } catch (Exception e) { 081 LOG.warn("DocumentBuilderFactory doesn't support the attribute {}, due to {}.", new Object[] {"http://apache.org/xml/properties/security-manager", e}); 082 } 083 // setup the feature from the system property 084 setupFeatures(factory); 085 return factory; 086 } 087 088 protected void setupFeatures(DocumentBuilderFactory factory) { 089 Properties properties = System.getProperties(); 090 List<String> features = new ArrayList<>(); 091 for (Map.Entry<Object, Object> prop : properties.entrySet()) { 092 String key = (String)prop.getKey(); 093 if (key.startsWith(DOCUMENT_BUILDER_FACTORY_FEATURE)) { 094 String uri = StringHelper.after(key, ":"); 095 Boolean value = Boolean.valueOf((String)prop.getValue()); 096 try { 097 factory.setFeature(uri, value); 098 features.add("feature " + uri + " value " + value); 099 } catch (ParserConfigurationException e) { 100 LOG.warn("DocumentBuilderFactory doesn't support the feature {} with value {}, due to {}.", uri, value, e); 101 } 102 } 103 } 104 if (features.size() > 0) { 105 StringBuilder featureString = new StringBuilder(); 106 // just log the configured feature 107 for (String feature : features) { 108 if (featureString.length() != 0) { 109 featureString.append(", "); 110 } 111 featureString.append(feature); 112 } 113 LOG.info("DocumentBuilderFactory has been set with features {{}}.", featureString); 114 } 115 116 } 117 118 public Document createDocument() throws ParserConfigurationException { 119 DocumentBuilder builder = createDocumentBuilder(); 120 return builder.newDocument(); 121 } 122 123 public DocumentBuilder createDocumentBuilder(DocumentBuilderFactory factory) throws ParserConfigurationException { 124 DocumentBuilder builder = factory.newDocumentBuilder(); 125 builder.setErrorHandler(DOCUMENT_BUILDER_LOGGING_ERROR_HANDLER); 126 return builder; 127 } 128 129 public DocumentBuilder createDocumentBuilder() throws ParserConfigurationException { 130 return createDocumentBuilder(getDocumentBuilderFactory()); 131 } 132 133 public DocumentBuilderFactory getDocumentBuilderFactory() { 134 if (documentBuilderFactory == null) { 135 documentBuilderFactory = createDocumentBuilderFactory(); 136 } 137 return documentBuilderFactory; 138 } 139 140 141 private static class DocumentBuilderLoggingErrorHandler implements ErrorHandler { 142 143 @Override 144 public void warning(SAXParseException exception) throws SAXException { 145 LOG.warn(exception.getMessage(), exception); 146 } 147 148 @Override 149 public void error(SAXParseException exception) throws SAXException { 150 LOG.error(exception.getMessage(), exception); 151 } 152 153 @Override 154 public void fatalError(SAXParseException exception) throws SAXException { 155 LOG.error(exception.getMessage(), exception); 156 } 157 } 158}