www.pudn.com > sxg.rar > CacheHandler.java


/*
 * Copyright (c) 2003 Jens Mueller
 *
 * 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, 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; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

/**
	Singleton class for caching of requested data in XML format. Cleans up cache
	periodically using inner class CachePurger, which forms an extra thread.

 	@author Jens Mueller
 */

import java.io.PrintWriter;
import java.util.List;
import java.util.Iterator;

import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.Attribute;
import org.dom4j.Node;
import org.dom4j.Branch;
import org.dom4j.Namespace;
import org.dom4j.XPath;
import org.dom4j.io.XMLWriter;
import org.dom4j.io.OutputFormat;

public class CacheHandler
{
	private static CacheHandler _instance;
	
	private Document _cache;
	private CachePurger _purger;
	private boolean _active;
	private Thread _thisThread;
	
	/**
		Cleans up cache periodically. The interval is determined by parameter
		cachePurgeIntervalSecs in servlet's deployment descriptor.
	 */
	private class CachePurger implements Runnable
	{
		private long _sleepIntervalMillis;
		
		CachePurger( long interval )
		{
			_sleepIntervalMillis = interval;
			_active = true;
			_thisThread = new Thread( this );
			_thisThread.start();
		}
		
		public void run()
		{
			while ( _active )
			{
				try
				{
					_thisThread.sleep( _sleepIntervalMillis * 1000 );
				}
				catch ( InterruptedException e )
				{
				}
				purge();
			}
		}
		
		public void stopExecution()
		{
			_active = false;
		}
	}
	
	
	
	
	private CacheHandler()
	{
		_cache = DocumentHelper.createDocument();
		Element data = _cache.addElement( "cache" );
		_purger = new CachePurger( GatewayConf.getCachePurgeIntervalSecs() );
	}
	
	
	/**
	 	returns the running instance. If not instanciated yet, it constructs a new 
	 	CacheHandler.
	 */
	private static CacheHandler getInstance()
	{
		if ( _instance == null )
			_instance = new CacheHandler();
		return _instance;
	}
	
	
	
	/** 		returns cached content, if up to date. If no content is found null is returned

		@param type	the table- or scalargroup-type to be resolved from cache 	 	@param req		the context / SNMP-Agent the content is cached for 	 	@return CacheRequestResult	the result of resolving or null, if no content up to date
	 												is found 	 */
	public static synchronized CacheRequestResult getFromCache( GwComplexType type, 
																								ContextGetRequest req )
	{
		CacheHandler handler = getInstance();
		Node matchNode = get( type, req );
		if ( matchNode != null )
		{
			Element context = matchNode.getParent();
			Attribute timeAttribute = context.attribute( GatewayConf.TIME );
			if ( GwDate.timeDiff( GwDate.getCurrent(), timeAttribute.getValue() ) < 
			   ( GatewayConf.getCachingTimeSecs() * 1000 ) )
				return new CacheRequestResult( timeAttribute.getValue(), context );			
		}
		return null;
	}
	
	
	/**
	 	iterates over cached content and removes data no longer up to date. periodically
	 	called by CachePurger 	 */
	private static synchronized void purge()
	{
		CacheHandler handler = getInstance();
		List elements = handler._cache.getRootElement().elements( "context" );
		for ( Iterator iterator = elements.iterator(); iterator.hasNext(); )
		{
			Element context = ( Element )iterator.next();
			Attribute timeAttribute = context.attribute( GatewayConf.TIME );
			if ( GwDate.timeDiff( GwDate.getCurrent(), timeAttribute.getValue() ) > 
			   ( GatewayConf.getCachingTimeSecs() * 1000 ) )
			{
				detach( context );
			}
		}		
	}

	
	/** 		removes an XML-Element from cache.

		@param elem	the Element to be removed 	 */
	private static synchronized void detach( Element elem )
	{
		elem.detach();
	}
	

	/**
	 	Adds content to the cache. 
	 	@param type	the table- or scalargroup-type to be added to cache 	 	@param req		the context / SNMP-Agent the content is cached for 	 */
	public static synchronized void cacheIn( GwComplexType type, ContextGetRequest req )
	{
		CacheHandler handler = getInstance();
		detach( type, req );
		
		handler._cache.getRootElement().appendContent( type.getTempDocument() );
	}
	
	
	
	/**
		removes content from cache. Called after issuing of SNMP-set-requests.

	 	@param type	the table- or scalargroup-type to be removed from cache 	 	@param req		the context / SNMP-Agent the content is cached for 	 */
	public static synchronized void removeFromCache(	GwComplexType type, 
																				ContextSetRequest req )
	{
		CacheHandler handler = getInstance();

		String PORT = GatewayConf.GET_PORT+"=\""+Integer.toString( req.getPort() ) + "\"";
		String IPADDR = GatewayConf.GET_IPADDR+"=\""+req.getIPaddr()+"\" and";
		String HOSTNAME = 	GatewayConf.GET_HOSTNAME+"=\""
					    	+ req.getInetAddress().getCanonicalHostName()+"\" and";


		XPath path = 	type.getTempDocument().createXPath("/cache/"
								+ GatewayConf.GET_AGENTELEMENT+"["+ IPADDR 
								+ HOSTNAME + PORT +"]/"+type.getName());
								
		Node node = path.selectSingleNode( handler._cache );
		
		if( node != null )
		{
			detach( node.getParent() );
		}
	}


	/**
		removes content from cache.

	 	@param type	the table- or scalargroup-type to be removed from cache
	 	@param req		the context / SNMP-Agent the content is cached for
	 */
	private static synchronized void detach( GwComplexType type, ContextGetRequest req )
	{
		CacheHandler handler = getInstance();
		Node matchNode = get( type, req );
		if ( matchNode != null )
		{
			//Element context = get( type, req ).getParent();
			//detach( context );
			detach( matchNode.getParent() );
		}
	}


	/**
		searches for cached content and returns the XML-Node if found.

		@param type	the table- or scalargroup-type to be resolved from cache
	 	@param req		the context / SNMP-Agent the content is cached for
	 	@return Node	the XML-Node of the content, null if no content is found 	 */
	private static synchronized Node get( GwComplexType type, ContextGetRequest req )
	{
		CacheHandler handler = getInstance();
		String PORT = GatewayConf.GET_PORT+"=\""+Integer.toString( req.getPort() ) + "\" and";
		String COMMSTRING = GatewayConf.GET_COMM_STR+"=\""+req.getCommString()+ "\"";
		String IPADDR = GatewayConf.GET_IPADDR+"=\""+req.getIPaddr()+"\" and";
		String HOSTNAME = 	GatewayConf.GET_HOSTNAME+"=\""
					    	+ req.getInetAddress().getCanonicalHostName()+"\" and";


		XPath path = 	type.getTempDocument().createXPath("/cache/"
								+ GatewayConf.GET_AGENTELEMENT+"["+ IPADDR 
								+ HOSTNAME + PORT + COMMSTRING +"]/"+type.getName());
								
		Node node = path.selectSingleNode( handler._cache );

		if ( node != null )
			return node;
		else	
			return null;
	}
	
	
	
	/**
	 	writes textual representation of cached content to writer for debugging
	 */
	public static void dump( PrintWriter writer )
	{
		OutputFormat outputformat = new OutputFormat( "  ", true );
		XMLWriter xmlWriter = new XMLWriter( writer, outputformat );
		try {xmlWriter.write( getInstance()._cache );}
		catch (Throwable th) {};
	}
}