View Javadoc

1   /*
2    * Copyright 2000-2004 The Apache Software Foundation.
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * 
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    * 
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.apache.jetspeed.portal.portlets;
18  
19  //standard java stuff
20  import java.io.IOException;
21  import java.io.Reader;
22  import java.io.StringReader;
23  import java.util.Enumeration;
24  import java.util.Hashtable;
25  import java.util.Iterator;
26  
27  //Element Construction Set
28  import org.apache.jetspeed.util.JetspeedClearElement;
29  import org.apache.ecs.ConcreteElement;
30  
31  //standard Jetspeed stuff
32  import org.apache.jetspeed.util.MimeType;
33  import org.apache.jetspeed.util.SimpleTransform;
34  import org.apache.jetspeed.cache.disk.JetspeedDiskCache;
35  import org.apache.jetspeed.portal.PortletException;
36  import org.apache.jetspeed.xml.JetspeedXMLEntityResolver;
37  import org.apache.jetspeed.capability.CapabilityMap;
38  import org.apache.jetspeed.capability.CapabilityMapFactory;
39  import org.apache.jetspeed.services.logging.JetspeedLogFactoryService;
40  import org.apache.jetspeed.services.logging.JetspeedLogger;
41  import org.apache.jetspeed.services.rundata.JetspeedRunData;
42  
43  //turbine
44  import org.apache.turbine.util.RunData;
45  
46  //JAXP support
47  import javax.xml.parsers.DocumentBuilder;
48  import javax.xml.parsers.DocumentBuilderFactory;
49  
50  //XML stuff
51  import org.w3c.dom.Document;
52  import org.w3c.dom.Node;
53  import org.w3c.dom.NodeList;
54  import org.xml.sax.InputSource;
55  import org.xml.sax.SAXException;
56  
57  /***
58  <p>Portlet which renders RDF Site Summary.</p>
59  <p>This portlet uses XML stylesheet for transforming the RSS
60  content into display markup depending on the MimeType requested
61  by the user-agent</p>
62  <p>It accepts the following parameters :
63  <dl>
64  <dt>itemDisplayed</dt>
65  <dd>The number of items from the RSS file to display on screen. Default 15 for HTML, 5 for WML</dd>
66  <dt>showDescription</dt>
67  <dd>Should the portlet show the item descriptions. Must be true or false. Default: true for HTML, false for WML</dd>
68  <dt>showTitle</dt>
69  <dd>Should the portlet show the channel description. Must be true or false. Default: true for HTML, false for WML</dd>
70  <dt>stylesheet[.<mime>]</dt>
71  <dd>The stylesheet URL. If a mime-type is specified, the stylesheet
72  is only used for this mime-type</dd>
73  </dl>
74  @author <A HREF="mailto:raphael@apache.org">Raphaël Luta</A>
75  @version $Id: NewRSSPortlet.java,v 1.22 2004/02/23 04:03:34 jford Exp $
76  */
77  public class NewRSSPortlet extends FileWatchPortlet 
78  {
79  
80      /***
81       * Static initialization of the logger for this class
82       */    
83      private static final JetspeedLogger logger = JetspeedLogFactoryService.getLogger(NewRSSPortlet.class.getName());
84      
85      public final static String ERROR_NOT_VALID = "This does not appear to be an RSS document";
86      public final static String INVALID_TYPE = "Unable to display for this browser";
87  
88      private Document document = null;
89      private Hashtable stylesheets = null;
90      private Hashtable params = null;
91  
92      /***
93          This method loads the init parameters and
94          parse the document tied to this portlet
95      */
96      public void init( ) throws PortletException {
97  
98          // first make sure we propagate init
99          super.init();
100 
101         DocumentBuilder parser = null;
102         String url = null;
103 
104         // load stylesheets available
105         stylesheets = new Hashtable();
106         params = new Hashtable();
107         Iterator i = this.getPortletConfig().getInitParameterNames();
108         while (i.hasNext()) {
109             String name = (String)i.next();
110             String base = MimeType.HTML.toString();
111 
112             if (name.startsWith("stylesheet")) {
113                 int idx=-1;
114                 if ((idx=name.indexOf("."))>-1) {
115                     base= name.substring(idx+1,name.length());
116                 }
117                 stylesheets.put(base, getPortletConfig().getInitParameter(name));
118             } else {
119                 params.put(name.toLowerCase(), getPortletConfig().getInitParameter(name));
120             }
121         }
122 
123         // read content, clean it, parse it and cache the DOM
124         try
125         {
126             final DocumentBuilderFactory docfactory = DocumentBuilderFactory.newInstance();
127             //Have it non-validating
128             docfactory.setValidating(false);
129             parser= docfactory.newDocumentBuilder();
130             parser.setEntityResolver(new JetspeedXMLEntityResolver() );
131 
132             url = getPortletConfig().getURL();
133             String content = JetspeedDiskCache.getInstance().getEntry( url ).getData();
134            CapabilityMap xmap =
135                CapabilityMapFactory.getCapabilityMap(CapabilityMapFactory.AGENT_XML);
136             setContent( new JetspeedClearElement(content), xmap );
137             InputSource isrc = new InputSource( this.cleanse( content ) );
138             isrc.setSystemId( url );
139             isrc.setEncoding("UTF-8");
140             this.document = parser.parse( isrc );
141             this.setMetainfo(document);
142 
143         } catch ( Throwable t )
144         {
145 
146             String message = "RSSPortlet:  Couldn't parse out XML document -> " +
147                               url;
148 
149             logger.error( message, t );
150             throw new PortletException( t.getMessage() );
151         }
152 
153 
154     }
155 
156     /***
157      * Parse out title and description
158      * 
159      * @param document
160      */
161     private void setMetainfo(Document document) throws PortletException
162     {
163         //Determine title and description for this portlet
164         String title = null;
165         String description = null;
166 
167         //now find the channel node.
168         Node channel = null;
169 
170         NodeList list = document.getElementsByTagName( "channel" );
171 
172         if ( list.getLength() != 1 ) {
173             throw new PortletException( ERROR_NOT_VALID );
174         }
175 
176         channel = list.item( 0 );
177 
178         Node tn = getNode( channel, "title" );
179 
180         if ( tn == null ) {
181             throw new PortletException( ERROR_NOT_VALID );
182         } 
183         else 
184         {
185             Node fc = tn.getFirstChild();
186             if (fc != null)
187             {
188                 title = fc.getNodeValue();
189             }
190         }
191 
192         Node dn = getNode( channel, "description" );
193 
194         if ( dn != null ) 
195         {
196             Node fc = dn.getFirstChild();
197             if (fc != null)
198             {
199                 description = fc.getNodeValue();
200             }
201         }
202 
203         this.setTitle( title );
204         this.setDescription( description );
205     }
206 
207     /***
208     This methods outputs the content of the portlet for a given
209     request.
210 
211     @param data the RunData object for the request
212     @return the content to be displayed to the user-agent
213     */
214     public ConcreteElement getContent( RunData data ) 
215     {
216         if (org.apache.jetspeed.util.PortletSessionState.getPortletConfigChanged(this, data))
217         {
218             try 
219             {
220                 init(); 
221             }
222             catch (PortletException pe)
223             {
224                 logger.error("Exception",  pe);
225             }
226         }
227         CapabilityMap map = ((JetspeedRunData)data).getCapability();
228         String type = map.getPreferredType().toString();
229         ConcreteElement content = new JetspeedClearElement(INVALID_TYPE);
230         String stylesheet = (String)stylesheets.get(type);
231 
232         if (stylesheet != null) {
233             content = getContent( data, map );
234             if ( content == null ) {
235                 try {
236                     content = new JetspeedClearElement(
237                         SimpleTransform.transform( this.document,
238                                                    stylesheet,
239                                                    this.params ) );
240                     setContent( content, map );
241                 } catch ( SAXException e ) {
242                     logger.error("Exception",  e);
243                     content = new JetspeedClearElement(e.getMessage());
244                 }
245             }
246         }
247         else
248         {
249             if (map.getPreferredType().equals(MimeType.XML))
250             {
251                 return getContent( data, map );
252             }
253         }
254 
255         return content;
256     }
257 
258     /***
259     This portlet supports has many types as those
260     it has stylesheets defined for in its parameters
261 
262     @see Portlet#supportsType
263     @param mimeType the MIME type queried
264     @return true if the portlet knows how to display
265     content for mimeType
266     */
267     public boolean supportsType( MimeType mimeType ) {
268 
269         Enumeration en = stylesheets.keys();
270         while(en.hasMoreElements()) {
271             String type = (String)en.nextElement();
272             if (type.equals(mimeType.toString())) return true;
273         }
274 
275         return false;
276     }
277 
278     /***
279     Utility method for traversing the document parsed
280     DOM tree and retrieving a Node by tagname
281 
282     @param start the parent node for the search
283     @param name the tag name to be searched for
284     @return the first child node of start whose tagname
285     is name
286     */
287     private final Node getNode( Node start, String name ) {
288 
289         NodeList list = start.getChildNodes();
290 
291         for ( int i = 0; i < list.getLength(); ++i ) {
292 
293             Node node = list.item( i );
294 
295             if ( node.getNodeName().equals( name ) ) {
296                 return node;
297             }
298         }
299         return null;
300     }
301 
302     /***
303     Given a URL to some content, clean the content to Xerces can handle it
304     better.  Right now this involves:
305     <ul>
306         <li>
307             If the document doesn't begin with "<?xml version=" truncate the
308             content until this is the first line
309         </li>
310 
311     </ul>
312 
313     */
314     private Reader cleanse( String content ) throws IOException {
315 
316         String filtered = null;
317 
318         //specify the XML declaration to search for... this is just a subset
319         //of the content but it will always exist.
320         String XMLDECL = "<?xml version=";
321 
322         int start = content.indexOf( XMLDECL );
323 
324         if ( start <= 0 ) {
325             filtered = content;
326         } else {
327             filtered = content.substring( start, content.length() );
328         }
329 
330         return new StringReader( filtered );
331     }
332 
333 }
334