001: /**********************************************************************************
002: * $URL: https://source.sakaiproject.org/svn/help/tags/sakai_2-4-1/help-tool/src/java/org/sakaiproject/tool/help/RestContentProvider.java $
003: * $Id: RestContentProvider.java 22794 2007-03-16 21:19:45Z jholtzman@berkeley.edu $
004: ***********************************************************************************
005: *
006: * Copyright (c) 2003, 2004 The Sakai Foundation.
007: *
008: * Licensed under the Educational Community License, Version 1.0 (the "License");
009: * you may not use this file except in compliance with the License.
010: * You may obtain a copy of the License at
011: *
012: * http://www.opensource.org/licenses/ecl1.php
013: *
014: * Unless required by applicable law or agreed to in writing, software
015: * distributed under the License is distributed on an "AS IS" BASIS,
016: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: * See the License for the specific language governing permissions and
018: * limitations under the License.
019: *
020: **********************************************************************************/package org.sakaiproject.tool.help;
021:
022: import java.io.BufferedReader;
023: import java.io.ByteArrayOutputStream;
024: import java.io.IOException;
025: import java.io.InputStream;
026: import java.io.InputStreamReader;
027: import java.io.StringReader;
028: import java.net.MalformedURLException;
029: import java.net.URL;
030: import java.net.URLConnection;
031: import java.util.Date;
032:
033: import javax.servlet.ServletContext;
034: import javax.xml.parsers.DocumentBuilder;
035: import javax.xml.parsers.DocumentBuilderFactory;
036: import javax.xml.parsers.ParserConfigurationException;
037: import javax.xml.transform.Result;
038: import javax.xml.transform.Source;
039: import javax.xml.transform.Transformer;
040: import javax.xml.transform.TransformerConfigurationException;
041: import javax.xml.transform.TransformerException;
042: import javax.xml.transform.TransformerFactory;
043: import javax.xml.transform.dom.DOMResult;
044: import javax.xml.transform.dom.DOMSource;
045: import javax.xml.transform.stream.StreamResult;
046:
047: import org.apache.commons.logging.Log;
048: import org.apache.commons.logging.LogFactory;
049: import org.sakaiproject.api.app.help.HelpManager;
050: import org.sakaiproject.api.app.help.Resource;
051: import org.sakaiproject.component.cover.ServerConfigurationService;
052: import org.w3c.dom.Document;
053: import org.w3c.dom.Element;
054: import org.w3c.dom.Node;
055: import org.w3c.dom.NodeList;
056: import org.w3c.dom.Text;
057: import org.xml.sax.InputSource;
058: import org.xml.sax.SAXException;
059:
060: import sun.misc.BASE64Encoder;
061:
062: public class RestContentProvider {
063:
064: private static final String XML_PREPROCESS_XSL = "/xsl/kbxml-preprocess.xsl";
065: private static final String XML_KB_XSL = "/xsl/kb.xsl";
066: private static Boolean XSL_INITIALIZED = Boolean.FALSE;
067:
068: private static Document xslDocumentPreprocess;
069: private static Document xslDocumentAllInOne;
070:
071: private static final Log LOG = LogFactory
072: .getLog(RestContentProvider.class);
073:
074: /**
075: * @param htmlDocument
076: * @return document with css link
077: */
078: private static void addLinkToCss(Document htmlDocument) {
079: if (LOG.isDebugEnabled()) {
080: LOG.debug("addLinkToCss(Document " + htmlDocument + ")");
081: }
082:
083: String skinRoot = ServerConfigurationService.getString(
084: "skin.repo", "/library/skin");
085: String skin = ServerConfigurationService.getString(
086: "skin.default", "default");
087:
088: NodeList nodes = htmlDocument.getElementsByTagName("head");
089: Node node = nodes.item(0);
090:
091: Element linkNodeBase = htmlDocument.createElement("link");
092: linkNodeBase.setAttribute("href", skinRoot + "/tool_base.css");
093: linkNodeBase.setAttribute("rel", "stylesheet");
094: linkNodeBase.setAttribute("content-type", "text/css");
095:
096: Element linkNodeDefault = htmlDocument.createElement("link");
097: linkNodeDefault.setAttribute("href", skinRoot + "/" + skin
098: + "/tool.css");
099: linkNodeDefault.setAttribute("rel", "stylesheet");
100: linkNodeDefault.setAttribute("content-type", "text/css");
101:
102: Element linkNodeREST = htmlDocument.createElement("link");
103: linkNodeREST.setAttribute("href", "css/REST.css");
104: linkNodeREST.setAttribute("rel", "stylesheet");
105: linkNodeREST.setAttribute("content-type", "text/css");
106:
107: if (node.getFirstChild() == null
108: || !(node.getFirstChild().getNodeName().equals("link"))) {
109: //node.appendChild(linkNodeBase);
110: //node.appendChild(linkNodeDefault);
111: node.appendChild(linkNodeREST);
112: }
113: }
114:
115: /**
116: *
117: * @param document
118: * @return serialized String
119: */
120: private static String serializeDocument(Document document) {
121: if (LOG.isDebugEnabled()) {
122: LOG.debug("serializeDocumentDocument(Document " + document
123: + ")");
124: }
125:
126: if (document != null) {
127: ByteArrayOutputStream out = new ByteArrayOutputStream();
128: Source xmlSource = new DOMSource(document);
129: Result outputTarget = new StreamResult(out);
130: Transformer tf;
131: try {
132: tf = TransformerFactory.newInstance().newTransformer();
133: tf.transform(xmlSource, outputTarget);
134: } catch (TransformerException e) {
135: LOG.warn(e);
136: }
137: return out.toString();
138: } else {
139: return "<html><body>Unable to retrieve document</body></html>";
140: }
141: }
142:
143: /**
144: * Apply transform
145: * @param transformer
146: * @param source
147: * @param result
148: */
149: private static void transform(Transformer transformer,
150: Source source, Result result) {
151: if (LOG.isDebugEnabled()) {
152: LOG.debug("transform(Transformer " + transformer
153: + ", Source" + source + ", Result " + result + ")");
154: }
155:
156: try {
157: transformer.transform(source, result);
158: } catch (TransformerException e) {
159: LOG.error(e.getMessage(), e);
160: }
161: }
162:
163: /**
164: * transform document
165: * @param document
166: * @param stylesheet
167: * @return
168: */
169: private static Document transformDocument(Document document,
170: Document stylesheet) {
171: if (LOG.isDebugEnabled()) {
172: LOG.debug("transformDocument(Document " + document
173: + ", Document " + stylesheet + ")");
174: }
175:
176: Document transformedDoc = createDocument();
177: DOMSource docSource = new DOMSource(document);
178: DOMResult docResult = new DOMResult(transformedDoc);
179: Transformer transformer = createTransformer(stylesheet);
180: transform(transformer, docSource, docResult);
181:
182: return transformedDoc;
183: }
184:
185: /**
186: * create document
187: * @return document
188: */
189: private static Document createDocument() {
190: if (LOG.isDebugEnabled()) {
191: LOG.debug("createDocument()");
192: }
193:
194: Document document = null;
195: DocumentBuilderFactory builderFactory = DocumentBuilderFactory
196: .newInstance();
197: builderFactory.setNamespaceAware(true);
198:
199: try {
200: DocumentBuilder documentBuilder = builderFactory
201: .newDocumentBuilder();
202: document = documentBuilder.newDocument();
203: } catch (ParserConfigurationException e) {
204: LOG.error(e.getMessage(), e);
205: e.printStackTrace();
206: }
207:
208: return document;
209: }
210:
211: /**
212: * create transformer
213: * @param stylesheet
214: * @return
215: */
216: private static Transformer createTransformer(Document stylesheet) {
217: if (LOG.isDebugEnabled()) {
218: LOG.debug("createTransformer(Document " + stylesheet + ")");
219: }
220:
221: Transformer transformer = null;
222: TransformerFactory transformerFactory = TransformerFactory
223: .newInstance();
224: URIResolver resolver = new URIResolver();
225: transformerFactory.setURIResolver(resolver);
226:
227: try {
228: DOMSource source = new DOMSource(stylesheet);
229: String systemId = "/xsl";
230: source.setSystemId(systemId);
231: transformer = transformerFactory.newTransformer(source);
232: } catch (TransformerConfigurationException e) {
233: LOG.error(e.getMessage(), e);
234: e.printStackTrace();
235: }
236:
237: return transformer;
238: }
239:
240: /**
241: * synchronize initialization of caching XSL
242: * @param context
243: */
244: public static void initializeXsl(ServletContext context) {
245: if (LOG.isDebugEnabled()) {
246: LOG.debug("initializeXsl(ServletContext " + context + ")");
247: }
248:
249: if (XSL_INITIALIZED.booleanValue()) {
250: return;
251: } else {
252: synchronized (XSL_INITIALIZED) {
253: if (!XSL_INITIALIZED.booleanValue()) {
254: //read in and parse xsl
255: InputStream iStreamPreprocess = null;
256: InputStream iStreamAllInOne = null;
257: try {
258: iStreamPreprocess = context
259: .getResourceAsStream(XML_PREPROCESS_XSL);
260: iStreamAllInOne = context
261: .getResourceAsStream(XML_KB_XSL);
262:
263: DocumentBuilderFactory builderFactory = DocumentBuilderFactory
264: .newInstance();
265: builderFactory.setNamespaceAware(true);
266: DocumentBuilder documentBuilder = builderFactory
267: .newDocumentBuilder();
268: xslDocumentPreprocess = documentBuilder
269: .parse(iStreamPreprocess);
270: xslDocumentAllInOne = documentBuilder
271: .parse(iStreamAllInOne);
272: } catch (ParserConfigurationException e) {
273: LOG.error(e.getMessage(), e);
274: } catch (IOException e) {
275: LOG.error(e.getMessage(), e);
276: } catch (SAXException e) {
277: LOG.error(e.getMessage(), e);
278: }
279: try {
280: iStreamPreprocess.close();
281: iStreamAllInOne.close();
282: } catch (IOException e) {
283: LOG.error(e.getMessage(), e);
284: }
285:
286: XSL_INITIALIZED = Boolean.TRUE;
287: }
288: }
289: }
290: }
291:
292: /**
293: * get transformed document
294: * @param servlet context
295: * @param sBuffer
296: * @return
297: */
298: private static Document getTransformedDocument(
299: ServletContext context, StringBuffer sBuffer) {
300:
301: if (LOG.isDebugEnabled()) {
302: LOG.debug("getTransformedDocument(ServletContext "
303: + context + ", StringBuffer " + sBuffer + ")");
304: }
305:
306: initializeXsl(context);
307:
308: Document result = null;
309: try {
310: DocumentBuilderFactory dbf = DocumentBuilderFactory
311: .newInstance();
312: DocumentBuilder builder = dbf.newDocumentBuilder();
313: StringReader sReader = new StringReader(sBuffer.toString());
314: InputSource is = new org.xml.sax.InputSource(sReader);
315: Document xmlDocument = builder.parse(is);
316:
317: // test for kb error condition
318: if (xmlDocument.getElementsByTagName("kberror").getLength() > 0) {
319: result = createErrorDocument();
320: } else {
321: /** debugging
322: OutputFormat format = new OutputFormat(xmlDocument);
323: XMLSerializer output = new XMLSerializer(System.out, format);
324: output.serialize(xmlDocument);
325: */
326:
327: result = transformDocument(xmlDocument,
328: xslDocumentPreprocess);
329: result = transformDocument(result, xslDocumentAllInOne);
330: }
331:
332: /** debugging
333: OutputFormat format = new OutputFormat(result);
334: XMLSerializer output = new XMLSerializer(System.out, format);
335: output.serialize(result);
336: */
337:
338: addLinkToCss(result);
339:
340: } catch (ParserConfigurationException e) {
341: LOG.error(e.getMessage(), e);
342: } catch (IOException e) {
343: LOG.error(e.getMessage(), e);
344: } catch (SAXException e) {
345: LOG.error(e.getMessage(), e);
346: }
347: return result;
348: }
349:
350: /**
351: * get transformed document
352: * @param context
353: * @return transformed document
354: */
355: public static String getTransformedDocument(ServletContext context,
356: HelpManager helpManager, Resource resource) {
357:
358: Long now = new Long((new Date()).getTime());
359:
360: if (LOG.isDebugEnabled()) {
361: LOG.debug("getTransformedDocument(ServletContext "
362: + context + ", HelpManager " + helpManager
363: + "String " + resource.getDocId() + ")");
364: }
365:
366: // test if resource is cached
367: if (resource.getTstamp() != null) {
368: if ((now.longValue() - resource.getTstamp().longValue()) < helpManager
369: .getRestConfiguration().getCacheInterval()) {
370: if (LOG.isDebugEnabled()) {
371: LOG.debug("retrieving document: "
372: + resource.getDocId() + " from cache");
373: }
374: return resource.getSource();
375: }
376: }
377:
378: URL url = null;
379: String transformedString = null;
380: try {
381: url = new URL(helpManager.getRestConfiguration()
382: .getRestUrlInDomain()
383: + resource.getDocId()
384: + "?domain="
385: + helpManager.getRestConfiguration()
386: .getRestDomain());
387: URLConnection urlConnection = url.openConnection();
388:
389: String basicAuthUserPass = helpManager
390: .getRestConfiguration().getRestCredentials();
391: String encoding = new BASE64Encoder()
392: .encode(basicAuthUserPass.getBytes());
393:
394: urlConnection.setRequestProperty("Authorization", "Basic "
395: + encoding);
396:
397: StringBuffer sBuffer = new StringBuffer();
398:
399: BufferedReader br = new BufferedReader(
400: new InputStreamReader(urlConnection
401: .getInputStream(), "UTF-8"), 512);
402: int readReturn = 0;
403: char[] cbuf = new char[512];
404: while ((readReturn = br.read(cbuf, 0, 512)) != -1) {
405: sBuffer.append(cbuf, 0, readReturn);
406: }
407:
408: Document transformedDocument = getTransformedDocument(
409: context, sBuffer);
410: transformedString = serializeDocument(transformedDocument);
411: } catch (MalformedURLException e) {
412: LOG.error("Malformed URL in REST document: "
413: + url.getPath());
414: } catch (IOException e) {
415: LOG.error("Could not open connection to REST document: "
416: + url.getPath());
417: }
418:
419: resource.setSource(transformedString);
420: resource.setTstamp(now);
421: helpManager.storeResource(resource);
422:
423: return transformedString;
424: }
425:
426: /**
427: * Given any error condition, create an error document including css
428: * @return Document
429: */
430: public static Document createErrorDocument() {
431: Document errorDocument = createDocument();
432:
433: Element html = errorDocument.createElement("html");
434: Element head = errorDocument.createElement("head");
435: Element body = errorDocument.createElement("body");
436: Element p = errorDocument.createElement("p");
437:
438: Text textNode = errorDocument
439: .createTextNode("An error retrieving document from knowledge base has occurred.");
440:
441: p.appendChild(textNode);
442: body.appendChild(p);
443: html.appendChild(head);
444: html.appendChild(body);
445: errorDocument.appendChild(html);
446:
447: return errorDocument;
448: }
449: }
|