/*
* Copyright (C) 2010 eXo Platform SAS.
*
* 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.xcmis.search.content.interceptors;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.xcmis.search.Startable;
import org.xcmis.search.content.command.InvocationContext;
import org.xcmis.search.content.command.VisitableCommand;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
/**
* Knows how to build and manage an chain of interceptors. Also in charge with invoking methods on the chain.
*/
public class InterceptorChain implements Startable
{
/**
* Reference to the first interceptor in the chain.
*/
private CommandInterceptor firstInChain;
private static final Log log = LogFactory.getLog(InterceptorChain.class);
/**
* Constructs an interceptor chain having the supplied interceptor as first.
*/
public InterceptorChain(CommandInterceptor first)
{
this.firstInChain = first;
}
/**
* Adds a new interceptor in list after an interceptor of a given type.
*
* @param toAdd
* @param afterInterceptor
* @return true if the interceptor was added; i.e. the afterInterceptor exists
*/
public synchronized boolean addAfterInterceptor(CommandInterceptor toAdd,
Class<? extends CommandInterceptor> afterInterceptor)
{
CommandInterceptor it = firstInChain;
while (it != null)
{
if (it.getClass().equals(afterInterceptor))
{
toAdd.setNext(it.getNext());
it.setNext(toAdd);
return true;
}
it = it.getNext();
}
return false;
}
/**
* Adds a new interceptor in list after an interceptor of a given type.
*
* @param toAdd
* @param beforeInterceptor
* @return true if the interceptor was added; i.e. the afterInterceptor exists
*/
public synchronized boolean addBeforeInterceptor(CommandInterceptor toAdd,
Class<? extends CommandInterceptor> beforeInterceptor)
{
if (firstInChain.getClass().equals(beforeInterceptor)
|| beforeInterceptor.isAssignableFrom(firstInChain.getClass()))
{
toAdd.setNext(firstInChain);
firstInChain = toAdd;
return true;
}
CommandInterceptor it = firstInChain;
while (it.getNext() != null)
{
if (it.getNext().getClass().equals(beforeInterceptor))
{
toAdd.setNext(it.getNext());
it.setNext(toAdd);
return true;
}
it = it.getNext();
}
return false;
}
/**
* Inserts the given interceptor at the specified position in the chain (o based indexing).
*
* @param interceptor CommandInterceptor
* @param position int
* @throws IllegalArgumentException if the position is invalid (e.g. 5 and there are only 2 interceptors in the chain)
*/
public synchronized void addInterceptor(CommandInterceptor interceptor, int position)
{
if (position == 0)
{
interceptor.setNext(firstInChain);
firstInChain = interceptor;
return;
}
if (firstInChain == null)
{
return;
}
CommandInterceptor it = firstInChain;
int index = 0;
while (it != null)
{
if (++index == position)
{
interceptor.setNext(it.getNext());
it.setNext(interceptor);
return;
}
it = it.getNext();
}
throw new IllegalArgumentException("Invalid index: " + index + " !");
}
/**
* Appends at the end.
*
* @param ci CommandInterceptor
*/
public void appendIntereceptor(CommandInterceptor ci)
{
CommandInterceptor it = firstInChain;
while (it.hasNext())
{
it = it.getNext();
}
it.setNext(ci);
// make sure we nullify the "next" pointer in the last interceptor.
ci.setNext(null);
}
/**
* Returns an unmofiable list with all the interceptors in sequence.
* If first in chain is null an empty list is returned.
*/
public List<CommandInterceptor> asList()
{
if (firstInChain == null)
{
return Collections.emptyList();
}
List<CommandInterceptor> retval = new LinkedList<CommandInterceptor>();
CommandInterceptor tmp = firstInChain;
do
{
retval.add(tmp);
tmp = tmp.getNext();
}
while (tmp != null);
return Collections.unmodifiableList(retval);
}
/**
* Checks whether the chain contains the supplied interceptor instance.
*/
public boolean containsInstance(CommandInterceptor interceptor)
{
CommandInterceptor it = firstInChain;
while (it != null)
{
if (it.equals(interceptor))
{
return true;
}
it = it.getNext();
}
return false;
}
/**
* @return the first interceptor in the chain.
*/
public CommandInterceptor getFirstInChain()
{
return firstInChain;
}
public String getInterceptorDetails()
{
StringBuilder sb = new StringBuilder("Interceptor chain: \n");
int count = 0;
for (CommandInterceptor i : asList())
{
count++;
sb.append(" ").append(count).append(". ").append(i).append("\n");
}
return sb.toString();
}
/**
* Returns all interceptors which extend the given command interceptor.
*
* @param interceptorClass
*/
public List<CommandInterceptor> getInterceptorsWhichExtend(Class<? extends CommandInterceptor> interceptorClass)
{
List<CommandInterceptor> result = new ArrayList<CommandInterceptor>();
for (CommandInterceptor interceptor : asList())
{
boolean isSubclass = interceptorClass.isAssignableFrom(interceptor.getClass());
if (isSubclass)
{
result.add(interceptor);
}
}
return result;
}
/**
* Returns all the interceptors that have the fully qualified name of their class equal with the supplied class name.
*
* @param fqName String
*/
public List<CommandInterceptor> getInterceptorsWithClassName(String fqName)
{
CommandInterceptor iterator = firstInChain;
List<CommandInterceptor> result = new ArrayList<CommandInterceptor>(2);
while (iterator != null)
{
if (iterator.getClass().getName().equals(fqName))
{
result.add(iterator);
}
iterator = iterator.getNext();
}
return result;
}
/**
* Walks the command through the interceptor chain. The received ctx is being passed in.
* @throws Throwable
*/
@SuppressWarnings("deprecation")
public Object invoke(InvocationContext ctx, VisitableCommand command) throws Throwable
{
//ctx.setCommand(command);
try
{
return command.acceptVisitor(ctx, firstInChain);
}
catch (InterruptedException ie)
{
Thread.currentThread().interrupt();
return null;
}
}
/**
* Removes all the occurences of supplied interceptor type from the chain.
*/
public synchronized void removeInterceptor(Class<? extends CommandInterceptor> clazz)
{
if (firstInChain.getClass() == clazz)
{
firstInChain = firstInChain.getNext();
}
CommandInterceptor it = firstInChain.getNext();
CommandInterceptor prevIt = firstInChain;
while (it != null)
{
if (it.getClass() == clazz)
{
prevIt.setNext(it.getNext());
}
prevIt = it;
it = it.getNext();
}
}
/**
* Removes the interceptor at the given postion.
*
* @throws IllegalArgumentException if the position is invalid (e.g. 5 and there are only 2 interceptors in the chain)
*/
public synchronized void removeInterceptor(int position)
{
if (firstInChain == null)
{
return;
}
if (position == 0)
{
firstInChain = firstInChain.getNext();
return;
}
CommandInterceptor it = firstInChain;
int index = 0;
while (it != null)
{
if (++index == position)
{
if (it.getNext() == null)
{
return; //nothing to remove
}
it.setNext(it.getNext().getNext());
return;
}
it = it.getNext();
}
throw new IllegalArgumentException("Invalid position: " + position + " !");
}
/**
* Mainly used by unit tests to replace the interceptor chain with the starting point passed in.
*
* @param interceptor interceptor to be used as the first interceptor in the chain.
*/
public void setFirstInChain(CommandInterceptor interceptor)
{
this.firstInChain = interceptor;
}
/**
* Returns the number of interceptors in the chain.
*/
public int size()
{
int size = 0;
CommandInterceptor it = firstInChain;
while (it != null)
{
size++;
it = it.getNext();
}
return size;
}
/**
* @see org.xcmis.search.Startable#start()
*/
public void start()
{
for (CommandInterceptor interceptor : asList())
{
interceptor.start();
}
}
/**
* @see org.xcmis.search.Startable#stop()
*/
public void stop()
{
for (CommandInterceptor interceptor : asList())
{
interceptor.stop();
}
}
void printChainInfo()
{
if (log.isDebugEnabled())
{
log.debug("Interceptor chain is: " + toString());
}
}
}