/********************************************************************************** * $URL: https://source.sakaiproject.org/svn/help/trunk/help-tool/src/java/org/sakaiproject/tool/help/RestContentProvider.java $ * $Id: RestContentProvider.java 105079 2012-02-24 23:08:11Z ottenhoff@longsight.com $ *********************************************************************************** * * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008 The Sakai Foundation * * Licensed under the Educational Community 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.opensource.org/licenses/ECL-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.sakaiproject.tool.help; import java.io.BufferedReader; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.StringReader; import java.net.MalformedURLException; import java.net.URL; import java.net.URLConnection; import java.util.Date; import javax.servlet.ServletContext; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.Result; import javax.xml.transform.Source; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerConfigurationException; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMResult; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import org.apache.commons.codec.binary.Base64; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.sakaiproject.api.app.help.HelpManager; import org.sakaiproject.api.app.help.Resource; import org.sakaiproject.component.cover.ServerConfigurationService; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.w3c.dom.Text; import org.xml.sax.InputSource; import org.xml.sax.SAXException; public class RestContentProvider { private static final String XML_PREPROCESS_XSL = "/xsl/kbxml-preprocess.xsl"; private static final String XML_KB_XSL = "/xsl/kb.xsl"; private static Boolean XSL_INITIALIZED = Boolean.FALSE; private static Object XSL_INITIALIZED_LOCK = new Object(); private static Document xslDocumentPreprocess; private static Document xslDocumentAllInOne; private static final Log LOG = LogFactory .getLog(RestContentProvider.class); /** * @param htmlDocument * @return document with css link */ private static void addLinkToCss(Document htmlDocument) { if (LOG.isDebugEnabled()) { LOG.debug("addLinkToCss(Document " + htmlDocument + ")"); } String skinRoot = ServerConfigurationService.getString("skin.repo", "/library/skin"); String skin = ServerConfigurationService.getString("skin.default", "default"); NodeList nodes = htmlDocument.getElementsByTagName("head"); Node node = nodes.item(0); Element linkNodeBase = htmlDocument.createElement("link"); linkNodeBase.setAttribute("href", skinRoot + "/tool_base.css"); linkNodeBase.setAttribute("rel", "stylesheet"); linkNodeBase.setAttribute("content-type", "text/css"); Element linkNodeDefault = htmlDocument.createElement("link"); linkNodeDefault.setAttribute("href", skinRoot + "/" + skin + "/tool.css"); linkNodeDefault.setAttribute("rel", "stylesheet"); linkNodeDefault.setAttribute("content-type", "text/css"); Element linkNodeREST = htmlDocument.createElement("link"); linkNodeREST.setAttribute("href", "css/REST.css"); linkNodeREST.setAttribute("rel", "stylesheet"); linkNodeREST.setAttribute("content-type", "text/css"); if (node.getFirstChild() == null || !(node.getFirstChild().getNodeName().equals("link"))) { //node.appendChild(linkNodeBase); //node.appendChild(linkNodeDefault); node.appendChild(linkNodeREST); } } /** * * @param document * @return serialized String */ private static String serializeDocument(Document document) { if (LOG.isDebugEnabled()) { LOG.debug("serializeDocumentDocument(Document " + document + ")"); } if (document != null) { ByteArrayOutputStream out = new ByteArrayOutputStream(); Source xmlSource = new DOMSource(document); Result outputTarget = new StreamResult(out); Transformer tf; try { tf = TransformerFactory.newInstance().newTransformer(); tf.transform(xmlSource, outputTarget); } catch (TransformerException e) { LOG.warn(e); } return out.toString(); } else { return "<html><body>Unable to retrieve document</body></html>"; } } /** * Apply transform * @param transformer * @param source * @param result */ private static void transform(Transformer transformer, Source source, Result result) { if (LOG.isDebugEnabled()) { LOG.debug("transform(Transformer " + transformer + ", Source" + source + ", Result " + result + ")"); } try { transformer.transform(source, result); } catch (TransformerException e) { LOG.error(e.getMessage(), e); } } /** * transform document * @param document * @param stylesheet * @return */ private static Document transformDocument(Document document, Document stylesheet) { if (LOG.isDebugEnabled()) { LOG.debug("transformDocument(Document " + document + ", Document " + stylesheet + ")"); } Document transformedDoc = createDocument(); DOMSource docSource = new DOMSource(document); DOMResult docResult = new DOMResult(transformedDoc); Transformer transformer = createTransformer(stylesheet); transform(transformer, docSource, docResult); return transformedDoc; } /** * create document * @return document */ private static Document createDocument() { if (LOG.isDebugEnabled()) { LOG.debug("createDocument()"); } Document document = null; DocumentBuilderFactory builderFactory = DocumentBuilderFactory .newInstance(); builderFactory.setNamespaceAware(true); try { DocumentBuilder documentBuilder = builderFactory.newDocumentBuilder(); document = documentBuilder.newDocument(); } catch (ParserConfigurationException e) { LOG.error(e.getMessage(), e); e.printStackTrace(); } return document; } /** * create transformer * @param stylesheet * @return */ private static Transformer createTransformer(Document stylesheet) { if (LOG.isDebugEnabled()) { LOG.debug("createTransformer(Document " + stylesheet + ")"); } Transformer transformer = null; TransformerFactory transformerFactory = TransformerFactory.newInstance(); URIResolver resolver = new URIResolver(); transformerFactory.setURIResolver(resolver); try { DOMSource source = new DOMSource(stylesheet); String systemId = "/xsl"; source.setSystemId(systemId); transformer = transformerFactory.newTransformer(source); } catch (TransformerConfigurationException e) { LOG.error(e.getMessage(), e); e.printStackTrace(); } return transformer; } /** * synchronize initialization of caching XSL * @param context */ public static void initializeXsl(ServletContext context) { if (LOG.isDebugEnabled()) { LOG.debug("initializeXsl(ServletContext " + context + ")"); } if (XSL_INITIALIZED.booleanValue()) { return; } else { synchronized (XSL_INITIALIZED_LOCK) { if (!XSL_INITIALIZED.booleanValue()) { //read in and parse xsl InputStream iStreamPreprocess = null; InputStream iStreamAllInOne = null; try { iStreamPreprocess = context.getResourceAsStream(XML_PREPROCESS_XSL); iStreamAllInOne = context.getResourceAsStream(XML_KB_XSL); DocumentBuilderFactory builderFactory = DocumentBuilderFactory .newInstance(); builderFactory.setNamespaceAware(true); DocumentBuilder documentBuilder = builderFactory .newDocumentBuilder(); xslDocumentPreprocess = documentBuilder.parse(iStreamPreprocess); xslDocumentAllInOne = documentBuilder.parse(iStreamAllInOne); } catch (ParserConfigurationException e) { LOG.error(e.getMessage(), e); } catch (IOException e) { LOG.error(e.getMessage(), e); } catch (SAXException e) { LOG.error(e.getMessage(), e); } try { iStreamPreprocess.close(); iStreamAllInOne.close(); } catch (IOException e) { LOG.error(e.getMessage(), e); } XSL_INITIALIZED = Boolean.TRUE; } } } } /** * get transformed document * @param servlet context * @param sBuffer * @return */ private static Document getTransformedDocument(ServletContext context, StringBuilder sBuffer) { if (LOG.isDebugEnabled()) { LOG.debug("getTransformedDocument(ServletContext " + context + ", StringBuilder " + sBuffer + ")"); } initializeXsl(context); Document result = null; try { DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = dbf.newDocumentBuilder(); StringReader sReader = new StringReader(sBuffer.toString()); InputSource is = new org.xml.sax.InputSource(sReader); Document xmlDocument = builder.parse(is); // test for kb error condition if (xmlDocument.getElementsByTagName("kberror").getLength() > 0){ result = createErrorDocument(); } else{ /** debugging OutputFormat format = new OutputFormat(xmlDocument); XMLSerializer output = new XMLSerializer(System.out, format); output.serialize(xmlDocument); */ result = transformDocument(xmlDocument, xslDocumentPreprocess); result = transformDocument(result, xslDocumentAllInOne); } /** debugging OutputFormat format = new OutputFormat(result); XMLSerializer output = new XMLSerializer(System.out, format); output.serialize(result); */ addLinkToCss(result); } catch (ParserConfigurationException e) { LOG.error(e.getMessage(), e); } catch (IOException e) { LOG.error(e.getMessage(), e); } catch (SAXException e) { LOG.error(e.getMessage(), e); } return result; } /** * get transformed document * @param context * @return transformed document */ public static String getTransformedDocument(ServletContext context, HelpManager helpManager, Resource resource) { Long now = new Long((new Date()).getTime()); if (LOG.isDebugEnabled()) { LOG.debug("getTransformedDocument(ServletContext " + context + ", HelpManager " + helpManager + "String " + resource.getDocId() + ")"); } // test if resource is cached if (resource.getTstamp() != null){ if ((now.longValue() - resource.getTstamp().longValue()) < helpManager.getRestConfiguration().getCacheInterval()){ if (LOG.isDebugEnabled()){ LOG.debug("retrieving document: " + resource.getDocId() + " from cache"); } return resource.getSource(); } } URL url = null; String transformedString = null; try { url = new URL(helpManager.getRestConfiguration().getRestUrlInDomain() + resource.getDocId() + "?domain=" + helpManager.getRestConfiguration().getRestDomain()); URLConnection urlConnection = url.openConnection(); String basicAuthUserPass = helpManager.getRestConfiguration().getRestCredentials(); String encoding = Base64.encodeBase64(basicAuthUserPass.getBytes("utf-8")).toString(); urlConnection.setRequestProperty("Authorization", "Basic " + encoding); StringBuilder sBuffer = new StringBuilder(); BufferedReader br = new BufferedReader( new InputStreamReader(urlConnection.getInputStream(),"UTF-8"), 512); try { int readReturn = 0; char[] cbuf = new char[512]; while ((readReturn = br.read(cbuf, 0, 512)) != -1) { sBuffer.append(cbuf, 0, readReturn); } } finally { br.close(); } Document transformedDocument = getTransformedDocument(context, sBuffer); transformedString = serializeDocument(transformedDocument); } catch (MalformedURLException e) { LOG.error("Malformed URL in REST document: " + url.getPath()); } catch (IOException e) { LOG.error("Could not open connection to REST document: " + url.getPath()); } resource.setSource(transformedString); resource.setTstamp(now); helpManager.storeResource(resource); return transformedString; } /** * Given any error condition, create an error document including css * @return Document */ public static Document createErrorDocument(){ Document errorDocument = createDocument(); Element html = errorDocument.createElement("html"); Element head = errorDocument.createElement("head"); Element body = errorDocument.createElement("body"); Element p = errorDocument.createElement("p"); Text textNode = errorDocument.createTextNode("An error retrieving document from knowledge base has occurred."); p.appendChild(textNode); body.appendChild(p); html.appendChild(head); html.appendChild(body); errorDocument.appendChild(html); return errorDocument; } }