/* ====================================================================
* The Apache Software License, Version 1.1
*
* Copyright (c) 2000-2001 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Apache" and "Apache Software Foundation" and
* "Apache Jetspeed" must not be used to endorse or promote products
* derived from this software without prior written permission. For
* written permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache" or
* "Apache Jetspeed", nor may "Apache" appear in their name, without
* prior written permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
package org.apache.jetspeed.portal.portlets;
//standard java stuff
import java.io.*;
import java.util.*;
//Element Construction Set
import org.apache.jetspeed.util.JetspeedClearElement;
import org.apache.ecs.ConcreteElement;
//standard Jetspeed stuff
import org.apache.jetspeed.util.*;
import org.apache.jetspeed.cache.disk.*;
import org.apache.jetspeed.portal.*;
import org.apache.jetspeed.xml.JetspeedXMLEntityResolver;
import org.apache.jetspeed.capability.CapabilityMap;
import org.apache.jetspeed.capability.CapabilityMapFactory;
import org.apache.jetspeed.services.rundata.JetspeedRunData;
//turbine
import org.apache.turbine.util.*;
//JAXP support
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
//XML stuff
import org.w3c.dom.*;
import org.xml.sax.*;
/**
<p>Portlet which renders RDF Site Summary.</p>
<p>This portlet uses XML stylesheet for transforming the RSS
content into display markup depending on the MimeType requested
by the user-agent</p>
<p>It accepts the following parameters :
<dl>
<dt>itemDisplayed</dt>
<dd>The number of items from the RSS file to display on screen. Default 15 for HTML, 5 for WML</dd>
<dt>showDescription</dt>
<dd>Should the portlet show the item descriptions. Must be true or false. Default: true for HTML, false for WML</dd>
<dt>showTitle</dt>
<dd>Should the portlet show the channel description. Must be true or false. Default: true for HTML, false for WML</dd>
<dt>stylesheet[.<mime>]</dt>
<dd>The stylesheet URL. If a mime-type is specified, the stylesheet
is only used for this mime-type</dd>
</dl>
@author <A HREF="mailto:raphael@apache.org">Rapha�l Luta</A>
@version $Id: NewRSSPortlet.java,v 1.17 2002/11/07 20:22:45 raphael Exp $
*/
public class NewRSSPortlet extends FileWatchPortlet {
public final static String ERROR_NOT_VALID = "This does not appear to be an RSS document";
public final static String INVALID_TYPE = "Unable to display for this browser";
private Document document = null;
private Hashtable stylesheets = null;
private Hashtable params = null;
/**
This method loads the init parameters and
parse the document tied to this portlet
*/
public void init( ) throws PortletException {
// first make sure we propagate init
super.init();
DocumentBuilder parser = null;
String url = null;
// load stylesheets available
stylesheets = new Hashtable();
params = new Hashtable();
Iterator i = this.getPortletConfig().getInitParameterNames();
while (i.hasNext()) {
String name = (String)i.next();
String base = MimeType.HTML.toString();
if (name.startsWith("stylesheet")) {
int idx=-1;
if ((idx=name.indexOf("."))>-1) {
base= name.substring(idx+1,name.length());
}
stylesheets.put(base, getPortletConfig().getInitParameter(name));
} else {
params.put(name.toLowerCase(), getPortletConfig().getInitParameter(name));
}
}
// read content, clean it, parse it and cache the DOM
try
{
final DocumentBuilderFactory docfactory = DocumentBuilderFactory.newInstance();
//Have it non-validating
docfactory.setValidating(false);
parser= docfactory.newDocumentBuilder();
parser.setEntityResolver(new JetspeedXMLEntityResolver() );
url = getPortletConfig().getURL();
String content = JetspeedDiskCache.getInstance().getEntry( url ).getData();
CapabilityMap xmap =
CapabilityMapFactory.getCapabilityMap(CapabilityMapFactory.AGENT_XML);
setContent( new JetspeedClearElement(content), xmap );
InputSource isrc = new InputSource( this.cleanse( content ) );
isrc.setSystemId( url );
isrc.setEncoding("UTF-8");
this.document = parser.parse( isrc );
} catch ( Throwable t )
{
String message = "RSSPortlet: Couldn't parse out XML document -> " +
url;
Log.error( message, t );
throw new PortletException( t.getMessage() );
}
//Determine title and description for this portlet
String title = null;
String description = null;
//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 );
}
/**
This methods outputs the content of the portlet for a given
request.
@param data the RunData object for the request
@return the content to be displayed to the user-agent
*/
public ConcreteElement getContent( RunData data ) {
CapabilityMap map = ((JetspeedRunData)data).getCapability();
String type = map.getPreferredType().toString();
ConcreteElement content = new JetspeedClearElement(INVALID_TYPE);
String stylesheet = (String)stylesheets.get(type);
if (stylesheet != null) {
content = getContent( data, map );
if ( content == null ) {
try {
content = new JetspeedClearElement(
SimpleTransform.transform( this.document,
stylesheet,
this.params ) );
setContent( content, map );
} catch ( SAXException e ) {
Log.error( e );
content = new JetspeedClearElement(e.getMessage());
}
}
}
else
{
if (map.getPreferredType().equals(MimeType.XML))
{
return getContent( data, map );
}
}
return content;
}
/**
This portlet supports has many types as those
it has stylesheets defined for in its parameters
@see Portlet#supportsType
@param mimeType the MIME type queried
@return true if the portlet knows how to display
content for mimeType
*/
public boolean supportsType( MimeType mimeType ) {
Enumeration en = stylesheets.keys();
while(en.hasMoreElements()) {
String type = (String)en.nextElement();
if (type.equals(mimeType.toString())) return true;
}
return false;
}
/**
Utility method for traversing the document parsed
DOM tree and retrieving a Node by tagname
@param start the parent node for the search
@param name the tag name to be searched for
@return the first child node of start whose tagname
is name
*/
private 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>
*/
private Reader cleanse( String content ) throws IOException {
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 );
}
}