001: /*
002: * (C) Copyright 2000 - 2003 Nabh Information Systems, Inc.
003: *
004: * This program is free software; you can redistribute it and/or
005: * modify it under the terms of the GNU General Public License
006: * as published by the Free Software Foundation; either version 2
007: * of the License, or (at your option) any later version.
008: *
009: * This program is distributed in the hope that it will be useful,
010: * but WITHOUT ANY WARRANTY; without even the implied warranty of
011: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
012: * GNU General Public License for more details.
013: *
014: * You should have received a copy of the GNU General Public License
015: * along with this program; if not, write to the Free Software
016: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
017: *
018: */
019:
020: package com.nabhinc.portlet.xsl;
021:
022: import java.io.File;
023: import java.io.IOException;
024: import java.io.StringWriter;
025: import java.io.Writer;
026: import java.net.URL;
027:
028: import javax.portlet.PortletConfig;
029: import javax.portlet.PortletContext;
030: import javax.portlet.PortletException;
031: import javax.portlet.PortletMode;
032: import javax.portlet.PortletPreferences;
033: import javax.portlet.RenderRequest;
034: import javax.portlet.RenderResponse;
035: import javax.portlet.UnavailableException;
036:
037: import javax.xml.transform.TransformerFactory;
038: import javax.xml.transform.Transformer;
039: import javax.xml.transform.stream.StreamSource;
040: import javax.xml.transform.stream.StreamResult;
041:
042: import com.nabhinc.portlet.base.BasePortlet;
043:
044: /**
045: * Portlet for converting XML document into HyperText Markup Language (HTML)
046: * suitable for Web presentation, using the eXtensible Stylesheet Language
047: * (XSL).
048: *
049: * @author Padmanabh Dabke
050: * (c) 2003 Nabh Information Systems, Inc. All Rights Reserved.
051: */
052: public class XSLPortlet extends BasePortlet {
053:
054: /**
055: * Parameter name for XSL URL. The <code>xslURL</code> should begin with
056: * <code>http://</code> or <code>file://</code> . Either XSL URL, file or
057: * path has to be specified. URL will take precedence, following the
058: * (absolute path) file and the "relative" (to the context) path.
059: */
060: public static final String XSL_URL = "xslURL";
061:
062: /**
063: * Parameter name for XSL path, relative to the context path.
064: * The <code>xslPath</code> has to be begun with "/".
065: */
066: public static final String XSL_PATH = "xslPath";
067:
068: /**
069: * Parameter name for absolute path to the XSL file (<code>xslFile</code>).
070: * The other alternative, which is preferable, is specified the absolute
071: * path using <code>file://</code> as the value of <code>xslURL</code>
072: * parameter.
073: */
074: public static final String XSL_FILE = "xslFile";
075:
076: /**
077: * Parameter name for XML document. The <code>docURL</code> should begin with
078: * http:// or file:// (for local document file). Either document URL, file or path
079: * has to be specified. URL will take precedence, following the (absolute
080: * path) file and the "relative" (to the context) path.
081: */
082: public static final String DOC_URL = "docURL";
083:
084: /**
085: * Parameter name for XML document path, relative to the context path.
086: * The <code>docPath</code> has to be begun with "/".
087: */
088: public static final String DOC_PATH = "docPath";
089:
090: /**
091: * Parameter name (<code>docFile</code>) for absolute path to the XML document .
092: * The other alternative, which is preferable, is specified the absolute
093: * path using <code>file://</code> as the value of <code>docURL</code>
094: * parameter.
095: */
096: public static final String DOC_FILE = "docFile";
097:
098: //public static final String REFRESH_PERIOD = "refreshPeriod";
099:
100: /**
101: * The formatted cache content as the result.
102: * <code>cache_content</code> is set as one of the <code>RenderRequest</code>
103: * attribute.
104: */
105: public static final String FORMATTED_CONTENT = "cache_content";
106:
107: /**
108: * Last time the XSL file was modified.
109: */
110: private long xpXSLLastModified = 0;
111:
112: /**
113: * Last time the XSL file was modified.
114: */
115: private long xpDocLastModified = 0;
116:
117: /**
118: * Cached content
119: */
120: private String xpCache = null;
121:
122: /**
123: * Document file path
124: */
125: //private String xpDocPath = null;
126: /**
127: * Stylesheet file path
128: */
129: //private String xpStyleSheetPath = null;
130: /**
131: * Document absolute path file.
132: */
133: private String xpDocFile = null;
134:
135: /**
136: * Stylesheet absolute path file.
137: */
138: private String xpStyleSheetFile = null;
139:
140: /**
141: * Document URL
142: */
143: private String xpDocURL = null;
144:
145: /**
146: * Stylesheet URL
147: */
148: private String xpStyleSheetURL = null;
149:
150: public void init(PortletConfig config) throws PortletException {
151: super .init(config);
152:
153: //Set stylesheet URL.
154: //Notes: URL to the protected target will throw NullPointerException
155: xpStyleSheetURL = config.getInitParameter(XSL_URL);
156:
157: if (xpStyleSheetURL == null) {
158: xpStyleSheetFile = config.getInitParameter(XSL_FILE);
159:
160: if (xpStyleSheetFile == null) {
161: String path = config.getInitParameter(XSL_PATH);
162: xpStyleSheetFile = getAbsoluteFilePath(
163: getPortletContext(), path, "init");
164: }
165: }
166: }
167:
168: public void doView(RenderRequest request, RenderResponse response)
169: throws PortletException, IOException {
170:
171: PortletPreferences pref = request.getPreferences();
172:
173: //Set original document.
174: //Notes: URL to the protected target will throw NullPointerException
175: xpDocURL = pref.getValue(DOC_URL, null);
176:
177: //URL takes precedence.
178: if (xpDocURL == null) {
179: xpDocFile = pref.getValue(DOC_FILE, null);
180:
181: if (xpDocFile == null) {
182: String docPath = pref.getValue(DOC_PATH, null);
183: xpDocFile = getAbsoluteFilePath(getPortletContext(),
184: docPath, "doView");
185: }
186: }
187:
188: setContent(PortletMode.VIEW);
189:
190: //request.setAttribute(FORMATTED_CONTENT, xpCache);
191: Writer w = response.getWriter();
192: w.write(xpCache);
193: }
194:
195: public void doEdit(RenderRequest request, RenderResponse response)
196: throws PortletException, IOException {
197: //the xml document will be editable. Restrict to be able to
198: // set absolute path or xpDocURL only.
199: //setContent(PortletMode.EDIT)
200: super .doEdit(request, response);
201: }
202:
203: /**
204: * Write the contents of this portlet to the output stream.
205: */
206: private void setContent(PortletMode mode) throws PortletException {
207:
208: if (isDataStale(mode))
209: createContent();
210: }
211:
212: private void createContent() throws PortletException {
213:
214: try {
215: // Use the static TransformerFactory.newInstance() method to instantiate
216: // a TransformerFactory. The javax.xml.transform.TransformerFactory
217: // system property setting determines the actual class to instantiate --
218: // org.apache.xalan.transformer.TransformerImpl.
219: TransformerFactory tFactory = TransformerFactory
220: .newInstance();
221:
222: StreamSource source = null;
223:
224: if (xpStyleSheetURL != null) {
225: source = new StreamSource(new URL(xpStyleSheetURL)
226: .openStream());
227:
228: } else { //if (xpStyleSheetPath != null) {
229: source = new StreamSource(new File(xpStyleSheetFile));
230:
231: }
232: // Use the TransformerFactory to instantiate a Transformer that will work with
233: // the stylesheet you specify. This method call also processes the stylesheet
234: // into a compiled Templates object.
235: Transformer transformer = tFactory.newTransformer(source);
236:
237: // Use the Transformer to apply the associated Templates object to an XML document
238: // (foo.xml) and write the output to a file (foo.out).
239: StringWriter sWriter = new StringWriter();
240: if (xpDocURL != null) {
241: transformer.transform(new StreamSource(
242: new URL(xpDocURL).openStream()),
243: new StreamResult(sWriter));
244:
245: } else {//if (xpDocPath != null) {
246: transformer.transform(new StreamSource(new File(
247: xpDocFile)), new StreamResult(sWriter));
248: }
249: xpCache = sWriter.toString();
250: sWriter.close();
251:
252: } catch (Exception ex) {
253: xpCache = "Error in generating content: " + ex.getMessage();
254: error("doView", xpCache);
255: throw new PortletException(ex);
256: }
257:
258: }
259:
260: /**
261: * Verifies if the url or file path of stylesheet/document is valid and if
262: * the file has been modified since the last accessed.
263: * @return true if the file has been modified since the last accessed.
264: * @throws PortletException
265: */
266: private boolean isDataStale(PortletMode mode)
267: throws PortletException {
268: boolean staleData = false;
269:
270: File file = null;
271: URL url = null;
272: //verify if the URL is valid or if the file has been modified since
273: //the last accessed.
274: //if (! mode.equals(PortletMode.EDIT)) {
275: if (xpStyleSheetFile != null) {
276: file = new File(xpStyleSheetFile);
277:
278: if (!file.exists()) {
279: xpCache = "Invalid XSL file path";
280: error("doView", "Invalid XSL file path: "
281: + xpStyleSheetFile);
282: throw new UnavailableException(
283: "Unable to find the stylesheet file: "
284: + xpStyleSheetFile, 3600);
285: }
286:
287: if (file.lastModified() != xpXSLLastModified) {
288: xpXSLLastModified = file.lastModified();
289: staleData = true;
290: }
291: } else if (xpStyleSheetURL != null) {
292: try {
293: url = new URL(xpStyleSheetURL);
294: long lastModified = url.openConnection()
295: .getLastModified();
296: if (lastModified != xpXSLLastModified) {
297: xpXSLLastModified = lastModified;
298: staleData = true;
299: }
300:
301: } catch (Exception ex) {
302: xpCache = "Invalid XSL URL";
303: error("doView", "Invalid XSL URL: " + xpStyleSheetURL);
304: throw new UnavailableException("Unable to find URL: "
305: + xpStyleSheetURL, 3600);
306: }
307: }
308: //}
309:
310: if (xpDocFile != null) {
311: file = new File(xpDocFile);
312: if (!file.exists()) {
313: xpCache = "Invalid Document path";
314: error("doView", "Invalid Document file path: "
315: + xpDocFile);
316: throw new UnavailableException(
317: "Unable to find the document file: "
318: + xpDocFile, 3600);
319: }
320:
321: if (file.lastModified() != xpDocLastModified) {
322: xpDocLastModified = file.lastModified();
323: staleData = true;
324: }
325: } else if (xpDocURL != null) {
326: try {
327: url = new URL(xpDocURL);
328: long lastModified = url.openConnection()
329: .getLastModified();
330: if (lastModified != xpDocLastModified) {
331: xpDocLastModified = lastModified;
332: staleData = true;
333: }
334: } catch (Exception ex) {
335: xpCache = "Invalid Document URL";
336: error("doView", "Invalid Document URL: " + xpDocURL);
337: throw new UnavailableException("Invalid URL: "
338: + xpDocURL, 3600);
339: }
340: }
341: return staleData;
342: }
343:
344: private String getAbsoluteFilePath(PortletContext context,
345: String path, String method) throws PortletException {
346:
347: if (path == null) {
348: error(method, "Missing file URL or path.");
349: throw new UnavailableException(
350: "The URL or path is not specified.");
351: } else if (!path.startsWith("/")) {
352: error(method,
353: "Invalid path. Path has to be begun with / character.");
354: throw new UnavailableException("Invalid path: " + path
355: + ". Path has to be begun with / character.");
356: }
357:
358: return getRealPath(context, path);
359: }
360:
361: }
|