package org.gomba;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import javax.servlet.GenericServlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Templates;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* Servlet implementation to be used for automatic webservice documentation.
*
* <p>
* The generated documentation exposes sensitive information about the
* implementation of the web service. It is suggested that you don't allow
* public access to this resource.
* </p>
*
* <dl>
* <dt>xslt</dt>
* <dd>The XSLT stylesheet to apply to the default XML output. (Optional)</dd>
* <dt>xslt-params</dt>
* <dd>XSLT parameters in Java Properties format. (Optional)</dd>
* </dl>
*
* @author Daniele Galdi
* @version $Id: DocumentationServlet.java,v 1.5 2005/07/20 16:49:56 flaviotordini Exp $
*/
public class DocumentationServlet extends GenericServlet {
private final static String DEFAULT_XSLT = "/org/gomba/documentationServlet.xslt";
/**
* The parsed XSLT stylesheet, if any.
*/
private Templates templates;
/**
* XSLT parameters.
*/
private Map xsltFixedParameters;
private HashMap validShowPath = new HashMap();
/**
* @see javax.servlet.Servlet#init(javax.servlet.ServletConfig)
*/
public void init(ServletConfig config) throws ServletException {
super.init(config);
InputStream is = getServletContext().getResourceAsStream("/WEB-INF/web.xml");
if (is != null) {
DocumentBuilder builder;
try {
builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document fileDoc = builder.parse(is);
NodeList list = fileDoc.getElementsByTagName("param-name");
for (int i=0; i <list.getLength(); i++) {
Node node = list.item(i);
Node child = node.getFirstChild();
if (child.getNodeValue().trim().equals("query")) {
Node parent = node.getParentNode();
NodeList children = parent.getChildNodes();
for (int j=0; j<children.getLength();j++) {
Node tmp = children.item(j);
if(tmp.getNodeName().equals("param-value")) {
String query = tmp.getFirstChild().getNodeValue().trim();
if (query.startsWith("/WEB-INF/")) {
validShowPath.put(query,query);
}
}
}
}
}
} catch (Exception e) {
throw new ServletException("Error during web.xml parsing", e);
}
}
// XSLT
final String xsltStyleSheet = config.getInitParameter("xslt");
if (xsltStyleSheet != null) {
// Create a templates object, which is the processed,
// thread-safe representation of the stylesheet.
is = getServletContext().getResourceAsStream(xsltStyleSheet);
} else {
is = DocumentationServlet.class.getResourceAsStream(DEFAULT_XSLT);
}
if (is == null) {
throw new ServletException("Cannot find stylesheet: " + xsltStyleSheet);
}
try {
TransformerFactory tfactory = TransformerFactory.newInstance();
Source xslSource = new StreamSource(is);
if (xsltStyleSheet != null) {
// Note that if we don't do this, relative URLs can not be
// resolved correctly!
xslSource.setSystemId(getServletContext().getRealPath(
xsltStyleSheet));
}
this.templates = tfactory.newTemplates(xslSource);
} catch (TransformerConfigurationException tce) {
throw new ServletException("Error parsing XSLT stylesheet: "
+ xsltStyleSheet, tce);
}
// create a map of fixed xslt parameters
final String xsltParams = config.getInitParameter("xslt-params");
if (xsltParams != null) {
try {
this.xsltFixedParameters = buildXsltFixedParams(xsltParams);
} catch (Exception e) {
throw new ServletException("Error parsing XSLT params: "
+ xsltParams, e);
}
}
}
private static Map buildXsltFixedParams(String xsltParams) throws IOException {
Properties parameters = new Properties();
InputStream inputStream = new ByteArrayInputStream(xsltParams.getBytes());
try {
parameters.load(inputStream);
} finally {
inputStream.close();
}
return parameters;
}
/**
* @see javax.servlet.Servlet#service(javax.servlet.ServletRequest,
* javax.servlet.ServletResponse)
*/
public void service(ServletRequest request, ServletResponse response)
throws ServletException, IOException {
boolean identity = false;
String show = request.getParameter("show");
if (show != null && !show.equals("/WEB-INF/web.xml") && validShowPath.containsKey(show)) identity = true;
else show = "/WEB-INF/web.xml";
try {
Transformer t;
if (!identity && this.templates != null) {
// Create a transformer using our stylesheet
t = this.templates.newTransformer();
// pass fixed XSLT parameters
if (this.xsltFixedParameters != null) {
for (Iterator i = this.xsltFixedParameters.entrySet()
.iterator(); i.hasNext();) {
Map.Entry mapEntry = (Map.Entry) i.next();
t.setParameter((String) mapEntry.getKey(), mapEntry
.getValue());
}
}
// TODO maybe we could also pass some dynamic values such as the
// request param or path info. But let's wait until the need
// arises...
} else {
// Create an "identity" transformer - copies input to output
t = TransformerFactory.newInstance().newTransformer();
}
// Set trasformation output properties
t.setOutputProperty(OutputKeys.ENCODING, response.getCharacterEncoding());
InputStream is = getServletContext().getResourceAsStream(show);
if (is != null) {
// Create the trasformation result
Result result = new StreamResult(response.getOutputStream());
String mediaType = this.templates.getOutputProperties().getProperty(OutputKeys.MEDIA_TYPE);
if (mediaType == null || identity) {
mediaType = "text/xml";
}
response.setContentType(mediaType);
// Go!
t.transform(new StreamSource(is), result);
}
} catch (TransformerException e) {
log("Error",e);
}
}
}