blob: a04e6c34600b14a4fb41b7740d080561d2a94fb0 [file] [log] [blame]
/*
JSPWiki - a JSP-based WikiWiki clone.
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you 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.wiki.action;
import java.io.IOException;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Element;
import net.sourceforge.stripes.action.*;
import net.sourceforge.stripes.validation.Validate;
import org.apache.wiki.WikiEngine;
import org.apache.wiki.api.WikiPage;
import org.apache.wiki.auth.permissions.PagePermission;
import org.apache.wiki.log.Logger;
import org.apache.wiki.log.LoggerFactory;
import org.apache.wiki.rss.RSSGenerator;
import org.apache.wiki.ui.stripes.HandlerPermission;
import org.apache.wiki.ui.stripes.WikiActionBeanContext;
import org.apache.wiki.ui.stripes.WikiRequestContext;
import org.apache.wiki.util.HttpUtil;
/**
* Generates RSS feeds for WikiPages.
*/
@UrlBinding( "/rss.jsp" )
public class RSSActionBean extends AbstractPageActionBean
{
private static final Logger log = LoggerFactory.getLogger( RSSActionBean.class );
/**
* {@inheritDoc} The {@code page} attribute is required.
*/
@Validate( required = true, on = "rss" )
public void setPage( WikiPage page )
{
super.setPage( page );
}
/**
* Generates a {@link StreamingResolution} containing the RSS feed for the
* current page. If the feed is already cached and hasn't expired, it is
* returned. Otherwise, a new feed is generated.
*
* @return the resolution
* @throws IOException if anything goes wrong
*/
@DefaultHandler
@HandlesEvent( "rss" )
@HandlerPermission( permissionClass = PagePermission.class, target = "${page.name}", actions = PagePermission.VIEW_ACTION )
@WikiRequestContext( "rss" )
public Resolution rss() throws IOException
{
// Get the RSS mode and type
final WikiActionBeanContext context = getContext();
final HttpServletRequest request = context.getRequest();
final HttpServletResponse response = context.getResponse();
final WikiEngine wiki = context.getEngine();
final WikiPage page = context.getPage();
final String mode = getRssMode( request );
final String type = getRssType( request );
final Cache cache = getRssCache();
// Redirect if baseURL not set or RSS generation not on
if( wiki.getBaseURL().length() == 0 )
{
response.sendError( 500, "The jspwiki.baseURL property has not been defined for this wiki - cannot generate RSS" );
return null;
}
if( wiki.getRSSGenerator() == null )
{
response.sendError( 404, "RSS feeds are disabled." );
return null;
}
if( page == null || !wiki.pageExists( page.getName() ) )
{
response.sendError( 404, "No such page " + page.getName() );
return null;
}
// All good, so generate the RSS now.
Resolution r = new StreamingResolution( RSSGenerator.getContentType( type ) + "; charset=UTF-8" ) {
@Override
protected void stream( HttpServletResponse response ) throws Exception
{
// Force the TranslatorReader to output absolute URLs
// regardless of the current settings.
context.setVariable( WikiEngine.PROP_REFSTYLE, "absolute" );
//
// Now, list items.
//
List<WikiPage> changed;
if( mode.equals( "blog" ) )
{
org.apache.wiki.plugin.WeblogPlugin plug = new org.apache.wiki.plugin.WeblogPlugin();
changed = plug.findBlogEntries( wiki.getContentManager(), page.getName(), new Date( 0L ), new Date() );
}
else
{
changed = wiki.getVersionHistory( page.getName() );
}
//
// Check if nothing has changed, so we can just return a 304
//
boolean hasChanged = false;
Date latest = new Date( 0 );
for( Iterator<WikiPage> i = changed.iterator(); i.hasNext(); )
{
WikiPage p = i.next();
if( !HttpUtil.checkFor304( request, p ) )
hasChanged = true;
if( p.getLastModified().after( latest ) )
latest = p.getLastModified();
}
if( !hasChanged && changed.size() > 0 )
{
response.sendError( HttpServletResponse.SC_NOT_MODIFIED );
log.info( "Requested RSS feed for page " + page.getPath().toString() + ", but nothing changed." );
return;
}
response.addDateHeader( "Last-Modified", latest.getTime() );
response.addHeader( "ETag", HttpUtil.createETag( page ) );
//
// Try to get the RSS XML from the cache. We build the hashkey
// based on the LastModified-date, so whenever it changes, so
// does
// the hashkey so we don't have to make any special
// modifications.
//
// TODO: Figure out if it would be a good idea to use a
// disk-based
// cache here.
//
String hashKey = page.getName() + ";" + mode + ";" + type + ";" + latest.getTime();
String rss = "";
Element e = cache.get( hashKey );
if( e != null )
{
log.info( "Returning cached RSS feed for page " + page.getPath().toString() + "." );
rss = (String) e.getValue();
}
else
{
try
{
log.info( "Generating RSS feed for page " + page.getPath().toString() + "." );
rss = wiki.getRSSGenerator().generateFeed( context, changed, mode, type );
cache.put( new Element( hashKey, rss ) );
}
catch( Exception e1 )
{
}
}
response.getWriter().println( rss );
}
};
return r;
}
/**
* Returns the RSS cache. If one does not exist, it will be initialized.
*
* @return the cache
*/
private Cache getRssCache()
{
CacheManager cacheManager = CacheManager.getInstance();
Cache cache = cacheManager.getCache( "jspwiki.rssCache" );
if( cache == null )
{
cache = new Cache( "jspwiki.rssCache", 500, false, false, 24 * 3600, 24 * 3600 );
cacheManager.addCache( cache );
}
return cache;
}
/**
* Returns the RSS mode, based on request parameters. The default is
* {@link RSSGenerator#MODE_BLOG}.
*
* @param request the HTTP request
* @return the mode
*/
private String getRssMode( HttpServletRequest request )
{
String mode = request.getParameter( "mode" );
if( mode == null || !(mode.equals( RSSGenerator.MODE_BLOG ) || mode.equals( RSSGenerator.MODE_WIKI )) )
{
mode = RSSGenerator.MODE_BLOG;
}
return mode;
}
/**
* Returns the RSS type, based on request parameters. The default is
* {@link RSSGenerator#RSS20}.
*
* @param request the HTTP request
* @return the type
*/
private String getRssType( HttpServletRequest request )
{
String type = request.getParameter( "type" );
if( type == null
|| !(type.equals( RSSGenerator.RSS10 ) || type.equals( RSSGenerator.RSS20 ) || type.equals( RSSGenerator.ATOM )) )
{
type = RSSGenerator.RSS20;
}
return type;
}
}