Java tutorial
package nl.armatiek.xslweb.web.servlet; /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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. */ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PrintStream; import java.io.StringReader; import java.io.StringWriter; import java.io.Writer; import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.ListIterator; import java.util.Map; import java.util.Properties; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.xml.stream.XMLStreamWriter; import javax.xml.transform.ErrorListener; import javax.xml.transform.OutputKeys; import javax.xml.transform.Source; import javax.xml.transform.stream.StreamSource; import javax.xml.validation.Schema; import javax.xml.validation.Validator; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.io.output.ProxyWriter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import net.sf.saxon.om.NodeInfo; import net.sf.saxon.s9api.Destination; import net.sf.saxon.s9api.QName; import net.sf.saxon.s9api.Serializer; import net.sf.saxon.s9api.Serializer.Property; import net.sf.saxon.s9api.TeeDestination; import net.sf.saxon.s9api.XdmDestination; import net.sf.saxon.s9api.XdmNode; import net.sf.saxon.s9api.XdmValue; import net.sf.saxon.s9api.Xslt30Transformer; import net.sf.saxon.s9api.XsltExecutable; import net.sf.saxon.serialize.MessageWarner; import net.sf.saxon.stax.XMLStreamWriterDestination; import nl.armatiek.xslweb.configuration.Context; import nl.armatiek.xslweb.configuration.Definitions; import nl.armatiek.xslweb.configuration.WebApp; import nl.armatiek.xslweb.error.XSLWebException; import nl.armatiek.xslweb.pipeline.FopSerializerStep; import nl.armatiek.xslweb.pipeline.JSONSerializerStep; import nl.armatiek.xslweb.pipeline.PipelineHandler; import nl.armatiek.xslweb.pipeline.PipelineStep; import nl.armatiek.xslweb.pipeline.ResourceSerializerStep; import nl.armatiek.xslweb.pipeline.ResponseStep; import nl.armatiek.xslweb.pipeline.SchemaValidatorStep; import nl.armatiek.xslweb.pipeline.SchematronValidatorStep; import nl.armatiek.xslweb.pipeline.SerializerStep; import nl.armatiek.xslweb.pipeline.SystemTransformerStep; import nl.armatiek.xslweb.pipeline.TransformerStep; import nl.armatiek.xslweb.pipeline.ZipSerializerStep; import nl.armatiek.xslweb.saxon.destination.SourceDestination; import nl.armatiek.xslweb.saxon.destination.TeeSourceDestination; import nl.armatiek.xslweb.saxon.destination.XdmSourceDestination; import nl.armatiek.xslweb.saxon.errrorlistener.TransformationErrorListener; import nl.armatiek.xslweb.saxon.errrorlistener.ValidatorErrorHandler; import nl.armatiek.xslweb.utils.Closeable; import nl.armatiek.xslweb.utils.XSLWebUtils; import nl.armatiek.xslweb.xml.CleanupXMLStreamWriter; public class XSLWebServlet extends HttpServlet { private static final long serialVersionUID = 1L; private static final Logger logger = LoggerFactory.getLogger(XSLWebServlet.class); private File homeDir; public void init() throws ServletException { super.init(); try { homeDir = Context.getInstance().getHomeDir(); } catch (Exception e) { logger.error(e.getMessage()); throw new ServletException(e); } } @SuppressWarnings("unchecked") @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { OutputStream respOs = resp.getOutputStream(); WebApp webApp = null; try { webApp = (WebApp) req.getAttribute(Definitions.ATTRNAME_WEBAPP); if (webApp.isClosed()) { resp.resetBuffer(); resp.setStatus(HttpServletResponse.SC_SERVICE_UNAVAILABLE); resp.setContentType("text/html; charset=UTF-8"); Writer w = new OutputStreamWriter(respOs, "UTF-8"); w.write("<html><body><h1>Service temporarily unavailable</h1></body></html>"); return; } executeRequest(webApp, req, resp, respOs); } catch (Exception e) { logger.error(e.getMessage(), e); if (webApp != null && webApp.getDevelopmentMode()) { resp.setContentType("text/plain; charset=UTF-8"); e.printStackTrace(new PrintStream(respOs)); } else if (!resp.isCommitted()) { resp.resetBuffer(); resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); resp.setContentType("text/html; charset=UTF-8"); Writer w = new OutputStreamWriter(respOs, "UTF-8"); w.write("<html><body><h1>Internal Server Error</h1></body></html>"); } } finally { // Delete any registered temporary files: try { List<File> tempFiles = (List<File>) req.getAttribute(Definitions.ATTRNAME_TEMPFILES); if (tempFiles != null) { ListIterator<File> li = tempFiles.listIterator(); while (li.hasNext()) { File file; if ((file = li.next()) != null) { FileUtils.deleteQuietly(file); } } } } catch (Exception se) { logger.error("Error deleting registered temporary files", se); } // Close any closeables: try { List<Closeable> closeables = (List<Closeable>) req.getAttribute("xslweb-closeables"); if (closeables != null) { ListIterator<Closeable> li = closeables.listIterator(closeables.size()); while (li.hasPrevious()) { li.previous().close(); } } } catch (Exception se) { logger.error("Could not close Closeable", se); } } } private Destination getDestination(WebApp webApp, Destination destination, PipelineStep step) { if (webApp.getDevelopmentMode() && step.getLog()) { StringWriter sw = new StringWriter(); sw.write("----------\n"); sw.write("OUTPUT OF STEP: \"" + step.getName() + "\":\n"); Serializer debugSerializer = webApp.getProcessor().newSerializer(new ProxyWriter(sw) { @Override public void flush() throws IOException { logger.debug(out.toString()); } }); debugSerializer.setOutputProperty(Serializer.Property.METHOD, "xml"); debugSerializer.setOutputProperty(Serializer.Property.INDENT, "yes"); if (destination instanceof XdmDestination) { destination = new TeeSourceDestination((XdmDestination) destination, debugSerializer); } else { destination = new TeeDestination(destination, debugSerializer); } } return destination; } private Properties getOutputProperties(WebApp webApp, ErrorListener errorListener, List<PipelineStep> steps) throws Exception { for (int i = steps.size() - 1; i >= 0; i--) { PipelineStep step = steps.get(i); if (step instanceof TransformerStep) { String xslPath = ((TransformerStep) step).getXslPath(); XsltExecutable executable = webApp.getTemplates(xslPath, errorListener); return executable.getUnderlyingCompiledStylesheet().getOutputProperties(); } } return null; } private void addResponseTransformationStep(List<PipelineStep> steps) { SystemTransformerStep step = new SystemTransformerStep("system/response/response.xsl", "client-response", false); if (steps.get(steps.size() - 1) instanceof SerializerStep) { steps.add(steps.size() - 1, step); } else { steps.add(step); } } private Destination getDestination(WebApp webApp, HttpServletRequest req, HttpServletResponse resp, OutputStream os, Properties outputProperties, PipelineStep currentStep, PipelineStep nextStep) throws Exception { Destination dest; if (nextStep == null) { Serializer serializer = webApp.getProcessor().newSerializer(os); if (outputProperties != null) { for (String key : outputProperties.stringPropertyNames()) { String value = outputProperties.getProperty(key); if (key.equals(OutputKeys.CDATA_SECTION_ELEMENTS)) { value = value.replaceAll("\\{\\}", "{''}"); } Property prop = Property.get(key); if (prop == null) { continue; } serializer.setOutputProperty(prop, value); } } XMLStreamWriter xsw = new CleanupXMLStreamWriter(serializer.getXMLStreamWriter()); dest = new XMLStreamWriterDestination(xsw); } else if (nextStep instanceof TransformerStep || nextStep instanceof SchemaValidatorStep) { dest = new XdmSourceDestination(); } else if (nextStep instanceof JSONSerializerStep) { dest = ((JSONSerializerStep) nextStep).getDestination(webApp, req, resp, os, outputProperties); } else if (nextStep instanceof ZipSerializerStep) { dest = ((ZipSerializerStep) nextStep).getDestination(webApp, req, resp, os, outputProperties); } else if (nextStep instanceof ResourceSerializerStep) { dest = ((ResourceSerializerStep) nextStep).getDestination(webApp, req, resp, os, outputProperties); } else if (nextStep instanceof FopSerializerStep) { dest = ((FopSerializerStep) nextStep).getDestination(webApp, req, resp, os, outputProperties); } else { throw new XSLWebException("Could not determine destination"); } return getDestination(webApp, dest, currentStep); } private Source makeNodeInfoSource(Source source, WebApp webApp, ErrorListener errorListener) throws Exception { if (source instanceof NodeInfo) { return source; } Xslt30Transformer identityTransformer = webApp .getTemplates(new File(homeDir, "common/xsl/system/identity/identity.xsl").getAbsolutePath(), errorListener) .load30(); XdmDestination dest = new XdmDestination(); identityTransformer.applyTemplates(source, dest); return dest.getXdmNode().asSource(); } private void executeRequest(WebApp webApp, HttpServletRequest req, HttpServletResponse resp, OutputStream respOs) throws Exception { boolean developmentMode = webApp.getDevelopmentMode(); String requestXML = (String) req.getAttribute(Definitions.ATTRNAME_REQUESTXML); PipelineHandler pipelineHandler = (PipelineHandler) req.getAttribute(Definitions.ATTRNAME_PIPELINEHANDLER); ErrorListener errorListener = new TransformationErrorListener(resp, developmentMode); MessageWarner messageWarner = new MessageWarner(); List<PipelineStep> steps = pipelineHandler.getPipelineSteps(); if (steps == null || steps.isEmpty()) { resp.sendError(HttpServletResponse.SC_NOT_FOUND, "Resource not found"); return; } OutputStream os = (developmentMode) ? new ByteArrayOutputStream() : respOs; Properties outputProperties = getOutputProperties(webApp, errorListener, steps); Map<QName, XdmValue> baseStylesheetParameters = XSLWebUtils.getStylesheetParameters(webApp, req, resp, homeDir); addResponseTransformationStep(steps); Map<QName, XdmValue> extraStylesheetParameters = null; Source source = new StreamSource(new StringReader(requestXML)); Destination destination = null; for (int i = 0; i < steps.size(); i++) { PipelineStep step = steps.get(i); if (step instanceof SerializerStep) { break; } PipelineStep nextStep = (i < steps.size() - 1) ? steps.get(i + 1) : null; if (step instanceof TransformerStep) { String xslPath = null; if (step instanceof SystemTransformerStep) { xslPath = new File(homeDir, "common/xsl/" + ((TransformerStep) step).getXslPath()) .getAbsolutePath(); } else { xslPath = ((TransformerStep) step).getXslPath(); } XsltExecutable templates = webApp.getTemplates(xslPath, errorListener); Xslt30Transformer transformer = templates.load30(); transformer.getUnderlyingController().setMessageEmitter(messageWarner); transformer.setErrorListener(errorListener); Map<QName, XdmValue> stylesheetParameters = new HashMap<QName, XdmValue>(); stylesheetParameters.putAll(baseStylesheetParameters); XSLWebUtils.addStylesheetParameters(stylesheetParameters, ((TransformerStep) step).getParameters()); if (extraStylesheetParameters != null) { stylesheetParameters.putAll(extraStylesheetParameters); extraStylesheetParameters.clear(); } transformer.setStylesheetParameters(stylesheetParameters); destination = getDestination(webApp, req, resp, os, outputProperties, step, nextStep); transformer.applyTemplates(source, destination); } else if (step instanceof SchemaValidatorStep) { SchemaValidatorStep svStep = (SchemaValidatorStep) step; List<String> schemaPaths = svStep.getSchemaPaths(); Schema schema = webApp.getSchema(schemaPaths, errorListener); source = makeNodeInfoSource(source, webApp, errorListener); destination = null; Serializer serializer = webApp.getProcessor().newSerializer(); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); serializer.setOutputStream(outputStream); serializer.setOutputProperty(Property.INDENT, "yes"); serializer.serializeNode(new XdmNode((NodeInfo) source)); Source validationSource = new StreamSource(new ByteArrayInputStream(outputStream.toByteArray())); Validator validator = schema.newValidator(); ValidatorErrorHandler errorHandler = new ValidatorErrorHandler( "Step: " + ((step.getName() != null) ? step.getName() : "noname")); validator.setErrorHandler(errorHandler); Properties properties = svStep.getProperties(); if (properties != null) { @SuppressWarnings("rawtypes") Enumeration names = properties.propertyNames(); while (names.hasMoreElements()) { String name = (String) names.nextElement(); validator.setProperty(name, properties.getProperty(name)); } } Properties features = svStep.getProperties(); if (features != null) { @SuppressWarnings("rawtypes") Enumeration names = features.propertyNames(); while (names.hasMoreElements()) { String name = (String) names.nextElement(); validator.setProperty(name, features.getProperty(name)); } } validator.validate(validationSource); Source resultsSource = errorHandler.getValidationResults(); if (resultsSource != null) { NodeInfo resultsNodeInfo = webApp.getConfiguration().buildDocumentTree(resultsSource) .getRootNode(); String xslParamName = svStep.getXslParamName(); if (xslParamName != null) { if (extraStylesheetParameters == null) { extraStylesheetParameters = new HashMap<QName, XdmValue>(); } extraStylesheetParameters.put(new QName(svStep.getXslParamNamespace(), xslParamName), new XdmNode(resultsNodeInfo)); } } } else if (step instanceof SchematronValidatorStep) { SchematronValidatorStep svStep = (SchematronValidatorStep) step; source = makeNodeInfoSource(source, webApp, errorListener); destination = null; /* Execute schematron validation */ XsltExecutable templates = webApp.getSchematron(svStep.getSchematronPath(), svStep.getPhase(), errorListener); Xslt30Transformer transformer = templates.load30(); transformer.getUnderlyingController().setMessageEmitter(messageWarner); transformer.setErrorListener(errorListener); XdmDestination svrlDest = new XdmDestination(); transformer.applyTemplates(source, svrlDest); String xslParamName = svStep.getXslParamName(); if (xslParamName != null) { if (extraStylesheetParameters == null) { extraStylesheetParameters = new HashMap<QName, XdmValue>(); } extraStylesheetParameters.put(new QName(svStep.getXslParamNamespace(), xslParamName), svrlDest.getXdmNode()); } } else if (step instanceof ResponseStep) { source = new StreamSource(new StringReader(((ResponseStep) step).getResponse())); continue; } if (destination instanceof SourceDestination) { /* Set source for next pipeline step: */ source = ((SourceDestination) destination).asSource(); } } if (developmentMode) { byte[] body = ((ByteArrayOutputStream) os).toByteArray(); IOUtils.copy(new ByteArrayInputStream(body), respOs); } } }