/*=============================================================================*
* Copyright 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.ws.resource.impl;
import commonj.timers.Timer;
import commonj.timers.TimerManager;
import org.apache.commons.collections.map.ReferenceMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.ws.resource.InvalidResourceKeyException;
import org.apache.ws.resource.JndiConstants;
import org.apache.ws.resource.NoSuchResourceException;
import org.apache.ws.resource.PersistenceCallback;
import org.apache.ws.resource.Resource;
import org.apache.ws.resource.ResourceException;
import org.apache.ws.resource.ResourceHome;
import org.apache.ws.resource.ResourceKey;
import org.apache.ws.resource.PropertiesResource;
import org.apache.ws.resource.i18n.Keys;
import org.apache.ws.resource.i18n.MessagesImpl;
import org.apache.ws.resource.lifetime.ScheduledResourceTerminationResource;
import org.apache.ws.resource.properties.ResourcePropertySet;
import org.apache.ws.util.Cache;
import org.apache.ws.util.i18n.Messages;
import org.apache.ws.util.jndi.Initializable;
import org.apache.ws.util.jndi.JNDIUtils;
import org.apache.ws.util.lock.Lock;
import org.apache.ws.util.lock.LockManager;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
/**
* LOG-DONE
* An implementation of the <code>ResourceHome</code> interface. This implementation was designed to work with resources
* that implement the {@link PersistenceCallback PersistenceCallback} interface as well as memory resident resources. If
* the resource class implements the {@link PersistenceCallback PersistenceCallback} interface
* <code>SoftReference</code>s will be used to recycle resource objects. The resource class implementation is
* responsible for saving its state to disk. This implementation will <b>not</b> call {@link PersistenceCallback#store()
* PersistenceCallback.store()}. The resource implementation must have a default constructor. <br><br> Configuration
* options: <ul> <li> sweeperDelay - configures how often the resource sweeper runs in msec. By default the resource
* sweeper runs every minute. For example:
* <pre>
* <parameter>
* <name>sweeperDelay</name>
* <value>60000</value>
* </parameter>
* </pre>
* <li> resourceClass - configures the name of the resource class. For example:
* <pre>
* <parameter>
* <name>resourceClass</name>
* <value>org.globus.wsrf.samples.counter.PersistentCounter</value>
* </parameter>
* </pre>
* <li> resourceKeyType - configures the key type class. By default <code>java.lang.String</code> is used. For example:
* <pre>
* <parameter>
* <name>resourceKeyType</name>
* <value>java.lang.Integer</value>
* </parameter>
* </pre>
* <li> resourceKeyName - configures the key name. For example:
* <pre>
* <parameter>
* <name>resourceKeyName</name>
* <value>{http://counter.com}CounterKey</value>
* </parameter>
* </pre>
* </ul> <br> <b>Note:</b> Must be deployed with <code>org.globus.wsrf.jndi.BeanFactory</code> in JNDI or user must
* first call {@link #init() initialize()} method. Also when overriding the {@link #init() initialize()} method make
* sure to call <code>super.initialize();</code>.
*
* @author Globus, Ian P. Springer
*/
public abstract class AbstractResourceHome
implements ResourceHome,
Initializable
{
private static final int DEFAULT_SWEEPER_DELAY = 60000;
private static final Log LOG = LogFactory.getLog( AbstractResourceHome.class );
public static final Messages MSG = MessagesImpl.getInstance();
/**
* DOCUMENT_ME
*/
protected Map m_resources;
/**
* DOCUMENT_ME
*/
protected String m_resourceClassName;
private boolean m_resourceIsPersistent;
private String m_resourceKeyClassName;
private String m_resourceKeyName;
private String m_wsdlTargetNamespace;
private String m_serviceClassName;
/**
* DOCUMENT_ME
*/
protected LockManager m_lockManager;
private String m_cacheLocation;
private Cache m_cache;
private long m_sweeperDelay = DEFAULT_SWEEPER_DELAY;
private Sweeper m_sweeper;
private boolean m_initialized;
private Class m_resourceClass;
private Class m_serviceClass;
private Class m_resourceKeyClass;
/**
* DOCUMENT_ME
*
* @param jndiLocation DOCUMENT_ME
*/
public void setCacheLocation( String jndiLocation )
{
m_cacheLocation = jndiLocation;
}
/**
* DOCUMENT_ME
*
* @return DOCUMENT_ME
*/
public String getCacheLocation( )
{
return m_cacheLocation;
}
/**
* DOCUMENT_ME
*
* @param resourceClass DOCUMENT_ME
*/
public void setResourceClassName( String resourceClass )
{
m_resourceClassName = resourceClass;
}
/**
* DOCUMENT_ME
*
* @return DOCUMENT_ME
*/
public String getResourceClassName( )
{
return m_resourceClassName;
}
/**
* DOCUMENT_ME
*
* @param keyClass DOCUMENT_ME
*/
public void setResourceKeyClassName( String keyClass )
{
m_resourceKeyClassName = keyClass;
}
/**
* DOCUMENT_ME
*
* @return DOCUMENT_ME
*/
public String getResourceKeyClassName( )
{
return m_resourceKeyClassName;
}
/**
* DOCUMENT_ME
*
* @param keyName DOCUMENT_ME
*/
public void setResourceKeyName( String keyName )
{
m_resourceKeyName = keyName;
}
/**
* DOCUMENT_ME
*
* @return DOCUMENT_ME
*/
public String getResourceKeyName( )
{
return m_resourceKeyName;
}
/**
* DOCUMENT_ME
*
* @param serviceClass DOCUMENT_ME
*/
public void setServiceClass( String serviceClass )
{
m_serviceClassName = serviceClass;
}
/**
* DOCUMENT_ME
*
* @param className DOCUMENT_ME
*/
public void setServiceClassName( String className )
{
m_serviceClassName = className;
}
/**
* DOCUMENT_ME
*
* @return DOCUMENT_ME
*/
public String getServiceClassName( )
{
return m_serviceClassName;
}
/**
* DOCUMENT_ME
*
* @param delay DOCUMENT_ME
*/
public void setSweeperDelay( long delay )
{
m_sweeperDelay = delay;
}
/**
* DOCUMENT_ME
*
* @return DOCUMENT_ME
*/
public long getSweeperDelay( )
{
return m_sweeperDelay;
}
/**
* DOCUMENT_ME
*
* @param targetNamespace DOCUMENT_ME
*/
public void setWsdlTargetNamespace( String targetNamespace )
{
m_wsdlTargetNamespace = targetNamespace;
}
/**
* DOCUMENT_ME
*
* @return DOCUMENT_ME
*/
public String getWsdlTargetNamespace( )
{
return m_wsdlTargetNamespace;
}
/**
* DOCUMENT_ME
*
* @param key DOCUMENT_ME
*
* @return DOCUMENT_ME
*
* @throws ResourceException if
* @throws InvalidResourceKeyException DOCUMENT_ME
*/
public Resource find( ResourceKey key )
throws ResourceException
{
LOG.debug(MSG.getMessage(Keys.FINDING_RESOURCE_WITH_KEY, String.valueOf(key)));
Resource resource = null;
Lock lock = getLock(key);
try
{
lock.acquire( );
}
catch ( InterruptedException e )
{
throw new ResourceException( e );
}
try
{
resource = get( key );
if ( m_cache != null )
{
m_cache.update( resource );
}
}
finally
{
lock.release( );
}
return resource;
}
private Lock getLock(ResourceKey key)
{
Lock lock;
Object lockKey = getLookupKey(key);
lock = m_lockManager.getLock( lockKey );
return lock;
}
/**
* DOCUMENT_ME
*
* @throws Exception DOCUMENT_ME
*/
public synchronized void init( )
throws Exception
{
LOG.debug(MSG.getMessage(Keys.INIT_HOME));
if ( m_initialized )
{
return;
}
/*if ( m_resourceKeyClassName == null )
{
m_resourceKeyClassName = String.class;
}
*/
if ( m_resourceClassName == null )
{
throw new Exception( MSG.getMessage( Keys.RESOURCE_CLASS_NULL) );
}
/* if ( !Resource.class.isAssignableFrom( m_resourceClassName ) )
{
throw new Exception( "invalidResourceType: " + m_resourceClassName.getName() );
}
if ( PersistenceCallback.class.isAssignableFrom( m_resourceClassName ) )
{
m_resourceIsPersistent = true;
}*/
Context initialContext = new InitialContext( );
initResourceMap( initialContext );
m_lockManager = new LockManager( );
if ( ScheduledResourceTerminationResource.class.isAssignableFrom( getResourceClass( ) ) )
{
initSweeper( initialContext );
}
m_initialized = true;
}
/**
* DOCUMENT_ME
*
* @param key DOCUMENT_ME
*
* @throws ResourceException DOCUMENT_ME
* @throws InvalidResourceKeyException DOCUMENT_ME
*/
public void remove( ResourceKey key )
throws ResourceException
{
Resource resource = null;
Lock lock = getLock(key);
try
{
lock.acquire( );
}
catch ( InterruptedException ie )
{
throw new ResourceException( ie );
}
try
{
resource = get( key );
try
{
resource.destroy( );
}
catch ( RuntimeException re )
{
throw new ResourceException( MSG.getMessage( Keys.FAILED_TO_DESTROY_RESOURCE, resource, re ));
}
m_resources.remove( getLookupKey(key) );
LOG.debug( MSG.getMessage( Keys.REMOVED_RESOURCE_WITH_KEY, resource.getClass( ).getName( ) ,String.valueOf(key) ));
if ( m_cache != null )
{
m_cache.remove( resource );
}
}
finally
{
lock.release( );
}
// TODO: send resource-terminated notification
}
/**
* DOCUMENT_ME
*
* @param key DOCUMENT_ME
* @param resource DOCUMENT_ME
*/
protected void add( ResourceKey key,
Resource resource )
{
addResource( key, resource );
if ( m_cache != null )
{
m_cache.update( resource );
}
}
/**
* DOCUMENT_ME
*
* @param key DOCUMENT_ME
* @return DOCUMENT_ME
*
* @throws ResourceException DOCUMENT_ME
* @throws IllegalStateException DOCUMENT_ME
*/
protected Resource createInstance(ResourceKey key)
throws ResourceException
{
LOG.debug(MSG.getMessage( Keys.CREATING_INSTANCE_WITH_KEY, String.valueOf(key)));
Resource resource;
try
{
resource = (Resource) getResourceClass( ).newInstance( );
}
catch ( Exception e )
{
throw new ResourceException( e );
}
resource.setID( key != null ? key.getValue() : null );
try
{
LOG.debug(MSG.getMessage( Keys.INIT_RESOURCE_LIFECYCLE_INSTANCE, resource.getClass().getName()));
resource.init();
}
catch ( RuntimeException re )
{
throw new ResourceException( MSG.getMessage( Keys.FAILED_TO_INIT_RESOURCE, resource, re ));
}
return resource;
}
/**
* DOCUMENT_ME
*
* @param key DOCUMENT_ME
*
* @return DOCUMENT_ME
*
* @throws ResourceException DOCUMENT_ME
*/
protected Resource createNewInstanceAndLoad( ResourceKey key )
throws ResourceException
{
Resource resource = createInstance( key);
LOG.debug(MSG.getMessage( Keys.LOADING_RESORUCE_FROM_PERSISTENCE, String.valueOf(key)));
( (PersistenceCallback) resource ).load( key );
return resource;
}
private Class getResourceClass( )
throws ClassNotFoundException, ResourceException
{
if ( m_resourceClass == null )
{
if ( m_resourceClassName != null )
{
m_resourceClass = Class.forName( m_resourceClassName );
}
else
{
throw new ClassNotFoundException( MSG.getMessage( Keys.RESOURCE_CLASSNAME_NOT_FOUND) );
}
}
return m_resourceClass;
}
private void addResource( ResourceKey key,
Resource resource )
{
LOG.debug(MSG.getMessage( Keys.ADDING_RESOURCE_FOR_KEY, String.valueOf(key)));
m_resources.put( getLookupKey(key), resource );
// schedule sweeper task if needed
if ( m_sweeper != null )
{
m_sweeper.schedule( );
}
}
private Resource get( ResourceKey key )
throws ResourceException
{
LOG.debug(MSG.getMessage( Keys.GET_RESOURCE_FOR_KEY,String.valueOf(key)));
Object lookupKey = getLookupKey(key);
Resource resource = (Resource) m_resources.get( lookupKey );
if ( resource == null )
{
if ( m_resourceIsPersistent )
{
resource = createNewInstanceAndLoad( key );
addResource( key, resource );
if ( ResourceSweeper.isExpired( resource ) )
{
remove( key );
throw new NoSuchResourceException( );
}
}
else
{
throw new NoSuchResourceException( );
}
}
return resource;
}
private Object getLookupKey(ResourceKey key)
{
Object lookupKey = key != null ? key : (Object)this;
return lookupKey;
}
private void initCachePolicy( Context initialContext )
throws NamingException
{
if ( m_cacheLocation != null )
{
m_cache = (Cache) JNDIUtils.lookup( initialContext, m_cacheLocation, Cache.class );
}
}
private void initResourceMap( Context initialContext )
throws NamingException
{
if ( m_resourceIsPersistent )
{
m_resources = new ReferenceMap( ReferenceMap.HARD, ReferenceMap.SOFT, true );
initCachePolicy( initialContext );
}
else
{
m_resources = new HashMap( );
}
m_resources = Collections.synchronizedMap( m_resources );
}
private void initSweeper( Context initialContext )
throws NamingException
{
LOG.debug( MSG.getMessage( Keys.TIMER_LOOKUP_WITH_JNDI_NAME,JndiConstants.KEY_NAME_DEFAULT_TIMER ) );
TimerManager timerManager = (TimerManager) initialContext.lookup( JndiConstants.KEY_NAME_DEFAULT_TIMER );
// TimerManager timerManager = new TimerManagerImpl();
m_sweeper = new Sweeper( this, m_resources, timerManager, m_sweeperDelay );
}
/**
* This ResourceSweeper implementation just returns the resources currently stored in the map. The reason is that the
* sweeper doesn't have to reactivate/reload a persistent resource if the resource object was reclaimed. So lifetime
* checks are not done on reclained resources. Lifetime checks have to be done on resource load.
*/
private static class Sweeper
extends ResourceSweeper
{
private TimerManager m_timerManager;
private Timer m_timer;
private long m_delay;
/**
* Creates a new {@link Sweeper} object.
*
* @param home DOCUMENT_ME
* @param resources DOCUMENT_ME
* @param timerManager DOCUMENT_ME
* @param delay DOCUMENT_ME
*/
public Sweeper( ResourceHome home,
Map resources,
TimerManager timerManager,
long delay )
{
super( home, resources );
m_timerManager = timerManager;
m_delay = delay;
}
/**
* DOCUMENT_ME
*
* @param timer DOCUMENT_ME
*/
public void timerExpired( Timer timer )
{
super.timerExpired( timer );
cancel( );
if ( !m_resources.isEmpty( ) )
{
schedule( );
}
}
/**
* DOCUMENT_ME
*
* @param key DOCUMENT_ME
*
* @return DOCUMENT_ME
*
* @throws ResourceException DOCUMENT_ME
*/
protected Resource getResource( ResourceKey key )
throws ResourceException
{
LOG.debug(MSG.getMessage( Keys.GET_RESOURCE_FOR_KEY, key.getValue()));
return (Resource) m_resources.get( key );
}
/**
* Schedules this resource sweeper.
*/
synchronized void schedule( )
{
if ( m_timer == null )
{
LOG.debug( MSG.getMessage( Keys.SCHEDULE_RESOURCE_SWEEPER) );
m_timer = m_timerManager.schedule( this, m_delay );
}
}
/**
* Cancels this resource sweeper.
*/
private synchronized void cancel( )
{
if ( m_timer != null )
{
LOG.debug( MSG.getMessage( Keys.CANCEL_RESOURCE_SWEEPER) );
m_timer = null;
}
}
}
}