All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.xins.server.XSLTCallingConvention Maven / Gradle / Ivy

There is a newer version: 3.0
Show newest version
/*
 * $Id: XSLTCallingConvention.java,v 1.61 2011/03/19 09:11:18 agoubard Exp $
 *
 * See the COPYRIGHT file for redistribution and use restrictions.
 */
package org.xins.server;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Templates;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.xins.common.Utils;
import org.xins.common.collections.InvalidPropertyValueException;
import org.xins.common.collections.MissingRequiredPropertyException;
import org.xins.common.manageable.InitializationException;
import org.xins.common.text.TextUtils;

/**
 * XSLT calling convention.
 * The XSLT calling convention input is the same as for the standard calling
 * convention. The XSLT calling convention output is the result of the XML
 * normally returned by the standard calling convention and the specified
 * XSLT.
 * The Mime type of the return data can be specified in the XSLT using the
 * media-type or method attribute of the XSL output element.
 * More information about the XSLT calling convention can be found in the
 * user guide.
 *
 * @version $Revision: 1.61 $ $Date: 2011/03/19 09:11:18 $
 * @author Anthony Goubard
 * @author Ernst de Haan
 */
public class XSLTCallingConvention extends StandardCallingConvention {

   /**
    * The name of the runtime property that defines if the templates should be
    * cached. Should be either "true" or "false".
    * By default the cache is enabled.
    */
   protected static final String TEMPLATES_CACHE_PROPERTY = "templates.cache";

   /**
    * The name of the input parameter that specifies the location of the XSLT
    * template to use.
    */
   protected static final String TEMPLATE_PARAMETER = "_template";

   /**
    * The name of the input parameter used to clear the template cache.
    */
   protected static final String CLEAR_TEMPLATE_CACHE_PARAMETER = "_cleartemplatecache";

   /**
    * The XSLT transformer. Never null.
    */
   private final TransformerFactory _factory;

   /**
    * Flag that indicates whether the templates should be cached. This field
    * is set during initialization.
    */
   private boolean _cacheTemplates;

   /**
    * The prefix to use for the _template parameter.
    * This field is set during initialization.
    * If the value is null the _template parameter is not allowed.
    */
   private String _templatesPrefix;

   /**
    * Location of the XSLT templates. This field is initially
    * null and set during initialization.
    */
   private String _location;

   /**
    * Cache for the XSLT templates. Never null.
    */
   private Map _templateCache;

   /**
    * Constructs a new XSLTCallingConvention object.
    */
   public XSLTCallingConvention() {

      // Create the transformer factory
      _factory = TransformerFactory.newInstance();

      // Initialize the template cache
      _templateCache = new HashMap(89);
   }

   protected void initImpl(Map runtimeProperties)
   throws MissingRequiredPropertyException,
          InvalidPropertyValueException,
          InitializationException {

      // Determine if the template cache should be enabled
      String cacheEnabled = runtimeProperties.get(TEMPLATES_CACHE_PROPERTY);
      initCacheEnabled(cacheEnabled);

      // Get the base directory of the style sheets.
      _location = getXSLTLocation(runtimeProperties, "source");

      // Determine whether template location can be passed as parameter
      _templatesPrefix = getXSLTLocation(runtimeProperties, "parameter.prefix");
   }

   /**
    * Determines if the template cache should be enabled. If no value is
    * passed, then by default the cache is enabled. An invalid value, however,
    * will trigger an {@link InvalidPropertyValueException}.
    *
    * @param cacheEnabled
    *    the value of the runtime property that specifies whether the cache
    *    should be enabled, can be null.
    *
    * @throws InvalidPropertyValueException
    *    if the value is incorrect.
    */
   private void initCacheEnabled(String cacheEnabled)
   throws InvalidPropertyValueException {

      // By default, the template cache is enabled
      if (TextUtils.isEmpty(cacheEnabled)) {
         _cacheTemplates = true;

      // Trim before comparing with 'true' and 'false'
      } else {
         cacheEnabled = cacheEnabled.trim();
         if ("true".equals(cacheEnabled)) {
            _cacheTemplates = true;
         } else if ("false".equals(cacheEnabled)) {
            _cacheTemplates = false;
         } else {
            throw new InvalidPropertyValueException(TEMPLATES_CACHE_PROPERTY,
               cacheEnabled, "Expected either \"true\" or \"false\".");
         }
      }

      // Log whether the cache is enabled or not
      if (_cacheTemplates) {
         Log.log_3440();
      } else {
         Log.log_3441();
      }
   }

   /**
    * Initializes the location for the XSLT templates.
    *
    * The name of the runtime property that defines the location of the XSLT
    * templates should indicate a directory, either locally or remotely.
    * Local locations will be interpreted as relative to the user home
    * directory. The value should be a URL or a relative directory.
    *
    * 

Examples of valid locations include: * *

    *
  • projects/dubey/xslt/
  • *
  • file:///home/john.doe/projects/dubey/xslt/
  • *
  • http://johndoe.com/projects/dubey/xslt/
  • *
  • https://xslt.johndoe.com/
  • *
  • http://xslt.mycompany.com/myapi/
  • *
  • file:///c:/home/
  • *
* *

XSLT template files must match the names of the corresponding * functions. * * @param runtimeProperties * the runtime properties, cannot be null. * * @param propertySuffix * the suffix of the runtime property we're looking for, cannot be null. * * @return * the path location where to find the XSLT style sheet files or null * if no location is specified. */ private String getXSLTLocation(Map runtimeProperties, String propertySuffix) { // Get the value of the property String templatesProperty = "templates." + getAPI().getName() + ".xins-xslt." + propertySuffix; String location = runtimeProperties.get(templatesProperty); if (TextUtils.isEmpty(location)) { return null; } // If the value is not a URL, it's considered as a relative path. // Relative URLs use the user directory as base dir. if (location.indexOf("://") == -1) { // Attempt to convert the home directory to a URL String home = System.getProperty("user.dir"); String homeURL = ""; try { homeURL = new File(home).toURL().toString(); // If the conversion to a URL failed, then just use the original } catch (IOException exception) { Utils.logIgnoredException(exception); } // Prepend the home directory URL location = homeURL + location; } // Log the base directory for XSLT templates Log.log_3442(getAPI().getName(), propertySuffix, location); return location; } @Override protected FunctionRequest convertRequestImpl(HttpServletRequest httpRequest) throws InvalidRequestException, FunctionNotSpecifiedException { FunctionRequest request = super.convertRequestImpl(httpRequest); // We also need to store a few properties in the backpack Map backpack = request.getBackpack(); backpack.put(CLEAR_TEMPLATE_CACHE_PARAMETER, "true".equals(httpRequest.getParameter(CLEAR_TEMPLATE_CACHE_PARAMETER))); backpack.put(TEMPLATE_PARAMETER, httpRequest.getParameter(TEMPLATE_PARAMETER)); backpack.put(BackpackConstants.FUNCTION_NAME, httpRequest.getParameter("_function")); return request; } protected void convertResultImpl(FunctionResult xinsResult, HttpServletResponse httpResponse, Map backpack) throws IOException { // If the request is to clear the cache, just clear the cache. if ((Boolean) backpack.get(CLEAR_TEMPLATE_CACHE_PARAMETER)) { _templateCache.clear(); PrintWriter out = httpResponse.getWriter(); out.write("Done."); out.close(); return; } // Get the XML output similar to the standard calling convention. StringWriter xmlOutput = new StringWriter(1024); CallResultOutputter.output(xmlOutput, xinsResult); xmlOutput.close(); // Get the location of the XSLT file. String xsltLocation = null; String templatesSuffix = (String) backpack.get(TEMPLATE_PARAMETER); if (_templatesPrefix != null && templatesSuffix != null) { if (templatesSuffix.indexOf("..") != -1) { throw new IOException("Incorrect " + TEMPLATE_PARAMETER + " parameter: " + templatesSuffix); } xsltLocation = _templatesPrefix + templatesSuffix; } if (_templatesPrefix == null && templatesSuffix != null) { throw new IOException(TEMPLATE_PARAMETER + " parameter not allowed."); } if (xsltLocation == null) { if (_location == null) { throw new IOException("No location specified for the XSLT stylesheets."); } xsltLocation = _location + backpack.get(BackpackConstants.FUNCTION_NAME) + ".xslt"; } try { // Load the template or get it from the cache. Templates templates; if (_cacheTemplates && _templateCache.containsKey(xsltLocation)) { templates = (Templates) _templateCache.get(xsltLocation); } else { Log.log_3443(xsltLocation); templates = _factory.newTemplates(new StreamSource(xsltLocation)); if (_cacheTemplates) { _templateCache.put(xsltLocation, templates); } } // Proceed to the transformation. Transformer xformer = templates.newTransformer(); Source source = new StreamSource(new StringReader(xmlOutput.toString())); Writer buffer = new StringWriter(4096); Result result = new StreamResult(buffer); xformer.transform(source, result); // Determine the MIME type for the output. String mimeType = getContentType(templates.getOutputProperties()); if (mimeType != null) { httpResponse.setContentType(mimeType); } httpResponse.setStatus(HttpServletResponse.SC_OK); PrintWriter out = httpResponse.getWriter(); out.print(buffer.toString()); out.close(); } catch (Exception exception) { if (exception instanceof IOException) { throw (IOException) exception; } else { String message = "Cannot transform the result with the XSLT " + "located at \"" + xsltLocation + "\"."; IOException ioe = new IOException(message); ioe.initCause(exception); throw ioe; } } } /** * Gets the MIME type and the character encoding to return for the HTTP response. * * @param outputProperties * the output properties defined in the XSLT, never null. * * @return * the content type, never null. */ private String getContentType(Properties outputProperties) { String mimeType = outputProperties.getProperty("media-type"); if (mimeType == null) { String method = outputProperties.getProperty("method"); if ("xml".equals(method)) { mimeType = "text/xml"; } else if ("html".equals(method)) { mimeType = "text/html"; } else if ("text".equals(method)) { mimeType = "text/plain"; } } String encoding = outputProperties.getProperty("encoding"); if (mimeType != null && encoding != null) { mimeType += "; charset=" + encoding; } return mimeType; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy