/*
* Copyright 2000-2004 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.jetspeed.portal.portlets;
//standard java stuff
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.util.Vector;
//Element Construction Set
import org.apache.jetspeed.util.JetspeedClearElement;
//standard Jetspeed stuff
import org.apache.jetspeed.util.SimpleTransform;
import org.apache.jetspeed.cache.disk.JetspeedDiskCache;
import org.apache.jetspeed.portal.PortletException;
import org.apache.jetspeed.services.logging.JetspeedLogFactoryService;
import org.apache.jetspeed.services.logging.JetspeedLogger;
import org.apache.jetspeed.xml.JetspeedXMLEntityResolver;
//JAXP support
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
//XML stuff
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
/**
Portlet to change RDF Site Summary into a portlet format for HTML presentation.
@author <A HREF="mailto:burton@apache.org">Kevin A. Burton</A>
@author <A HREF="mailto:sgala@hisitech.com">Santiago Gala</A>
@version $Id: RSSPortlet.java,v 1.52 2004/02/23 04:03:33 jford Exp $
*/
public class RSSPortlet extends FileWatchPortlet
{
/**
* Static initialization of the logger for this class
*/
private static final JetspeedLogger logger = JetspeedLogFactoryService.getLogger(RSSPortlet.class.getName());
public final static String ERROR_NOT_VALID = "This does not appear to be an RSS document";
/**
The
*/
private Item[] items = new Item[0];
/**
@author <A HREF="mailto:burton@apache.org">Kevin A. Burton</A>
@version $Id: RSSPortlet.java,v 1.52 2004/02/23 04:03:33 jford Exp $
*/
public void init( ) throws PortletException {
DocumentBuilder parser = null;
Document document = null;
String url = null;
try {
final DocumentBuilderFactory docfactory = DocumentBuilderFactory.newInstance();
//Have it non-validating
docfactory.setValidating(false);
parser= docfactory.newDocumentBuilder();
//SGP Changing Resolver to enable Reading through cache
parser.setEntityResolver(new JetspeedXMLEntityResolver() );
url = this.getPortletConfig().getURL();
String content = JetspeedDiskCache.getInstance().getEntry( url ).getData();
InputSource is = new InputSource( this.cleanse( content ) );
//SGP Should make no difference ...
is.setEncoding( "UTF8" );
is.setSystemId( url );
//parser.setFeature( "http://apache.org/xml/features/allow-java-encodings",
// true );
document = parser.parse( is );
} catch ( Throwable t )
{
String message = "RSSPortlet: Couldn't parse out XML document -> " +
url;
logger.error( message, t );
throw new PortletException( t.getMessage() );
}
//SGP giving NullPointer
try {
//now that we have the document set the items for this
this.setItems( this.parseItems( document ) );
String title = null;
String description = null;
//this a hack until DOM2 namespace support becomes better in Xerces.
Node root = document.getFirstChild();
//now find the channel node.
Node channel = null;
NodeList list = document.getElementsByTagName( "channel" );
if ( list.getLength() != 1 ) {
throw new PortletException( ERROR_NOT_VALID );
}
channel = list.item( 0 );
Node tn = getNode( channel, "title" );
if ( tn == null ) {
throw new PortletException( ERROR_NOT_VALID );
} else {
title = tn.getFirstChild().getNodeValue();
}
Node dn = getNode( channel, "description" );
if ( dn != null ) {
description = dn.getFirstChild().getNodeValue();
}
this.setTitle( title );
this.setDescription( description );
//now that we have the DOM we should be able to do a transform here.
String stylesheet = this.getPortletConfig().getInitParameter( "stylesheet" );
if ( stylesheet == null ) {
throw new PortletException( "The 'stylesheet' parameter was not defined." );
}
try {
//Set encoding for the document to utf-8...
String content = SimpleTransform.transform( document,
stylesheet,
this.getPortletConfig().getInitParameters() );
this.setContent( new JetspeedClearElement( content ) );
} catch ( SAXException e ) {
logger.error("Exception", e);
throw new PortletException( e.getMessage() );
}
} catch (Throwable t) {
String message = "RSSPortlet: Couldn't set items for XML document -> " +
url;
logger.error( message, t );
throw new PortletException( t.getMessage() );
}
}
/**
Given a base node... search this for a node with the given name and return
it or null if it does not exist
*/
private static final Node getNode( Node start, String name ) {
NodeList list = start.getChildNodes();
for ( int i = 0; i < list.getLength(); ++i ) {
Node node = list.item( i );
if ( node.getNodeName().equals( name ) ) {
return node;
}
}
return null;
}
/**
Given a URL to some content, clean the content to Xerces can handle it
better. Right now this involves:
<ul>
<li>
If the document doesn't begin with "<?xml version=" truncate the
content until this is the first line
</li>
</ul>
*/
/**
test it...
*/
private Reader cleanse( String content ) throws IOException, SAXException {
String filtered = null;
//specify the XML declaration to search for... this is just a subset
//of the content but it will always exist.
String XMLDECL = "<?xml version=";
int start = content.indexOf( XMLDECL );
if ( start <= 0 ) {
filtered = content;
} else {
filtered = content.substring( start, content.length() );
}
return new StringReader( filtered );
}
/**
Get the items that were defined by this XML content
*/
public Item[] getItems() {
return this.items;
}
public void setItems( Item[] items ) {
this.items = items;
}
//Cacheable interface..
/**
@author <A HREF="mailto:burton@apache.org">Kevin A. Burton</A>
@version $Id: RSSPortlet.java,v 1.52 2004/02/23 04:03:33 jford Exp $
*/
public boolean isCacheable() {
return true;
}
/**
Given a Document, find all the <item>'s and return them as peer's
*/
private Item[] parseItems( Document doc ) {
String root = doc.getDocumentElement().getTagName();
if ( root.equals( "rdf:RDF" ) ) {
NodeList list = doc.getElementsByTagName( "item" );
return getItems( list );
//parse out each nodelist item and create
} else if ( root.equals( "rss" ) ) {
NodeList list = doc.getElementsByTagName( "channel" );
if ( list.getLength() != 1 ) {
//if there aren't any channels there can't be any items.
return new Item[0];
}
Node channel = list.item( 0 );
//how get the items as a nodelist
return getItems( channel.getChildNodes() );
} else if ( root.equals( "xml" ) ) {
NodeList list = doc.getElementsByTagName( "item" );
return getItems( list );
//parse out each nodelist item and create
} else {
//don't know what to do...
return new Item[0];
}
}
/**
After you find the nodelist for items parse it out and get some Items
*/
private Item[] getItems( NodeList items ) {
Vector v = new Vector();
for ( int i = 0; i < items.getLength(); ++i ) {
Node node = items.item( i );
//just make sure this is an <item> element
if ( node.getNodeName().equals( "item" ) == false ) {
continue;
}
NodeList itemChildren = node.getChildNodes();
String title = null;
String link = null;
String description = null;
for ( int j = 0; j < itemChildren.getLength(); ++j ) {
Node child = itemChildren.item( j );
if ( child.getNodeName().equals( "title" ) ) {
if ( child.getFirstChild() != null )
title = child.getFirstChild().getNodeValue();
}
if ( child.getNodeName().equals( "link" ) ) {
if ( child.getFirstChild() != null )
link = child.getFirstChild().getNodeValue();
}
if ( child.getNodeName().equals( "description" ) ) {
if (child.getFirstChild() != null) {
description = child.getFirstChild().getNodeValue();
}
}
}
v.addElement( new Item( title, link, description ) );
}
Item[] foundItems = new Item[ v.size() ];
v.copyInto( foundItems );
return foundItems;
}
/**
Represents an RSS item.
*/
public static class Item {
private String title;
private String link;
private String description;
public Item( String title,
String link,
String description ) {
this( title, link );
this.description = description;
}
public Item ( String title,
String link ) {
this.title = title;
this.link = link;
}
public String getTitle() {
return this.title;
}
public String getLink() {
return this.link;
}
/**
Get the description for this item... it may be null.
*/
public String getDescription() {
return this.description;
}
}
}