/* | |
* Copyright 2004 The Apache Software Foundation | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
package org.apache.pluto.util.assemble.file; | |
import org.apache.pluto.util.assemble.Assembler; | |
import org.apache.pluto.util.assemble.AssemblerConfig; | |
import org.apache.pluto.util.UtilityException; | |
import org.apache.pluto.descriptors.portlet.PortletAppDD; | |
import org.apache.pluto.descriptors.portlet.PortletDD; | |
import org.apache.pluto.descriptors.services.PortletAppDescriptorService; | |
import org.apache.pluto.descriptors.services.castor.EntityResolverImpl; | |
import org.apache.pluto.descriptors.services.castor.PortletAppDescriptorServiceImpl; | |
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 javax.xml.parsers.DocumentBuilder; | |
import javax.xml.parsers.DocumentBuilderFactory; | |
import javax.xml.parsers.ParserConfigurationException; | |
import javax.xml.transform.*; | |
import javax.xml.transform.dom.DOMSource; | |
import javax.xml.transform.stream.StreamResult; | |
import java.io.IOException; | |
import java.io.InputStream; | |
import java.io.OutputStream; | |
import java.io.FileInputStream; | |
import java.io.FileOutputStream; | |
import java.util.*; | |
/** | |
* | |
* @author <a href="mailto:ddewolf@apache.org">David H. DeWolf</a> | |
* @version 1.0 | |
* @since Nov 8, 2004 | |
*/ | |
public class FileAssembler implements Assembler { | |
/** The XML output properties. */ | |
private static final Properties PROPERTIES = new Properties(); | |
/** Element tagnames that may appear before servlet elements. */ | |
private static final Collection BEFORE_SERVLET_DEF = new ArrayList(); | |
/** Element tagnames that may appear before servlet-mapping elements. */ | |
private static final Collection BEFORE_MAPPING_DEF = new ArrayList(); | |
static { | |
// Initialize xml output properties. | |
PROPERTIES.setProperty(OutputKeys.INDENT, "yes"); | |
// Initialize BEFORE_SERVLET_DEF collection. | |
BEFORE_SERVLET_DEF.add("icon"); | |
BEFORE_SERVLET_DEF.add("display-name"); | |
BEFORE_SERVLET_DEF.add("description"); | |
BEFORE_SERVLET_DEF.add("distributable"); | |
BEFORE_SERVLET_DEF.add("context-param"); | |
BEFORE_SERVLET_DEF.add("filter"); | |
BEFORE_SERVLET_DEF.add("filter-mapping"); | |
BEFORE_SERVLET_DEF.add("listener"); | |
// initialize BEFORE_MAPPING_DEF collection. | |
BEFORE_MAPPING_DEF.addAll(BEFORE_SERVLET_DEF); | |
BEFORE_MAPPING_DEF.add("servlet"); | |
} | |
// Constructor ------------------------------------------------------------- | |
/** | |
* Default no-arg constructor. | |
*/ | |
public FileAssembler() { | |
// Do nothing. | |
} | |
// Assembler Impl ---------------------------------------------------------- | |
public void assemble(AssemblerConfig config) throws UtilityException { | |
try { | |
InputStream webXmlIn = new FileInputStream( | |
config.getWebappDescriptor()); | |
InputStream portletXmlIn = new FileInputStream( | |
config.getPortletDescriptor()); | |
Document xmlDoc = updateWebappDescriptor(webXmlIn, portletXmlIn); | |
webXmlIn.close(); | |
FileOutputStream webXmlOut = new FileOutputStream( | |
config.getDestination()); | |
save(xmlDoc, webXmlOut); | |
} catch (IOException ex) { | |
throw new UtilityException(ex.getMessage(), ex, null); | |
} | |
} | |
// Protected Methods ------------------------------------------------------- | |
/** | |
* Saves the XML document to the specified output stream. | |
* @param xmlDoc the XML document. | |
* @param out the output stream. | |
* @throws IOException if an error occurs. | |
*/ | |
protected void save(Document xmlDoc, OutputStream out) | |
throws IOException { | |
try { | |
TransformerFactory factory = TransformerFactory.newInstance(); | |
Transformer transformer = factory.newTransformer(); | |
transformer.setOutputProperties(PROPERTIES); | |
transformer.transform(new DOMSource(xmlDoc), | |
new StreamResult(out)); | |
} catch (TransformerConfigurationException ex) { | |
ex.printStackTrace(); | |
throw new IOException(ex.getMessage()); | |
} catch (TransformerException ex) { | |
ex.printStackTrace(); | |
throw new IOException(ex.getMessage()); | |
} finally { | |
out.flush(); | |
out.close(); | |
} | |
} | |
/** | |
* Updates the webapp descriptor by injecting portlet wrapper servlet | |
* definitions and mappings. | |
* | |
* TODO: currently we rely specifically on the castor implementation. | |
* | |
* @param webXmlIn input stream to the webapp descriptor. | |
* @param portletXmlIn input stream to the portlet app descriptor. | |
* @return the updated webapp descriptor XML document. | |
* @throws IOException | |
*/ | |
protected Document updateWebappDescriptor(InputStream webXmlIn, | |
InputStream portletXmlIn) | |
throws IOException { | |
Document webXmlDoc = parse(webXmlIn); | |
Collection servletElements = new ArrayList(); | |
Collection mappingElements = new ArrayList(); | |
PortletAppDescriptorService portletAppDescriptorService = | |
new PortletAppDescriptorServiceImpl(); | |
PortletAppDD portletAppDD = portletAppDescriptorService.read(portletXmlIn); | |
for (Iterator it = portletAppDD.getPortlets().iterator(); | |
it.hasNext(); ) { | |
// Read portlet definition. | |
PortletDD portlet = (PortletDD) it.next(); | |
String name = portlet.getPortletName(); | |
// Create servlet definition element. | |
Element servlet = webXmlDoc.createElement("servlet"); | |
Element servletName = webXmlDoc.createElement("servlet-name"); | |
servletName.appendChild(webXmlDoc.createTextNode(name)); | |
servlet.appendChild(servletName); | |
Element servletClass = webXmlDoc.createElement("servlet-class"); | |
servletClass.appendChild(webXmlDoc.createTextNode(DISPATCH_SERVLET_CLASS)); | |
servlet.appendChild(servletClass); | |
Element initParam = webXmlDoc.createElement("init-param"); | |
Element paramName = webXmlDoc.createElement("param-name"); | |
paramName.appendChild(webXmlDoc.createTextNode("portlet-name")); | |
Element paramValue = webXmlDoc.createElement("param-value"); | |
paramValue.appendChild(webXmlDoc.createTextNode(name)); | |
initParam.appendChild(paramName); | |
initParam.appendChild(paramValue); | |
servlet.appendChild(initParam); | |
Element load = webXmlDoc.createElement("load-on-startup"); | |
load.appendChild(webXmlDoc.createTextNode("1")); | |
servlet.appendChild(load); | |
// Create servlet mapping element. | |
Element mapping = webXmlDoc.createElement("servlet-mapping"); | |
servletName = webXmlDoc.createElement("servlet-name"); | |
servletName.appendChild(webXmlDoc.createTextNode(name)); | |
Element uri = webXmlDoc.createElement("url-pattern"); | |
uri.appendChild(webXmlDoc.createTextNode("/PlutoInvoker/"+name)); | |
mapping.appendChild(servletName); | |
mapping.appendChild(uri); | |
// Save servlet definition and servlet mapping. | |
servletElements.add(servlet); | |
mappingElements.add(mapping); | |
} | |
Element webAppNode = webXmlDoc.getDocumentElement(); | |
NodeList nodes = webAppNode.getChildNodes(); | |
// Find the first node that shouldn't be before the servlet and start | |
// appending. This is kind of ugly, but the hack works for now! | |
for (int i = 0; i < nodes.getLength(); i++) { | |
Node node = nodes.item(i); | |
if (node.getNodeType() == Node.ELEMENT_NODE) { | |
if (!BEFORE_SERVLET_DEF.contains(node.getNodeName())) { | |
for (Iterator it = servletElements.iterator(); | |
it.hasNext(); ) { | |
Node servlet = (Node) it.next(); | |
webAppNode.insertBefore(servlet, node); | |
it.remove(); | |
} | |
} | |
if(!BEFORE_MAPPING_DEF.contains(node.getNodeName())) { | |
for (Iterator it = mappingElements.iterator(); | |
it.hasNext(); ) { | |
Node mapping = (Node) it.next(); | |
webAppNode.insertBefore(mapping, node); | |
it.remove(); | |
} | |
} | |
} | |
} | |
// Now, in case there are not any nodes after the servlet def! | |
for (Iterator it = servletElements.iterator(); it.hasNext(); ) { | |
webAppNode.appendChild((Node)it.next()); | |
} | |
for (Iterator it = mappingElements.iterator(); it.hasNext(); ) { | |
webAppNode.appendChild((Node)it.next()); | |
} | |
// Return the updated web.xml document. | |
return webXmlDoc; | |
} | |
/** | |
* Parses an input stream of an XML file to an XML document. | |
* @param xmlIn the input stream of an XML file. | |
* @return the XML document. | |
* @throws IOException if an error occurs. | |
*/ | |
private Document parse(InputStream xmlIn) throws IOException { | |
Document xmlDoc = null; | |
try { | |
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); | |
DocumentBuilder builder = factory.newDocumentBuilder(); | |
builder.setEntityResolver(new EntityResolverImpl()); | |
xmlDoc = builder.parse(xmlIn); | |
} catch (ParserConfigurationException ex) { | |
throw new IOException(ex.getMessage()); | |
} catch (SAXException ex) { | |
throw new IOException(ex.getMessage()); | |
} | |
return xmlDoc; | |
} | |
} | |