blob: c3a97cf4c8bee4d6101b66b15c15c7e43069fa6f [file] [log] [blame]
/*
* 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;
}
}