001: /* Copyright 2001 The JA-SIG Collaborative. All rights reserved.
002: * See license distributed with this file and
003: * available online at http://www.uportal.org/license.html
004: */
005:
006: package org.jasig.portal.tools;
007:
008: import java.io.IOException;
009: import java.io.PrintWriter;
010: import java.io.StringWriter;
011: import java.util.Enumeration;
012:
013: import javax.servlet.ServletConfig;
014: import javax.servlet.ServletException;
015: import javax.servlet.http.HttpServlet;
016: import javax.servlet.http.HttpServletRequest;
017: import javax.servlet.http.HttpServletResponse;
018: import javax.servlet.http.HttpSession;
019: import javax.xml.transform.sax.SAXResult;
020: import javax.xml.transform.sax.TransformerHandler;
021:
022: import org.apache.commons.logging.Log;
023: import org.apache.commons.logging.LogFactory;
024: import org.jasig.portal.BrowserInfo;
025: import org.jasig.portal.ChannelRuntimeData;
026: import org.jasig.portal.ChannelSAXStreamFilter;
027: import org.jasig.portal.ChannelStaticData;
028: import org.jasig.portal.IChannel;
029: import org.jasig.portal.IPrivilegedChannel;
030: import org.jasig.portal.MediaManager;
031: import org.jasig.portal.PortalControlStructures;
032: import org.jasig.portal.PortalException;
033: import org.jasig.portal.PortalSessionManager;
034: import org.jasig.portal.StylesheetSet;
035: import org.jasig.portal.UPFileSpec;
036: import org.jasig.portal.security.IPerson;
037: import org.jasig.portal.security.PersonFactory;
038: import org.jasig.portal.serialize.BaseMarkupSerializer;
039: import org.jasig.portal.utils.ResourceLoader;
040: import org.jasig.portal.utils.SAX2BufferImpl;
041: import org.jasig.portal.utils.XSLT;
042: import org.xml.sax.ContentHandler;
043: import org.xml.sax.SAXException;
044:
045: /**
046: * A servlet that allows one to render an IChannel outside of the portal.
047: * @author Peter Kharchenko <pkharchenko@interactivebusiness.com>
048: * @version $Revision: 36731 $
049: */
050: public class ChannelServlet extends HttpServlet {
051: private static final Log LOG = LogFactory
052: .getLog(ChannelServlet.class);
053: public static String detachBaseStart = "detach_";
054: StylesheetSet set;
055: MediaManager mediaM;
056: private boolean initialized = false;
057: private IChannel channel;
058: private String channelName;
059: private long timeOut = 10000; // 10 seconds is the default timeout value
060: private static final String relativeSSLLocation = "ChannelServlet/ChannelServlet.ssl";
061:
062: public void init() throws ServletException {
063: ServletConfig sc = this .getServletConfig();
064: if (sc != null) {
065: // initialize stylesheet set
066: // once JNDI DB access is in place the following line can be removed
067: try {
068: this .set = new StylesheetSet(ResourceLoader
069: .getResourceAsURLString(this .getClass(),
070: relativeSSLLocation));
071: String mediaPropsUrl = ResourceLoader
072: .getResourceAsURLString(this .getClass(),
073: "/properties/media.properties");
074: this .set.setMediaProps(mediaPropsUrl);
075: this .mediaM = MediaManager.getMediaManager();
076: } catch (PortalException pe) {
077: throw new ServletException(pe);
078: }
079: // determine the channel with its parameters
080: String className = sc.getInitParameter("className");
081: channelName = sc.getInitParameter("channelName");
082: String s_timeOut = sc.getInitParameter("timeOut");
083: if (s_timeOut != null) {
084: this .timeOut = Long.parseLong(s_timeOut);
085: }
086: // instantiate channel class
087: try {
088: channel = (org.jasig.portal.IChannel) Class.forName(
089: className).newInstance();
090: // construct a ChannelStaticData object
091: ChannelStaticData sd = new ChannelStaticData();
092: sd.setChannelSubscribeId("singlet");
093: sd.setTimeout(timeOut);
094: // determine the IPerson object
095: IPerson person = PersonFactory.createGuestPerson();
096: sd.setPerson(person);
097: // todo: determine and pass channel publish/subscribe parameters.
098: // sd.setParameters (params);
099: channel.setStaticData(sd);
100: initialized = true;
101: } catch (Exception e) {
102: // some diagnostic state can be saved here
103: LOG.error(e, e);
104: }
105: }
106: }
107:
108: public void doPost(HttpServletRequest req, HttpServletResponse res)
109: throws ServletException, IOException {
110: doGet(req, res);
111: }
112:
113: public void doGet(HttpServletRequest req, HttpServletResponse res)
114: throws ServletException, IOException {
115: if (initialized) {
116: // construct runtime data object
117: ChannelRuntimeData rd = new ChannelRuntimeData();
118: rd.setBrowserInfo(new BrowserInfo(req));
119:
120: for (Enumeration en = req.getParameterNames(); en
121: .hasMoreElements();) {
122: String pName = (String) en.nextElement();
123: if (!pName.startsWith("uP_")) {
124: String[] val = (String[]) req
125: .getParameterValues(pName);
126: rd.put(pName, val);
127: }
128: }
129:
130: try {
131: rd.setUPFile(new UPFileSpec(null,
132: UPFileSpec.RENDER_METHOD, "servletRoot",
133: "singlet", null));
134: } catch (PortalException pe) {
135: LOG.error("unable to construct a UPFile !", pe);
136: }
137:
138: if (channel instanceof IPrivilegedChannel) {
139: // provide as much of PCS as we can
140: PortalControlStructures pcs = new PortalControlStructures();
141: pcs.setHttpServletRequest(req);
142: pcs.setHttpServletResponse(res);
143: try {
144: ((IPrivilegedChannel) channel)
145: .setPortalControlStructures(pcs);
146: } catch (Exception e) {
147: // channel failed to accept portal control structures
148: LOG
149: .error(
150: "channel failed to accept portal control structures.",
151: e);
152: }
153: }
154: // start rendering in a separate thread
155: SAX2BufferImpl buffer = new SAX2BufferImpl();
156: Worker worker = new Worker(channel, rd, buffer);
157: Thread workerThread = new Thread(PortalSessionManager
158: .getThreadGroup(), worker, "servlet renderer");
159: workerThread.start();
160: long startTime = System.currentTimeMillis();
161: // set the mime type
162: res.setContentType(mediaM.getReturnMimeType(req));
163: // set up the serializer
164: BaseMarkupSerializer ser = mediaM.getSerializer(mediaM
165: .getMedia(req), res.getWriter());
166: ser.asContentHandler();
167: // get the framing stylesheet
168: String xslURI = null;
169: try {
170: xslURI = set.getStylesheetURI(req);
171: } catch (PortalException pe) {
172: throw new ServletException(pe);
173: }
174: try {
175: TransformerHandler th = XSLT
176: .getTransformerHandler(xslURI);
177: th.setResult(new SAXResult(ser));
178: try {
179: long wait = timeOut - System.currentTimeMillis()
180: + startTime;
181: if (wait > 0)
182: workerThread.join(wait);
183: } catch (InterruptedException e) {
184: // thread waiting on the worker has been interrupted
185: LOG
186: .error(
187: "thread waiting on the worker has been interrupted.",
188: e);
189: }
190: // kill the working thread
191: // yes, this is terribly crude and unsafe, but I don't see an alternative
192: workerThread.stop();
193: if (worker.done()) {
194: if (worker.successful()) {
195: // unplug the buffer
196: try {
197: org.xml.sax.helpers.AttributesImpl atl = new org.xml.sax.helpers.AttributesImpl();
198: atl.addAttribute("", "name", "name",
199: "CDATA", channelName);
200: // add other attributes: hasHelp, hasAbout, hasEdit
201: th.startDocument();
202: th.startElement("", "channel", "channel",
203: atl);
204: ChannelSAXStreamFilter custodian = new ChannelSAXStreamFilter(
205: th);
206: custodian.setParent(buffer);
207: buffer.stopBuffering();
208: buffer.outputBuffer();
209: th.endElement("", "channel", "channel");
210: th.endDocument();
211: } catch (SAXException e) {
212: // worst case scenario: partial content output :(
213: LOG.error("error during unbuffering", e);
214: }
215: } else {
216: // rendering was not successful
217: Exception e;
218: if ((e = worker.getException()) != null) {
219: // channel generated an exception ... this should be handled
220: StringWriter sw = new StringWriter();
221: e.printStackTrace(new PrintWriter(sw));
222: sw.flush();
223: showErrorMessage(
224: "channel generated exception "
225: + e.toString()
226: + ". Stack trace: "
227: + sw.toString(), res);
228: }
229: // should never get there, unless thread.stop() has seriously messed things up for the worker thread.
230: }
231: } else {
232: // rendering has timed out
233: showErrorMessage("channel rendering timed out", res);
234: }
235: } catch (Exception e) {
236: // some exception occurred during processor initialization or framing transformation
237: showErrorMessage(
238: "Exception occurred during the framing transformation or XSLT processor initialization",
239: res);
240: }
241: } else
242: showErrorMessage("failed to initialize", res);
243: }
244:
245: private void showErrorMessage(String message,
246: HttpServletResponse res) {
247: res.setContentType("text/html");
248: try {
249: PrintWriter out = res.getWriter();
250: out.println("<html>");
251: out.println("<body>");
252: if (channelName != null)
253: out.println("<h1>" + channelName + "</h1>");
254: else
255: out
256: .println("<h1>"
257: + getServletConfig().getServletName()
258: + "</h1>");
259: out.println("<h3>Error !</h3>");
260: out.println("<p>" + message + "<p>");
261: out.println("</body></html>");
262: } catch (Exception e) {
263: // unable to get a writer from the HttpServletResponse object
264: }
265: }
266:
267: // the object that does the actual rendering
268: protected class Worker implements Runnable {
269: private boolean successful;
270: private boolean done;
271: private IChannel channel;
272: private ChannelRuntimeData rd;
273: private ContentHandler contentHandler;
274: private Exception exc = null;
275:
276: public Worker(IChannel ch, ChannelRuntimeData runtimeData,
277: ContentHandler dh) {
278: this .channel = ch;
279: this .contentHandler = dh;
280: this .rd = runtimeData;
281: }
282:
283: public void run() {
284: successful = false;
285: done = false;
286: try {
287: if (rd != null)
288: channel.setRuntimeData(rd);
289: channel.renderXML(contentHandler);
290: successful = true;
291: } catch (Exception e) {
292: this .exc = e;
293: }
294: done = true;
295: }
296:
297: public boolean successful() {
298: return this .successful;
299: }
300:
301: public boolean done() {
302: return this .done;
303: }
304:
305: public Exception getException() {
306: return exc;
307: }
308: }
309:
310: protected IPerson getPerson(HttpServletRequest req) {
311: HttpSession session = req.getSession(false);
312: IPerson person = (IPerson) session.getAttribute("up_person");
313: return person;
314: }
315: }
|