/*! * This program is free software; you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software * Foundation. * * You should have received a copy of the GNU Lesser General Public License along with this * program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html * or from the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Lesser General Public License for more details. * * Copyright (c) 2002-2016 Pentaho Corporation.. All rights reserved. */ package org.pentaho.platform.web.http.context; import com.google.common.annotations.VisibleForTesting; import org.pentaho.di.core.KettleClientEnvironment; import org.pentaho.di.core.KettleClientEnvironment.ClientType; import org.pentaho.platform.util.xml.XMLParserFactoryProducer; import org.pentaho.platform.web.http.PentahoHttpSessionHelper; import org.springframework.core.io.FileSystemResource; import org.springframework.core.io.Resource; import org.springframework.web.context.support.XmlWebApplicationContext; import org.w3c.dom.Comment; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.text.MessageFormat; import java.util.Iterator; import javax.xml.namespace.NamespaceContext; import javax.xml.namespace.QName; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; /** * Overrides <code>getResourceByPath</code> so that relative paths are relative to the Pentaho solution repository's * system directory instead of being relative to servlet context root. * * @author mlowery */ public class PentahoSolutionSpringApplicationContext extends XmlWebApplicationContext { private static final String DEFAULT_NAMESPASE = "ns"; private static final String SYSTEM_FOLDER = "system"; private static final String DEFAULT_SPRING_XML = "pentaho-spring-beans.xml"; private static final String IMPORT_TAG = "import"; private static final String RESOURCE_ATTR = "resource"; private static final String RESOURCE = "importExport.xml"; private static final String IMPORT_COMMENT = "Import \"{0}\" was added by \"{1}\" class automatically"; private static final XPathFactory XPATH_FACTORY = XPathFactory.newInstance(); protected Resource getResourceByPath( String path ) { Resource resource = null; String solutionPath = PentahoHttpSessionHelper.getSolutionPath( getServletContext() ); if ( solutionPath != null ) { File file = new File( solutionPath + File.separator + SYSTEM_FOLDER + File.separator + path ); //$NON-NLS-1$ resource = new FileSystemResource( file ); } else { resource = super.getResourceByPath( path ); } ClientType clientType = null; // We need to check if we are running in spoon. For that we need to get the kettle client type if ( KettleClientEnvironment.isInitialized( ) && KettleClientEnvironment.getInstance() != null ) { clientType = KettleClientEnvironment.getInstance().getClient(); } // If the client type is spoon then we will skip adding the xml file if ( path.toLowerCase().endsWith( DEFAULT_SPRING_XML ) && ( clientType == null || !clientType.equals( ClientType.SPOON ) ) ) { try { Document doc = getResourceDocument( resource.getInputStream() ); Node node = doc.getDocumentElement(); NodeList nodes = evaluateXPath( node, MessageFormat.format( "./{0}:{1}[@{2}=''{3}'']", DEFAULT_NAMESPASE, IMPORT_TAG, RESOURCE_ATTR, RESOURCE ), XPathConstants.NODESET ); if ( nodes.getLength() > 0 ) { return resource; } Element importEl = doc.createElementNS( node.getNamespaceURI(), IMPORT_TAG ); importEl.setAttribute( RESOURCE_ATTR, RESOURCE ); Comment comment = doc.createComment( MessageFormat.format( IMPORT_COMMENT, RESOURCE, this.getClass().getSimpleName() ) ); addLineBreak( node ); node.appendChild( comment ); addLineBreak( node ); node.appendChild( importEl ); addLineBreak( node ); TransformerFactory tFactory = TransformerFactory.newInstance(); Transformer transformer = tFactory.newTransformer(); DOMSource source = new DOMSource( doc ); FileOutputStream out = new FileOutputStream( resource.getFile() ); try { StreamResult result = new StreamResult( out ); transformer.transform( source, result ); } finally { out.flush(); out.close(); } } catch ( Exception e ) { e.printStackTrace(); } } return resource; } private void addLineBreak( Node node ) { node.appendChild( node.getOwnerDocument().createTextNode( "\n" ) ); } @SuppressWarnings( "unchecked" ) private <T> T evaluateXPath( Node node, String exp, QName res ) throws XPathExpressionException { XPath xpath = XPATH_FACTORY.newXPath(); xpath.setNamespaceContext( new DefNamespaceContext( node.getNamespaceURI() ) ); return (T) xpath.compile( exp ).evaluate( node, res ); } private static class DefNamespaceContext implements NamespaceContext { String defaultNS; public DefNamespaceContext( String defaultNS ) { this.defaultNS = defaultNS; } @Override public String getNamespaceURI( String prefix ) { if ( DEFAULT_NAMESPASE.equals( prefix ) ) { return defaultNS; } return null; } @Override public String getPrefix( String namespaceURI ) { return null; } @Override public Iterator<?> getPrefixes( String namespaceURI ) { return null; } } @VisibleForTesting Document getResourceDocument( InputStream is ) throws ParserConfigurationException, SAXException, IOException { DocumentBuilderFactory dFactory = XMLParserFactoryProducer.createSecureDocBuilderFactory(); dFactory.setNamespaceAware( true ); DocumentBuilder dBuilder = dFactory.newDocumentBuilder(); return dBuilder.parse( is ); } }