| /* |
| * 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"); |
| } |
| } |
| |
| } |