blob: cc616e8ab2a3767decd9ad0018274ba9ca5dd43f [file] [log] [blame]
/*
JSPWiki - a JSP-based WikiWiki clone.
Copyright (C) 2001-2002 Janne Jalkanen (Janne.Jalkanen@iki.fi)
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.ecyrd.jspwiki;
import java.io.IOException;
import java.util.Properties;
import java.util.Collection;
import java.util.HashMap;
import java.util.Date;
import java.util.Iterator;
import org.apache.log4j.Category;
import com.ecyrd.jspwiki.providers.WikiPageProvider;
import com.ecyrd.jspwiki.providers.ProviderException;
/**
* Manages the WikiPages. This class functions as an unified interface towards
* the page providers. It handles initialization and management of the providers,
* and provides utility methods for accessing the contents.
*
* @author Janne Jalkanen
* @since 2.0
*/
// FIXME: This class currently only functions just as an extra layer over providers,
// complicating things. We need to move more provider-specific functionality
// from WikiEngine (which is too big now) into this class.
public class PageManager
{
public static final String PROP_PAGEPROVIDER = "jspwiki.pageProvider";
public static final String PROP_USECACHE = "jspwiki.usePageCache";
public static final String PROP_LOCKEXPIRY = "jspwiki.lockExpiryTime";
static Category log = Category.getInstance( PageManager.class );
private WikiPageProvider m_provider;
private HashMap m_pageLocks = new HashMap();
/**
* The expiry time. Default is 60 minutes.
*/
private int m_expiryTime = 60;
/**
* Creates a new PageManager.
* @throws WikiException If anything goes wrong, you get this.
*/
public PageManager( Properties props )
throws WikiException
{
String classname;
boolean useCache = "true".equals(props.getProperty( PROP_USECACHE ));
m_expiryTime = TextUtil.parseIntParameter( props.getProperty( PROP_LOCKEXPIRY ),
m_expiryTime );
//
// If user wants to use a cache, then we'll use the CachingProvider.
//
if( useCache )
{
classname = "com.ecyrd.jspwiki.providers.CachingProvider";
}
else
{
classname = props.getProperty( PROP_PAGEPROVIDER );
}
try
{
Class providerclass = WikiEngine.findWikiClass( classname,
"com.ecyrd.jspwiki.providers" );
m_provider = (WikiPageProvider)providerclass.newInstance();
log.debug("Initializing page provider class "+m_provider);
m_provider.initialize( props );
}
catch( ClassNotFoundException e )
{
log.error("Unable to locate provider class "+classname,e);
throw new WikiException("no provider class");
}
catch( InstantiationException e )
{
log.error("Unable to create provider class "+classname,e);
throw new WikiException("faulty provider class");
}
catch( IllegalAccessException e )
{
log.error("Illegal access to provider class "+classname,e);
throw new WikiException("illegal provider class");
}
catch( NoRequiredPropertyException e )
{
log.error("Provider did not found a property it was looking for: "+e.getMessage(),
e);
throw e; // Same exception works.
}
catch( IOException e )
{
log.error("An I/O exception occurred while trying to create a new page provider: "+classname, e );
throw new WikiException("Unable to start page provider: "+e.getMessage());
}
//
// Start the lock reaper.
//
new LockReaper().start();
}
/**
* Returns the page provider currently in use.
*/
public WikiPageProvider getProvider()
{
return m_provider;
}
public Collection getAllPages()
throws ProviderException
{
return m_provider.getAllPages();
}
public String getPageText( String pageName, int version )
throws ProviderException
{
return m_provider.getPageText( pageName, version );
}
public void putPageText( WikiPage page, String content )
throws ProviderException
{
m_provider.putPageText( page, content );
}
/**
* Locks page for editing. Note, however, that the PageManager
* will in no way prevent you from actually editing this page;
* the lock is just for information.
*
* @return null, if page could not be locked.
*/
public PageLock lockPage( WikiPage page, String user )
{
PageLock lock = null;
synchronized( m_pageLocks )
{
lock = (PageLock) m_pageLocks.get( page.getName() );
if( lock == null )
{
//
// Lock is available, so make a lock.
//
Date d = new Date();
lock = new PageLock( page, user, d,
new Date( d.getTime() + m_expiryTime*60*1000L ) );
m_pageLocks.put( page.getName(), lock );
log.debug( "Locked page "+page.getName()+" for "+user);
}
else
{
log.debug( "Page "+page.getName()+" already locked by "+lock.getLocker() );
lock = null; // Nothing to return
}
}
return lock;
}
/**
* Marks a page free to be written again. If there has not been a lock,
* will fail quietly.
*
* @param lock A lock acquired in lockPage(). Safe to be null.
*/
public void unlockPage( PageLock lock )
{
if( lock == null ) return;
synchronized( m_pageLocks )
{
PageLock old = (PageLock)m_pageLocks.remove( lock.getPage().getName() );
log.debug( "Unlocked page "+lock.getPage().getName() );
}
}
/**
* Returns the current lock owner of a page. If the page is not
* locked, will return null.
*
* @return Current lock.
*/
public PageLock getCurrentLock( WikiPage page )
{
PageLock lock = null;
synchronized( m_pageLocks )
{
lock = (PageLock)m_pageLocks.get( page.getName() );
}
return lock;
}
public Collection findPages( QueryItem[] query )
{
return m_provider.findPages( query );
}
public WikiPage getPageInfo( String pageName, int version )
throws ProviderException
{
return m_provider.getPageInfo( pageName, version );
}
/**
* Gets a version history of page. Each element in the returned
* Collection is a WikiPage.
* <P>
* @return If the page does not exist, returns null, otherwise a Collection
* of WikiPages.
*/
public Collection getVersionHistory( String pageName )
throws ProviderException
{
if( pageExists( pageName ) )
{
return m_provider.getVersionHistory( pageName );
}
return null;
}
public String getProviderDescription()
{
return m_provider.getProviderInfo();
}
public int getTotalPageCount()
{
try
{
return m_provider.getAllPages().size();
}
catch( ProviderException e )
{
log.error( "Unable to count pages: ",e );
return -1;
}
}
public boolean pageExists( String pageName )
{
return m_provider.pageExists( pageName );
}
/**
* This is a simple reaper thread that runs roughly every minute
* or so (it's not really that important, as long as it runs),
* and removes all locks that have expired.
*/
private class LockReaper extends Thread
{
public void run()
{
while( true )
{
try
{
Thread.sleep( 60 * 1000L );
synchronized( m_pageLocks )
{
Collection entries = m_pageLocks.values();
Date now = new Date();
for( Iterator i = entries.iterator(); i.hasNext(); )
{
PageLock p = (PageLock) i.next();
if( now.after( p.getExpiryTime() ) )
{
i.remove();
log.debug( "Reaped lock: "+p.getPage().getName()+
" by "+p.getLocker()+
", acquired "+p.getAcquisitionTime()+
", and expired "+p.getExpiryTime() );
}
}
}
}
catch( Throwable t ) {}
}
}
}
}