/*
* JBoss, Home of Professional Open Source
* Copyright 2005, JBoss Inc., and individual contributors as indicated
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.cache.interceptors;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jboss.cache.CacheException;
import org.jboss.cache.CacheSPI;
import org.jboss.cache.Fqn;
import org.jboss.cache.InvocationContext;
import org.jboss.cache.NodeSPI;
import org.jboss.cache.config.Configuration;
import org.jboss.cache.factories.annotations.Inject;
import org.jboss.cache.factories.annotations.Start;
import javax.transaction.Status;
import javax.transaction.SystemException;
import javax.transaction.Transaction;
import java.util.Collections;
import java.util.Map;
/**
* Class representing an interceptor.
*
* @author Bela Ban
* @version $Id: Interceptor.java 5306 2008-02-06 01:19:52Z manik.surtani@jboss.com $
*/
public abstract class Interceptor implements InterceptorMBean
{
protected Interceptor next = null, last = null;
protected CacheSPI<?, ?> cache;
protected Log log = null;
protected Configuration configuration;
private boolean statsEnabled = false;
protected boolean trace;
public Interceptor()
{
initLogger();
}
protected void initLogger()
{
log = LogFactory.getLog(getClass());
trace = log.isTraceEnabled();
}
public void setNext(Interceptor i)
{
next = i;
}
public Interceptor getNext()
{
return next;
}
public void setCache(CacheSPI cache)
{
}
@Start
private void start()
{
setStatisticsEnabled(configuration.getExposeManagementStatistics());
// for backward compatibility, this must only be done when the cache starts.
setCache(cache);
}
@Inject
private void injectDependencies(CacheSPI cache, Configuration configuration)
{
this.configuration = configuration;
this.cache = cache;
}
/**
* Using this method call for forwarding a call in the chain is not redable and error prone in the case of interceptors
* extending other interceptors. This metod rather refers to interceptor doing its business operations rather than
* delegating to the nextInterceptor interceptor in chain. For delegation please use {@link #nextInterceptor(org.jboss.cache.InvocationContext)}
*/
public Object invoke(InvocationContext ctx) throws Throwable
{
return next.invoke(ctx);
}
/**
* Forwards the call to the nextInterceptor interceptor in the chain.
*/
public Object nextInterceptor(InvocationContext ctx) throws Throwable
{
return next.invoke(ctx);
}
public boolean getStatisticsEnabled()
{
return statsEnabled;
}
public void setStatisticsEnabled(boolean enabled)
{
statsEnabled = enabled;
}
public Interceptor getLast()
{
return last;
}
public void setLast(Interceptor last)
{
this.last = last;
}
/**
* This implementation returns an empty Map. If individual Interceptors wish to expose statistics, they can override this
* method.
*/
public Map<String, Object> dumpStatistics()
{
return Collections.emptyMap();
}
/**
* This implementation is a no-op. If individual Interceptors wish to expose statistics, they can override this
* method.
*/
public void resetStatistics()
{
}
/**
* Returns true if transaction is ACTIVE, false otherwise
*/
protected boolean isActive(Transaction tx)
{
if (tx == null) return false;
int status = -1;
try
{
status = tx.getStatus();
return status == Status.STATUS_ACTIVE;
}
catch (SystemException e)
{
log.error("failed getting transaction status", e);
return false;
}
}
/**
* Returns true if transaction is PREPARING, false otherwise
*/
protected boolean isPreparing(Transaction tx)
{
if (tx == null) return false;
int status = -1;
try
{
status = tx.getStatus();
return status == Status.STATUS_PREPARING;
}
catch (SystemException e)
{
log.error("failed getting transaction status", e);
return false;
}
}
/**
* Return s true of tx's status is ACTIVE or PREPARING
*
* @param tx
* @return true if the tx is active or preparing
*/
protected boolean isValid(Transaction tx)
{
return isActive(tx) || isPreparing(tx);
}
/**
* Tests whether the caller is in a valid transaction. If not, will throw a CacheException.
*/
protected void assertTransactionValid(InvocationContext ctx)
{
Transaction tx = ctx.getTransaction();
if (!isValid(tx)) try
{
throw new CacheException("Invalid transaction " + tx + ", status = " + (tx == null ? null : tx.getStatus()));
}
catch (SystemException e)
{
throw new CacheException("Exception trying to analyse status of transaction " + tx, e);
}
}
public String toString()
{
return getClass().getName()
+ "{next: "
+ (getNext() == null ? null : getNext().getClass())
+ "; last: "
+ (getLast() == null ? null : getLast().getClass())
+ "}";
}
/**
* First checks the invocation context for previously obtained reference to a node, if this doesn't exist, performs
* a cache.peek() and holds on to the node reference.
*
* @param ctx invocation context
* @param f fqn to find
* @param forceRefresh forces calling cache.peek() even if we hold a reference to the relevant node.
* @param includeDeletedNodes includes nodes marked for deletion if this is true
* @param includeInvalidNodes includes nodes marked as invalid if this is true
* @return a node, or null if one cannot be found.
* @since 2.1.0
*/
public NodeSPI peekNode(InvocationContext ctx, Fqn f, boolean forceRefresh, boolean includeDeletedNodes, boolean includeInvalidNodes)
{
return cache.peek(f, includeDeletedNodes, includeInvalidNodes);
// Disabling this for now, until we can prove that it is in fact beneficial.
// preliminary profiling tests show that it is not.
// NodeSPI n;
// if (forceRefresh || (n = ctx.getPeekedNode(f)) == null)
// {
// n = cache.peek(f, true, true);
// // put this in the invocation cache
// ctx.savePeekedNode(n, f);
// }
//
// if (n != null)
// {
// if (!includeDeletedNodes && n.isDeleted()) return null;
// if (!includeInvalidNodes && !n.isValid()) return null;
// }
// return n;
}
}
|