blob: 41ff9f9a29aac39c09fd597773766ddec9017b89 [file] [log] [blame]
/*
* 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.
*/
package org.apache.felix.mosgi.jmx.httpconnector.mx4j.tools.adaptor.http;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.util.StringTokenizer;
import javax.management.MBeanException;
import javax.management.ReflectionException;
import javax.management.RuntimeMBeanException;
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.URIResolver;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import org.w3c.dom.Document;
import org.osgi.framework.ServiceReference;
import org.osgi.service.log.LogService;
import org.apache.felix.mosgi.jmx.httpconnector.HttpConnectorActivator;
/**
* XSLTPostProcessor pass the document through an XSLT transformation
*
* @author <a href="mailto:tibu@users.sourceforge.net">Carlos Quiroz</a>
* @version $Revision: 1.1.1.1 $
* the HttpAdaptor through a XSL transformation" extends="mx4j.tools.adaptor.http.ProcessorMBean"
*/
public class XSLTProcessor
implements ProcessorMBean, XSLTProcessorMBean, URIResolver
{
TransformerFactory factory = null;
private Map templatesCache = new HashMap();
private String path = "mx4j/tools/adaptor/http/xsl";
private File root = null;
private Map mimeTypes = new HashMap();
/** Indicated whether the file are read from a file */
private boolean useJar = true;
private boolean useCache = true;
private ClassLoader targetClassLoader = ClassLoader.getSystemClassLoader();
private String defaultPage = "serverbydomain";
/**
* The locale is set with the default as en_US since it is the
* one bundled
*/
private Locale locale = new Locale("en", "");
public XSLTProcessor()
{
factory = TransformerFactory.newInstance();
factory.setURIResolver(this);
mimeTypes.put(".gif", "image/gif");
mimeTypes.put(".jpg", "image/jpg");
mimeTypes.put(".png", "image/png");
mimeTypes.put(".tif", "image/tiff");
mimeTypes.put(".tiff", "image/tiff");
mimeTypes.put(".ico", "image/ico");
mimeTypes.put(".html", "text/html");
mimeTypes.put(".htm", "text/html");
mimeTypes.put(".txt", "text/plain");
mimeTypes.put(".xml", "text/xml");
mimeTypes.put(".xsl", "text/xsl");
mimeTypes.put(".css", "text/css");
mimeTypes.put(".js", "text/x-javascript");
mimeTypes.put(".jar", "application/java-archive");
}
public void writeResponse(HttpOutputStream out, HttpInputStream in, Document document) throws IOException
{
out.setCode(HttpConstants.STATUS_OKAY);
out.setHeader("Content-Type", "text/html");
// added some caching attributes to fornce not to cache
out.setHeader("Cache-Control", "no-cache");
out.setHeader("expires", "now");
out.setHeader("pragma", "no-cache");
out.sendHeaders();
Transformer transformer = null;
String path = preProcess(in.getPath());
if (in.getVariable("template") != null)
{
transformer = createTransformer(in.getVariable("template") + ".xsl");
}
else
{
transformer = createTransformer(path + ".xsl");
}
if (transformer != null)
{
// added so that the document() function works
transformer.setURIResolver(this);
// The variables are passed to the XSLT as (param.name, value)
Map variables = in.getVariables();
Iterator j = variables.keySet().iterator();
while (j.hasNext())
{
String key = (String)j.next();
Object value = variables.get(key);
if (value instanceof String) {
transformer.setParameter("request." + key, value);
}
if (value instanceof String[]) {
String[] allvalues = (String[])value;
// not a good solution, only the first one is presented
transformer.setParameter("request." + key, allvalues[0]);
}
}
if (!variables.containsKey("locale"))
{
transformer.setParameter("request.locale", locale.toString());
}
try
{
ByteArrayOutputStream output = new ByteArrayOutputStream();
XSLTProcessor.log(LogService.LOG_DEBUG,"transforming " + path,null);
transformer.transform(new DOMSource(document), new StreamResult(output));
output.writeTo(out);
}
catch (TransformerException e)
{
XSLTProcessor.log(LogService.LOG_ERROR,"Transformation exception ", e);
}
}
else
{
XSLTProcessor.log(LogService.LOG_WARNING,"Transformer for path " + path + " not found",null);
}
}
protected Transformer createTransformer(String path)
{
Transformer transformer = null;
try
{
if (useCache && templatesCache.containsKey(path))
{
transformer = ((Templates)templatesCache.get(path)).newTransformer();
}
else
{
InputStream file = getInputStream(path);
if (file != null)
{
XSLTProcessor.log(LogService.LOG_INFO,"Creating template for path "+path, null);
Templates template = factory.newTemplates(new StreamSource(file));
transformer = template.newTransformer();
if (useCache)
{
templatesCache.put(path, template);
}
}
else
{
XSLTProcessor.log(LogService.LOG_WARNING,"template for path "+path+" not found", null);
}
}
}
catch (TransformerConfigurationException e)
{
XSLTProcessor.log(LogService.LOG_ERROR,"Exception during template construction", e);
}
return transformer;
}
protected void processHttpException(HttpInputStream in, HttpOutputStream out, HttpException e) throws IOException
{
out.setCode(e.getCode());
out.setHeader("Content-Type", "text/html");
out.sendHeaders();
// hardcoded dir :-P
Transformer transformer = createTransformer("error.xsl");
transformer.setURIResolver(this);
Document doc = e.getResponseDoc();
if (doc != null)
{
try
{
if (!in.getVariables().containsKey("locale"))
{
transformer.setParameter("request.locale", locale.toString());
}
ByteArrayOutputStream output = new ByteArrayOutputStream();
transformer.transform(new DOMSource(doc), new StreamResult(output));
output.writeTo(out);
}
catch (TransformerException ex)
{
XSLTProcessor.log(LogService.LOG_ERROR,"Exception during error output", ex);
}
}
}
public void writeError(HttpOutputStream out, HttpInputStream in, Exception e) throws IOException
{
Exception t = e;
if (e instanceof RuntimeMBeanException)
{
t = ((RuntimeMBeanException)e).getTargetException();
}
XSLTProcessor.log(LogService.LOG_INFO,"Processing error " + t.getMessage(),null);
if (t instanceof HttpException)
{
processHttpException(in, out, (HttpException)e);
}
else if ((t instanceof MBeanException) && (((MBeanException)t).getTargetException() instanceof HttpException))
{
processHttpException(in, out, (HttpException)((MBeanException)t).getTargetException());
}
else if ((t instanceof ReflectionException) && (((ReflectionException)t).getTargetException() instanceof HttpException))
{
processHttpException(in, out, (HttpException)((ReflectionException)t).getTargetException());
}
else
{
out.setCode(HttpConstants.STATUS_INTERNAL_ERROR);
out.setHeader("Content-Type", "text/html");
out.sendHeaders();
}
}
public String preProcess(String path) {
if (path.equals("/"))
{
path = "/" + defaultPage;
}
return path;
}
public String notFoundElement(String path, HttpOutputStream out, HttpInputStream in) throws IOException, HttpException
{
File file = new File(this.path, path);
XSLTProcessor.log(LogService.LOG_INFO,"Processing file request " + file,null);
String name = file.getName();
int extensionIndex = name.lastIndexOf('.');
String mime = null;
if (extensionIndex < 0)
{
XSLTProcessor.log(LogService.LOG_WARNING,"Filename has no extensions " + file.toString(),null);
mime = "text/plain";
}
else
{
String extension = name.substring(extensionIndex, name.length());
if (mimeTypes.containsKey(extension))
{
mime = (String)mimeTypes.get(extension);
} else {
XSLTProcessor.log(LogService.LOG_WARNING,"MIME type not found " + extension,null);
mime = "text/plain";
}
}
try
{
XSLTProcessor.log(LogService.LOG_DEBUG,"Trying to read file " + file,null);
BufferedInputStream fileIn = new BufferedInputStream(getInputStream(path));
ByteArrayOutputStream outArray = new ByteArrayOutputStream();
BufferedOutputStream outBuffer = new BufferedOutputStream(outArray);
int piece = 0;
while ((piece = fileIn.read()) >= 0)
{
outBuffer.write(piece);
}
outBuffer.flush();
out.setCode(HttpConstants.STATUS_OKAY);
out.setHeader("Content-type", mime);
out.sendHeaders();
XSLTProcessor.log(LogService.LOG_DEBUG,"File output " + mime,null);
outArray.writeTo(out);
fileIn.close();
}
catch (Exception e)
{
XSLTProcessor.log(LogService.LOG_WARNING,"Exception loading file " + file, e);
throw new HttpException(HttpConstants.STATUS_NOT_FOUND, "file " + file + " not found");
}
return null;
}
protected InputStream getInputStream(String path)
{
InputStream file = null;
if (!useJar)
{
try
{
// load from a dir
file = new FileInputStream(new File(this.root, path));
}
catch (FileNotFoundException e)
{
XSLTProcessor.log(LogService.LOG_ERROR,"File not found", e);
}
}
else
{
// load from a jar
String targetFile = this.path;
// workaround, should tought of somehting better
if (path.startsWith("/"))
{
targetFile += path;
} else {
targetFile += "/" + path;
}
if (root != null)
{
file = targetClassLoader.getResourceAsStream(targetFile);
}
if (file == null)
{
ClassLoader cl=getClass().getClassLoader();
if (cl == null)
{
file = ClassLoader.getSystemClassLoader().getResourceAsStream(targetFile);
}
else
{
file = getClass().getClassLoader().getResourceAsStream(targetFile);
}
file = getClass().getClassLoader().getResourceAsStream(targetFile);
}
}
return file;
}
public Source resolve(String href, String base)
{
StreamSource source = new StreamSource(getInputStream(href));
// this works with saxon7/saxon6.5.2/xalan
source.setSystemId(href);
return source;
}
public void setFile(String file)
{
if (file != null)
{
File target = new File(file);
if (!target.exists())
{
XSLTProcessor.log(LogService.LOG_WARNING,"Target file " + file + " does not exist, defaulting to previous",null);
return;
}
if (target.isDirectory())
{
useJar = false;
XSLTProcessor.log(LogService.LOG_INFO, "Using " + file + " as the root dir", null);
this.root = target;
return;
}
if (target.isFile() && (target.getName().endsWith(".jar") ||
(target.getName().endsWith(".zip"))))
{
try {
URL url = target.toURL();
targetClassLoader = new URLClassLoader(new URL[] {url});
XSLTProcessor.log(LogService.LOG_INFO,"Using compressed file " + url + " as the root file",null);
this.root = target;
useJar = true;
} catch (MalformedURLException e) {
XSLTProcessor.log(LogService.LOG_WARNING,"Unable to create class loader", e);
}
}
else
{
XSLTProcessor.log(LogService.LOG_WARNING,"Target file " + file + " does not exist, defaulting to previous",null);
}
}
}
public String getFile()
{
return (root != null)?root.getName():null;
}
public String getPathInJar()
{
return path;
}
public void setPathInJar(String path)
{
this.path = path;
}
public String getDefaultPage()
{
return defaultPage;
}
public void setDefaultPage(String defaultPage)
{
this.defaultPage = defaultPage;
}
public boolean isUseJar()
{
return useJar;
}
public boolean isUsePath()
{
return !useJar;
}
public void addMimeType(String extension, String type)
{
if (extension != null && type != null)
{
XSLTProcessor.log(LogService.LOG_INFO,"Added MIME type " + type + " for extension " + extension,null);
mimeTypes.put(extension, type);
}
}
public void setUseCache(boolean useCache)
{
this.useCache = useCache;
}
public boolean isUseCache()
{
return useCache;
}
public String getName()
{
return "XSLT Processor";
}
public Locale getLocale() {
return locale;
}
public void setLocale(Locale locale) {
this.locale = locale;
}
public void setLocaleString(String locale) {
if (locale == null || locale.length()==0) {
this.locale = new Locale("en", "");
}
else
{
// split locale based on underbar
StringTokenizer tknzr = new StringTokenizer(locale,"_");
String language = tknzr.nextToken();
String country = "";
String variant = "";
if (tknzr.hasMoreTokens())
country = tknzr.nextToken();
if (tknzr.hasMoreTokens())
variant = tknzr.nextToken();
this.locale = new Locale(language,country,variant);
}
}
private static void log(int prio, String message, Throwable t){
if (HttpConnectorActivator.bc!=null){
ServiceReference logSR=HttpConnectorActivator.bc.getServiceReference(LogService.class.getName());
if (logSR!=null){
((LogService)HttpConnectorActivator.bc.getService(logSR)).log(prio, message, t);
}else{
System.out.println("No Log Service");
}
}else{
System.out.println("mx4j.tools.adapatoir.http.XSLTProcessor.log: No bundleContext");
}
}
}