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.Vector;
24  
25  //Element Construction Set
26  import org.apache.jetspeed.util.JetspeedClearElement;
27  
28  //standard Jetspeed stuff
29  import org.apache.jetspeed.util.SimpleTransform;
30  import org.apache.jetspeed.cache.disk.JetspeedDiskCache;
31  import org.apache.jetspeed.portal.PortletException;
32  import org.apache.jetspeed.services.logging.JetspeedLogFactoryService;
33  import org.apache.jetspeed.services.logging.JetspeedLogger;
34  import org.apache.jetspeed.xml.JetspeedXMLEntityResolver;
35  
36  //JAXP support
37  import javax.xml.parsers.DocumentBuilder;
38  import javax.xml.parsers.DocumentBuilderFactory;
39  
40  //XML stuff
41  import org.w3c.dom.Document;
42  import org.w3c.dom.Node;
43  import org.w3c.dom.NodeList;
44  import org.xml.sax.InputSource;
45  import org.xml.sax.SAXException;
46  
47  /***
48  Portlet to change RDF Site Summary into a portlet format for HTML presentation.
49  
50  @author <A HREF="mailto:burton@apache.org">Kevin A. Burton</A>
51  @author <A HREF="mailto:sgala@hisitech.com">Santiago Gala</A>
52  @version $Id: RSSPortlet.java,v 1.52 2004/02/23 04:03:33 jford Exp $
53  */
54  public class RSSPortlet extends FileWatchPortlet 
55  {
56  
57      /***
58       * Static initialization of the logger for this class
59       */    
60      private static final JetspeedLogger logger = JetspeedLogFactoryService.getLogger(RSSPortlet.class.getName());    
61      
62      public final static String ERROR_NOT_VALID = "This does not appear to be an RSS document";
63  
64      /***
65      The
66      */
67      private Item[]              items = new Item[0];
68  
69      /***
70      @author <A HREF="mailto:burton@apache.org">Kevin A. Burton</A>
71      @version $Id: RSSPortlet.java,v 1.52 2004/02/23 04:03:33 jford Exp $
72      */
73      public void init( ) throws PortletException {
74  
75          DocumentBuilder parser = null;
76          Document document = null;
77          String url = null;
78  
79          try {
80              final DocumentBuilderFactory docfactory = DocumentBuilderFactory.newInstance();
81              //Have it non-validating
82              docfactory.setValidating(false);
83              parser= docfactory.newDocumentBuilder();
84              //SGP Changing Resolver to enable Reading through cache
85              parser.setEntityResolver(new JetspeedXMLEntityResolver() );
86  
87              url = this.getPortletConfig().getURL();
88  
89              String content = JetspeedDiskCache.getInstance().getEntry( url ).getData();
90              InputSource is = new InputSource( this.cleanse( content ) );
91  
92              //SGP Should make no difference ...
93              is.setEncoding( "UTF8" );
94              is.setSystemId( url );
95  
96              //parser.setFeature( "http://apache.org/xml/features/allow-java-encodings",
97              //                   true );
98              document = parser.parse( is );
99  
100         } catch ( Throwable t )
101         {
102 
103             String message = "RSSPortlet:  Couldn't parse out XML document -> " +
104                               url;
105 
106             logger.error( message, t );
107             throw new PortletException( t.getMessage() );
108 
109         }
110 
111         //SGP giving NullPointer
112         try {
113             //now that we have the document set the items for this
114 
115             this.setItems( this.parseItems( document ) );
116 
117             String title = null;
118             String description = null;
119 
120             //this a hack until DOM2 namespace support becomes better in Xerces.
121             Node root = document.getFirstChild();
122             //now find the channel node.
123             Node channel = null;
124 
125             NodeList list = document.getElementsByTagName( "channel" );
126 
127             if ( list.getLength() != 1 ) {
128                 throw new PortletException( ERROR_NOT_VALID );
129         }
130 
131             channel = list.item( 0 );
132 
133             Node tn = getNode( channel, "title" );
134 
135             if ( tn == null ) {
136                 throw new PortletException( ERROR_NOT_VALID );
137             } else {
138                 title = tn.getFirstChild().getNodeValue();
139             }
140 
141             Node dn = getNode( channel, "description" );
142 
143             if ( dn != null ) {
144                 description = dn.getFirstChild().getNodeValue();
145             }
146 
147             this.setTitle( title );
148             this.setDescription( description );
149 
150 
151             //now that we have the DOM we should be able to do a transform here.
152 
153             String stylesheet = this.getPortletConfig().getInitParameter( "stylesheet" );
154 
155             if ( stylesheet == null ) {
156                 throw new PortletException( "The 'stylesheet' parameter was not defined." );
157         }
158 
159             try {
160                 //Set encoding for the document to utf-8...
161                 String content = SimpleTransform.transform( document,
162                                                             stylesheet,
163                                                             this.getPortletConfig().getInitParameters() );
164 
165                 this.setContent( new JetspeedClearElement( content ) );
166 
167 
168             } catch ( SAXException e ) {
169                 logger.error("Exception",  e);
170                 throw new PortletException( e.getMessage() );
171             }
172         } catch (Throwable t) {
173             String message = "RSSPortlet:  Couldn't set items for XML document -> " +
174                 url;
175 
176 
177             logger.error( message, t );
178             throw new PortletException( t.getMessage() );
179         }
180 
181 
182     }
183 
184 
185 
186     /***
187     Given a base node... search this for a node with the given name and return
188     it or null if it does not exist
189     */
190     private static final Node getNode( Node start, String name ) {
191 
192         NodeList list = start.getChildNodes();
193 
194         for ( int i = 0; i < list.getLength(); ++i ) {
195 
196             Node node = list.item( i );
197 
198             if ( node.getNodeName().equals( name ) ) {
199                 return node;
200             }
201         }
202         return null;
203     }
204 
205     /***
206     Given a URL to some content, clean the content to Xerces can handle it
207     better.  Right now this involves:
208     <ul>
209         <li>
210             If the document doesn't begin with "<?xml version=" truncate the
211             content until this is the first line
212         </li>
213 
214     </ul>
215 
216     */
217     /***
218     test it...
219     */
220     private Reader cleanse( String content ) throws IOException, SAXException {
221 
222         String filtered = null;
223 
224         //specify the XML declaration to search for... this is just a subset
225         //of the content but it will always exist.
226         String XMLDECL = "<?xml version=";
227 
228         int start = content.indexOf( XMLDECL );
229 
230         if ( start <= 0 ) {
231             filtered = content;
232         } else {
233             filtered = content.substring( start, content.length() );
234         }
235 
236         return new StringReader( filtered );
237     }
238 
239     /***
240     Get the items that were defined by this XML content
241     */
242     public Item[] getItems() {
243         return this.items;
244     }
245 
246     public void setItems( Item[] items ) {
247         this.items = items;
248     }
249 
250 
251     //Cacheable interface..
252 
253     /***
254     @author <A HREF="mailto:burton@apache.org">Kevin A. Burton</A>
255     @version $Id: RSSPortlet.java,v 1.52 2004/02/23 04:03:33 jford Exp $
256     */
257     public boolean isCacheable() {
258         return true;
259     }
260 
261     /***
262     Given a Document, find all the <item>'s and return them as peer's
263     */
264     private Item[] parseItems( Document doc ) {
265 
266         String root = doc.getDocumentElement().getTagName();
267 
268         if ( root.equals( "rdf:RDF" ) ) {
269 
270             NodeList list = doc.getElementsByTagName( "item" );
271 
272             return getItems( list );
273 
274             //parse out each nodelist item and create
275 
276         } else if ( root.equals( "rss" ) ) {
277 
278             NodeList list = doc.getElementsByTagName( "channel" );
279 
280             if ( list.getLength() != 1 ) {
281                 //if there aren't any channels there can't be any items.
282                 return new Item[0];
283             }
284 
285             Node channel = list.item( 0 );
286 
287             //how get the items as a nodelist
288 
289             return getItems( channel.getChildNodes() );
290 
291 
292           } else if ( root.equals( "xml" ) ) {
293 
294             NodeList list = doc.getElementsByTagName( "item" );
295 
296             return getItems( list );
297 
298             //parse out each nodelist item and create
299 
300           } else {
301             //don't know what to do...
302             return new Item[0];
303         }
304 
305     }
306 
307     /***
308     After you find the nodelist for items parse it out and get some Items
309     */
310     private Item[] getItems( NodeList items ) {
311 
312 
313         Vector v = new Vector();
314 
315         for ( int i = 0; i < items.getLength(); ++i ) {
316 
317             Node node = items.item( i );
318 
319             //just make sure this is an <item> element
320             if ( node.getNodeName().equals( "item" ) == false ) {
321                 continue;
322             }
323 
324             NodeList itemChildren = node.getChildNodes();
325 
326             String title = null;
327             String link = null;
328             String description = null;
329 
330             for ( int j = 0; j < itemChildren.getLength(); ++j ) {
331 
332 
333                 Node child = itemChildren.item( j );
334 
335                 if ( child.getNodeName().equals( "title" ) ) {
336                     if ( child.getFirstChild() != null )
337                         title = child.getFirstChild().getNodeValue();
338                 }
339 
340                 if ( child.getNodeName().equals( "link" ) ) {
341                     if ( child.getFirstChild() != null )
342                         link = child.getFirstChild().getNodeValue();
343                 }
344 
345                 if ( child.getNodeName().equals( "description" ) ) {
346                     if (child.getFirstChild() != null) {
347                        description = child.getFirstChild().getNodeValue();
348                     }
349                 }
350 
351 
352             }
353 
354             v.addElement( new Item( title, link, description ) );
355 
356         }
357 
358         Item[] foundItems = new Item[ v.size() ];
359         v.copyInto( foundItems );
360         return foundItems;
361 
362     }
363 
364 	/***
365 	Represents an RSS item.
366 	*/
367 	public static class Item {
368 
369 	    private String title;
370 	    private String link;
371 	    private String description;
372 
373 	    public Item( String title,
374 	                 String link,
375 	                 String description ) {
376 	        this( title, link );
377 	        this.description = description;
378 
379 	    }
380 
381 	    public Item ( String title,
382 	                  String link ) {
383 	        this.title = title;
384 	        this.link = link;
385 	    }
386 
387 	    public String getTitle() {
388 	        return this.title;
389 	    }
390 
391 	    public String getLink() {
392 	        return this.link;
393 	    }
394 
395 	    /***
396 	    Get the description for this item... it may be null.
397 	    */
398 	    public String getDescription() {
399 	        return this.description;
400 	    }
401 
402 	}
403 
404 }