001: /*
002: * The contents of this file are subject to the
003: * Mozilla Public License Version 1.1 (the "License");
004: * you may not use this file except in compliance with the License.
005: * You may obtain a copy of the License at http://www.mozilla.org/MPL/
006: *
007: * Software distributed under the License is distributed on an "AS IS"
008: * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
009: * See the License for the specific language governing rights and
010: * limitations under the License.
011: *
012: * The Initial Developer of the Original Code is Simulacra Media Ltd.
013: * Portions created by Simulacra Media Ltd are Copyright (C) Simulacra Media Ltd, 2004.
014: *
015: * All Rights Reserved.
016: *
017: * Contributor(s):
018: */package org.openharmonise.rm.publishing;
019:
020: import java.io.OutputStream;
021: import java.util.Vector;
022: import java.util.logging.*;
023:
024: import org.openharmonise.commons.cache.CacheException;
025: import org.openharmonise.commons.dsi.AbstractDataStoreInterface;
026: import org.openharmonise.commons.xml.*;
027: import org.openharmonise.commons.xml.namespace.NamespaceClashException;
028: import org.openharmonise.rm.*;
029: import org.openharmonise.rm.commands.CommandProcessHandler;
030: import org.openharmonise.rm.config.*;
031: import org.openharmonise.rm.factory.*;
032: import org.openharmonise.rm.logging.*;
033: import org.openharmonise.rm.publishing.renderers.*;
034: import org.openharmonise.rm.resources.AbstractObject;
035: import org.openharmonise.rm.resources.publishing.*;
036: import org.openharmonise.rm.resources.xml.XMLResource;
037: import org.openharmonise.rm.security.authentication.InvalidUserException;
038: import org.openharmonise.rm.security.authorization.*;
039: import org.openharmonise.rm.sessions.*;
040: import org.w3c.dom.*;
041:
042: /**
043: * This class provides the funtionality to process the publishing rules
044: * defined in a <code>WebPage</code> object's XML to produce XML containing the content
045: * to be presented.</p>
046: *
047: * @author Michael Bell
048: * @version $Revision: 1.2.2.1 $
049: *
050: */
051: public class WebPageEngine {
052:
053: protected AbstractDataStoreInterface m_dsi = null;
054:
055: protected Session m_Session = null;
056:
057: protected static XMLPrettyPrint m_xprinter = new XMLPrettyPrint();
058:
059: /**
060: * Logger for this class
061: */
062: private static final Logger m_logger = Logger
063: .getLogger(WebPageEngine.class.getName());
064:
065: /**
066: * Basic constructor
067: *
068: */
069: public WebPageEngine() {
070: }
071:
072: /**
073: * Constructs an object with an interface to the DB
074: *
075: * @param con
076: * @throws PublishException
077: */
078: public WebPageEngine(AbstractDataStoreInterface con)
079: throws PublishException {
080: m_dsi = con;
081:
082: try {
083: m_Session = new Session(
084: m_dsi,
085: Integer
086: .parseInt(ConfigSettings
087: .getProperty(Session.DEFAULT_TIMEOUT_PNAME)));
088: WebPageEngineCache.getInstance(this .m_dsi).addToCache(
089: m_Session.getSessionId(), this );
090: } catch (NumberFormatException e) {
091: throw new PublishException("Number formatting error", e);
092: } catch (SessionException e) {
093: throw new PublishException("Error creating session", e);
094: } catch (ConfigException e) {
095: throw new PublishException("Error getting config data", e);
096: } catch (CacheException e) {
097: throw new PublishException(
098: "Error getting object from cache", e);
099: }
100: }
101:
102: /**
103: * Constructs an object with an interface to the DB for the session associated
104: * to the given session id.
105: *
106: * @param con
107: * @param sSessionId
108: * @throws PublishException
109: */
110: public WebPageEngine(AbstractDataStoreInterface con,
111: String sSessionId) throws PublishException {
112: m_dsi = con;
113:
114: if (m_logger.isLoggable(Level.FINE)) {
115: m_logger.logp(Level.FINE, this .getClass().getName(),
116: "WebPageEngine",
117: "Constructing webpage engine for session "
118: + sSessionId);
119: }
120:
121: try {
122: m_Session = new Session(m_dsi, sSessionId);
123: } catch (SessionException e) {
124: throw new PublishException("Error creating session", e);
125: }
126: }
127:
128: /**
129: * Returns the <code>Session</code> associated with this object
130: *
131: */
132: public Session getSession() {
133: return m_Session;
134: }
135:
136: /**
137: * Processes the given <code>WebPage</code> in the context of the given <code>State</code>
138: * and returns the resultant <code>HarmoniseOutput</code>. The method gets the XML associated
139: * with the <code>WebPage</code> and parses it to find the instructions on what
140: * content to include in the resultant <code>HarmoniseOutput</code>, the <code>State</code>
141: * gives a context to the process.
142: *
143: * @param page
144: * @param state
145: * @return
146: * @throws PublishException
147: */
148: public HarmoniseOutput createXML(WebPage page, State state)
149: throws PublishException {
150: XMLDocument xml = null;
151: HarmoniseOutput xout = null;
152:
153: if (m_logger.isLoggable(Level.FINE)) {
154: m_logger.logp(Level.FINE, this .getClass().getName(),
155: "createXML", "Generating XML for webpage "
156: + page.getId());
157: }
158:
159: try {
160: m_Session.processState(state, page.getTimeout());
161:
162: if (AuthorizationValidator.isVisible(m_Session.getUser(),
163: page) == false) {
164: if (m_logger.isLoggable(Level.INFO)) {
165: m_logger.logp(Level.INFO,
166: this .getClass().getName(), "createXML",
167: "User id " + m_Session.getUser().getId()
168: + " not allowed to view page "
169: + page.getId());
170: }
171: throw new InvalidUserException(
172: "User does not have permission to view page");
173: }
174: xml = page.getXML().getXIncludeResolvedDocument();
175:
176: } catch (SessionException e) {
177: throw new PublishException("Error processing state", e);
178: } catch (DataAccessException e) {
179: throw new PublishException("Data access error", e);
180: } catch (AuthorizationException e) {
181: throw new PublishException("Error validating user", e);
182: } catch (InvalidUserException e) {
183: throw new PublishException("Invalid User", e);
184: }
185: try {
186: xout = processXML(xml.getDocumentElement(), state);
187: } catch (PublishException e) {
188: m_logger.log(Level.SEVERE, "Template XML form for page id"
189: + page.getId() + " was invalid,"
190: + " unable to build any XML", e);
191: throw new RuntimeException("Template XML form for page id"
192: + page.getId() + " was invalid,"
193: + " unable to build any XML");
194: }
195: return xout;
196: }
197:
198: /**
199: * Processes the given <code>Element</code> in the context of the given <code>State</code>
200: * and returns the resultant <code>HarmoniseOutput</code>. The method
201: * parses the XML to find the instructions on what content to include in the
202: * resultant <code>HarmoniseOutput</code>. The <code>State</code>
203: * gives a context to the process.
204: *
205: * @param page
206: * @param state
207: * @return
208: * @throws PublishException
209: */
210: public HarmoniseOutput createXML(Element topEl, State state)
211: throws PublishException {
212: XMLDocument xdoc = null;
213: try {
214: XMLResource xml = new XMLResource();
215: m_xprinter.setNamespaceAware(true);
216: xml.setContent(m_xprinter.printNode(topEl));
217: xdoc = xml.getXIncludeResolvedDocument();
218: } catch (PopulateException e) {
219: throw new PublishException("Error populating xml resource",
220: e);
221: } catch (NamespaceClashException e) {
222: throw new PublishException("Namespace error", e);
223: } catch (DataAccessException e) {
224: throw new PublishException("Data access error", e);
225: }
226:
227: return processXML(xdoc.getDocumentElement(), state);
228: }
229:
230: /**
231: * Processes the given <code>Element</code> in the context of the given <code>State</code>
232: * and returns the resultant <code>HarmoniseOutput</code>. The method parses the XML
233: * to find the instructions on what content to include in the resultant
234: * <code>HarmoniseOutput</code>. The <code>State</code> gives a context to the process.
235: *
236: * @param topEl
237: * @param state
238: * @return
239: * @throws PublishException
240: */
241: private HarmoniseOutput processXML(Element topEl, State state)
242: throws PublishException {
243: Element pageState;
244: HarmoniseOutput xOutput = new HarmoniseOutput(m_dsi);
245:
246: Element tempPublishEl;
247: Element root;
248: root = xOutput.createElement(WebPage.TAG_HARMONISE);
249: xOutput.appendChild(root);
250:
251: tempPublishEl = null;
252:
253: pageState = xOutput.createElement(State.TAG_STATE);
254: root.appendChild(pageState);
255:
256: pageState.appendChild(m_Session.publish(xOutput));
257:
258: copyStateChildren((Node) pageState, state, xOutput);
259:
260: NodeList children = topEl.getChildNodes();
261:
262: for (int i = 0; i < children.getLength(); i++) {
263: if (children.item(i).getNodeType() != Node.ELEMENT_NODE) {
264: continue;
265: }
266:
267: Element next = (Element) children.item(i);
268: String sTagName = next.getTagName();
269:
270: Vector ignore_tags = new Vector();
271:
272: ignore_tags.add(WebPage.TAG_PAGETITLE);
273:
274: try {
275: if (sTagName.equals(WebPage.TAG_NAVIGATION)) {
276: tempPublishEl = navigation(next, state, xOutput);
277: } else if (sTagName.equals(Template.TAG_TEMPLATE)) {
278: tempPublishEl = publishTemplate(next, state,
279: xOutput);
280: } else if (sTagName
281: .equals(CommandProcessHandler.TAG_WORKFLOW)) {
282: CommandProcessHandler cmdHandler = new CommandProcessHandler(
283: m_dsi);
284:
285: tempPublishEl = cmdHandler.publish(next, xOutput,
286: state);
287: } else if (ignore_tags.contains(sTagName)) {
288: tempPublishEl = (Element) xOutput.copyNode(next);
289: } else {
290: try {
291: Publishable pubObj = HarmoniseObjectFactory
292: .instantiatePublishableObject(
293: this .m_dsi, next, state);
294:
295: tempPublishEl = pubObj.publish(next, xOutput,
296: state);
297: } catch (HarmoniseFactoryException e) {
298: tempPublishEl = (Element) xOutput
299: .copyNode(next);
300: root.appendChild(e.publish(xOutput));
301: m_logger.log(Level.WARNING, e
302: .getLocalizedMessage(), e);
303: }
304: }
305: } catch (PublishException e) {
306: m_logger.log(Level.WARNING, e.getLocalizedMessage(), e);
307: tempPublishEl = e.publish(xOutput);
308: }
309:
310: if (tempPublishEl != null) {
311: root.appendChild(tempPublishEl);
312: }
313:
314: }
315:
316: if (((Node) root).getChildNodes().getLength() == 0) {
317: throw new PublishException(
318: "Unable to publish Harmonise Page.");
319: }
320:
321: return xOutput;
322: }
323:
324: /* (non-Javadoc)
325: * @see java.lang.Object#toString()
326: */
327: public String toString() {
328: StringBuffer strbuf = new StringBuffer();
329:
330: strbuf.append("WepPageEngine Session details:").append(
331: m_Session.toString());
332:
333: return strbuf.toString();
334: }
335:
336: /**
337: * Renders page to <code>OutputStream</code> using the page's default renderer
338: *
339: * @param page
340: * @param state
341: * @param out
342: * @throws PublishException
343: * @throws RenderException
344: */
345: public void render(WebPage page, State state, OutputStream out)
346: throws PublishException, RenderException {
347:
348: if (m_logger.isLoggable(Level.FINE)) {
349: m_logger.logp(Level.FINE, this .getClass().getName(),
350: "render", "Rendering page " + page.getId());
351: }
352:
353: XMLDocument xdoc = createXML(page, state);
354:
355: try {
356: PageRenderer renderer = PageRendererFactory
357: .getRenderer(page.getXSL().getOutputType());
358:
359: renderer.render(xdoc, page.getXSL().getTemplates(), out);
360: logPublish(page, state);
361: } catch (DataAccessException e) {
362: throw new PublishException(
363: "Error occured accessing page data", e);
364: }
365: }
366:
367: /**
368: * Renders page to <code>OutputStream</code> using the given renderer.
369: *
370: * @param page
371: * @param state
372: * @param renderer
373: * @param out
374: * @throws PublishException
375: * @throws RenderException
376: */
377: public void render(WebPage page, State state,
378: PageRenderer renderer, OutputStream out)
379: throws PublishException, RenderException {
380: XMLDocument xdoc = createXML(page, state);
381:
382: try {
383: renderer.render(xdoc, page.getXSL().getTemplates(), out);
384:
385: logPublish(page, state);
386:
387: } catch (DataAccessException e) {
388: throw new PublishException(
389: "Error occured accessing page data", e);
390: }
391: }
392:
393: /*----------------------------------------------------------------------------
394: Protected Functions
395: -----------------------------------------------------------------------------*/
396:
397: /**
398: * Logs the publishing of the given web page.
399: *
400: * @param page
401: * @param state
402: * @throws PublishException
403: */
404: protected void logPublish(WebPage page, State state)
405: throws PublishException {
406:
407: try {
408: PublishLogEvent event = new PublishLogEvent();
409:
410: event.setEventObject(page);
411: event.setState(state);
412:
413: EventLogController.getInstance().logEvent(event);
414: } catch (PopulateException e) {
415: throw new PublishException("Error setting state of event",
416: e);
417: } catch (LogException e) {
418: throw new PublishException("Error logging event", e);
419: }
420:
421: }
422:
423: /**
424: * Processes a 'Navigation' element, returning a new 'Navigation' element with
425: * the appropriate content.
426: *
427: * @param nav
428: * @param state
429: * @param output
430: * @return
431: * @throws PublishException
432: */
433: protected Element navigation(Element nav, State state,
434: HarmoniseOutput output) throws PublishException {
435: Element tempEl = null;
436:
437: Element newNav = output.createElement(WebPage.TAG_NAVIGATION);
438:
439: Element childNav = null;
440:
441: NamedNodeMap navAttributes = nav.getAttributes();
442: Node navName = navAttributes.getNamedItem("name");
443:
444: newNav.setAttribute("name", navName.getNodeValue());
445:
446: Node navDescription = navAttributes.getNamedItem("description");
447:
448: if (navDescription != null) {
449: newNav.setAttribute("description", navDescription
450: .getNodeValue());
451: }
452:
453: NodeList children = nav.getChildNodes();
454:
455: for (int i = 0; i < children.getLength(); i++) {
456: if (children.item(i).getNodeType() != Node.ELEMENT_NODE) {
457: continue;
458: }
459:
460: Element next = (Element) children.item(i);
461:
462: if (next.getTagName().equals(Template.TAG_TEMPLATE)) {
463:
464: tempEl = publishTemplate(next, state, output);
465:
466: if (tempEl != null) {
467: newNav.appendChild(tempEl);
468: }
469:
470: } else if (next.getTagName().equals(WebPage.TAG_NAVIGATION)) {
471: childNav = null;
472: childNav = navigation(next, state, output);
473:
474: if (childNav != null) {
475: newNav.appendChild(childNav);
476: }
477: } else if (next.getTagName().equals(
478: WebPage.TAG_ANCILLARY_TEXT)) {
479: newNav.appendChild(output.copyNode(next));
480: } else if (next.getTagName()
481: .equals(AbstractObject.TAG_LINK)) {
482: newNav.appendChild(output.copyNode(next));
483: } else {
484:
485: try {
486: Publishable pubObj = HarmoniseObjectFactory
487: .instantiatePublishableObject(this .m_dsi,
488: next, state);
489:
490: newNav.appendChild(pubObj.publish(next, output,
491: state));
492: } catch (HarmoniseFactoryException e) {
493: throw new PublishException(
494: "Error instantiating object from factory",
495: e);
496: } catch (PublishException e) {
497: newNav.appendChild(e.publish(output));
498: }
499:
500: }
501: }
502:
503: return newNav;
504: }
505:
506: /**
507: * Processes a 'Template' element, returning an element which is the result of the
508: * publishing the corresponding <code>Template</code> object.
509: *
510: * @param templateEl
511: * @param state
512: * @param output
513: * @return
514: * @throws PublishException
515: */
516: protected Element publishTemplate(Element templateEl, State state,
517: HarmoniseOutput output) throws PublishException {
518: if (templateEl.getTagName().equalsIgnoreCase(
519: Template.TAG_TEMPLATE) == false) {
520: throw new InvalidXMLElementException("Template tag needed");
521: }
522:
523: Element returnEl = null;
524: Template templ = null;
525: try {
526: templ = (Template) HarmoniseObjectFactory
527: .instantiateHarmoniseObject(m_dsi, Template.class
528: .getName(), Integer.parseInt(templateEl
529: .getAttribute(AbstractObject.ATTRIB_ID)));
530: } catch (NumberFormatException e) {
531: throw new PublishException(e);
532: } catch (HarmoniseFactoryException e) {
533: throw new PublishException(e);
534: }
535: Element templRoot = null;
536: try {
537: templRoot = templ.getTemplateRootElement();
538: } catch (DataAccessException e) {
539: throw new PublishException(
540: "Error occured getting template root", e);
541: }
542: NodeList templChildren = templateEl.getChildNodes();
543:
544: int nPageId = -2;
545: NodeList pageNodes = templateEl
546: .getElementsByTagName(WebPage.TAG_PAGE);
547:
548: if (pageNodes.getLength() > 0) {
549: nPageId = Integer.parseInt(((Element) pageNodes.item(0))
550: .getAttribute(AbstractObject.ATTRIB_ID));
551: }
552:
553: for (int i = 0; i < templChildren.getLength(); i++) {
554: if (templChildren.item(i).getNodeType() == Node.ELEMENT_NODE) {
555: Element child = (Element) templChildren.item(i);
556:
557: if (templRoot.getTagName().equalsIgnoreCase(
558: child.getTagName())) {
559:
560: Publishable pubObj = null;
561: try {
562: pubObj = HarmoniseObjectFactory
563: .instantiatePublishableObject(
564: this .m_dsi, child, state);
565: } catch (HarmoniseFactoryException e) {
566: throw new PublishException(
567: "Error occured getting object from factory",
568: e);
569: }
570:
571: if (pubObj != null) {
572: returnEl = templ.publishObjectToElement(pubObj,
573: output, state);
574:
575: String sStateId = child
576: .getAttribute(State.ATTRIB_STATE_ID);
577:
578: if ((sStateId != null)
579: && (sStateId.length() > 0)) {
580: returnEl.setAttribute(
581: State.ATTRIB_STATE_ID, sStateId);
582: }
583:
584: output.addPageIdToLinkNode(state
585: .getLoggedInUser(), returnEl, nPageId);
586: }
587:
588: }
589: }
590: }
591:
592: return returnEl;
593: }
594:
595: /**
596: * Copies the children of the given <code>State</code>, ignoring any Session tags,
597: * to the <code>Node</code> appendTo.
598: *
599: * @param appendTo
600: * @param state
601: * @param output
602: */
603: protected void copyStateChildren(Node appendTo, State state,
604: HarmoniseOutput output) {
605: Node stateRoot = state.getDocumentElement();
606: NodeList stateElements = stateRoot.getChildNodes();
607:
608: for (int i = 0; i < stateElements.getLength(); i++) {
609:
610: if ((stateElements.item(i).getNodeType() == Node.ELEMENT_NODE)
611: && (((Element) stateElements.item(i)).getTagName()
612: .equals(Session.TAG_SESSION))) {
613: continue;
614: }
615:
616: appendTo
617: .appendChild(output.copyNode(stateElements.item(i)));
618: }
619: }
620:
621: /**
622: * Assign the session id associated with this object to the
623: * given <code>State</code>.
624: *
625: * @param state the state to be assigned the session id
626: */
627: public void assignSessionId(State state) {
628: String sSessionId = m_Session.getSessionId();
629:
630: state.setSessionId(sSessionId);
631: }
632:
633: }
|