/*
* JBoss, Home of Professional Open Source.
* Copyright 2006, Red Hat Middleware LLC, and individual contributors
* as indicated by the @author tags. See the copyright.txt file 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.aop.array;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.jboss.aop.AspectManager;
import org.jboss.aop.advice.AdviceFactory;
import org.jboss.aop.advice.GeneratedAdvisorInterceptor;
import org.jboss.aop.advice.Interceptor;
import org.jboss.aop.advice.InterceptorFactory;
import org.jboss.aop.advice.PerVmAdvice;
import org.jboss.aop.advice.PrecedenceSorter;
import org.jboss.aop.advice.ScopedInterceptorFactory;
/**
*
* @author <a href="kabir.khan@jboss.com">Kabir Khan</a>
* @version $Revision: 1.1 $
*/
public class ArrayAdvisor
{
static HashSet<ArrayBinding> bindings = new LinkedHashSet<ArrayBinding>();
static boolean updated;
public static void addBinding(ArrayBinding arrayBinding)
{
ChainCreator.addBinding(arrayBinding);
}
public static void removeBinding(ArrayBinding arrayBinding)
{
ChainCreator.removeBinding(arrayBinding);
}
public static Interceptor[] getReadInterceptors()
{
return ChainCreator.getReadInterceptors();
}
public static Interceptor[] getWriteInterceptors()
{
return ChainCreator.getWriteInterceptors();
}
public static void updateArrayField(Object target, String fieldName, Object oldValue, Object newValue)
{
ArrayRegistry registry = ArrayRegistry.getInstance();
registry.removeFieldReference(target, fieldName, oldValue);
registry.addFieldReference(target, fieldName, newValue);
}
public static void arrayWriteObject(Object array, int index, Object value) throws Throwable
{
Interceptor[] interceptors = getWriteInterceptors();
ArrayRegistry registry = ArrayRegistry.getInstance();
if (registry.isRegistered(array))
{
//The old value might be an array, remove references to that
Object oldValue = ((Object[])array)[index];
boolean ignoreUpdate = (oldValue == value);
if (!ignoreUpdate)
{
registry.removeElementReference(array, index, oldValue);
//The new value might be an array
if (value != null && value.getClass().isArray())
{
registry.addElementReference(array, index, value);
}
if (interceptors != null)
{
ObjectArrayElementWriteInvocation invocation = new ObjectArrayElementWriteInvocation(interceptors, (Object[])array, index, value);
invocation.invokeNext();
return;
}
}
}
((Object[])array)[index] = value;
}
public static void arrayWriteInt(Object array, int index, int value) throws Throwable
{
Interceptor[] interceptors = getWriteInterceptors();
if (interceptors != null && ArrayRegistry.getInstance().isRegistered(array) && ((int[])array)[index] != value)
{
IntArrayElementWriteInvocation invocation = new IntArrayElementWriteInvocation(interceptors, ((int[])array), index, value);
invocation.invokeNext();
}
else
{
((int[])array)[index] = value;
}
}
public static void arrayWriteByteOrBoolean(Object array, int index, byte value) throws Throwable
{
Interceptor[] interceptors = getWriteInterceptors();
if (interceptors != null && ArrayRegistry.getInstance().isRegistered(array))
{
if (array instanceof boolean[])
{
if (((boolean[])array)[index] != ByteBooleanConverter.toBoolean(value))
{
BooleanArrayElementWriteInvocation invocation = new BooleanArrayElementWriteInvocation(interceptors, ((boolean[])array), index, ByteBooleanConverter.toBoolean(value));
invocation.invokeNext();
return;
}
((boolean[])array)[index] = ByteBooleanConverter.toBoolean(value);
}
else
{
if (((byte[])array)[index] != value)
{
ByteArrayElementWriteInvocation invocation = new ByteArrayElementWriteInvocation(interceptors, ((byte[])array), index, value);
invocation.invokeNext();
return;
}
((byte[])array)[index] = value;
}
}
}
public static void arrayWriteChar(Object array, int index, char value) throws Throwable
{
Interceptor[] interceptors = getWriteInterceptors();
if (interceptors != null && ArrayRegistry.getInstance().isRegistered(array) && ((char[])array)[index] != value)
{
CharArrayElementWriteInvocation invocation = new CharArrayElementWriteInvocation(interceptors, ((char[])array), index, value);
invocation.invokeNext();
}
else
{
((char[])array)[index] = value;
}
}
public static void arrayWriteDouble(Object array, int index, double value) throws Throwable
{
Interceptor[] interceptors = getWriteInterceptors();
if (interceptors != null && ArrayRegistry.getInstance().isRegistered(array) && ((double[])array)[index] != value)
{
DoubleArrayElementWriteInvocation invocation = new DoubleArrayElementWriteInvocation(interceptors, ((double[])array), index, value);
invocation.invokeNext();
}
else
{
((double[])array)[index] = value;
}
}
public static void arrayWriteShort(Object array, int index, short value) throws Throwable
{
Interceptor[] interceptors = getWriteInterceptors();
if (interceptors != null && ArrayRegistry.getInstance().isRegistered(array) && ((short[])array)[index] != value)
{
ShortArrayElementWriteInvocation invocation = new ShortArrayElementWriteInvocation(interceptors, ((short[])array), index, value);
invocation.invokeNext();
}
else
{
((short[])array)[index] = value;
}
}
public static void arrayWriteFloat(Object array, int index, float value) throws Throwable
{
Interceptor[] interceptors = getWriteInterceptors();
if (interceptors != null && ArrayRegistry.getInstance().isRegistered(array) && ((float[])array)[index] != value)
{
FloatArrayElementWriteInvocation invocation = new FloatArrayElementWriteInvocation(interceptors, ((float[])array), index, value);
invocation.invokeNext();
}
else
{
((float[])array)[index] = value;
}
}
public static void arrayWriteLong(Object array, int index, long value) throws Throwable
{
Interceptor[] interceptors = getWriteInterceptors();
if (interceptors != null && ArrayRegistry.getInstance().isRegistered(array) && ((long[])array)[index] != value)
{
LongArrayElementWriteInvocation invocation = new LongArrayElementWriteInvocation(interceptors, ((long[])array), index, value);
invocation.invokeNext();
}
else
{
((long[])array)[index] = value;
}
}
public static Object arrayReadObject(Object array, int index) throws Throwable
{
Interceptor[] interceptors = getReadInterceptors();
if (interceptors != null && ArrayRegistry.getInstance().isRegistered(array))
{
ObjectArrayElementReadInvocation invocation = new ObjectArrayElementReadInvocation(interceptors, (Object[])array, index);
return invocation.invokeNext();
}
else
{
return ((Object[])array)[index];
}
}
public static int arrayReadInt(Object array, int index) throws Throwable
{
Interceptor[] interceptors = getReadInterceptors();
if (interceptors != null && ArrayRegistry.getInstance().isRegistered(array))
{
IntArrayElementReadInvocation invocation = new IntArrayElementReadInvocation(interceptors, (int[])array, index);
return ((Integer)invocation.invokeNext()).intValue();
}
else
{
return ((int[])array)[index];
}
}
public static byte arrayReadByteOrBoolean(Object array, int index) throws Throwable
{
Interceptor[] interceptors = getReadInterceptors();
if (interceptors != null && ArrayRegistry.getInstance().isRegistered(array))
{
if (array instanceof boolean[])
{
BooleanArrayElementReadInvocation invocation = new BooleanArrayElementReadInvocation(interceptors, (boolean[])array, index);
boolean b = ((Boolean)invocation.invokeNext()).booleanValue();
return ByteBooleanConverter.toByte(b);
}
else
{
ByteArrayElementReadInvocation invocation = new ByteArrayElementReadInvocation(interceptors, (byte[])array, index);
return ((Byte)invocation.invokeNext()).byteValue();
}
}
else
{
if (array instanceof boolean[])
{
return ByteBooleanConverter.toByte(((boolean[])array)[index]);
}
else
{
return ((byte[])array)[index];
}
}
}
public static char arrayReadChar(Object array, int index) throws Throwable
{
Interceptor[] interceptors = getReadInterceptors();
if (interceptors != null && ArrayRegistry.getInstance().isRegistered(array))
{
CharArrayElementReadInvocation invocation = new CharArrayElementReadInvocation(interceptors, (char[])array, index);
return ((Character)invocation.invokeNext()).charValue();
}
else
{
return ((char[])array)[index];
}
}
public static double arrayReadDouble(Object array, int index) throws Throwable
{
Interceptor[] interceptors = getReadInterceptors();
if (interceptors != null && ArrayRegistry.getInstance().isRegistered(array))
{
DoubleArrayElementReadInvocation invocation = new DoubleArrayElementReadInvocation(interceptors, (double[])array, index);
return ((Double)invocation.invokeNext()).doubleValue();
}
else
{
return ((double[])array)[index];
}
}
public static float arrayReadFloat(Object array, int index) throws Throwable
{
Interceptor[] interceptors = getReadInterceptors();
if (interceptors != null && ArrayRegistry.getInstance().isRegistered(array))
{
FloatArrayElementReadInvocation invocation = new FloatArrayElementReadInvocation(interceptors, (float[])array, index);
return ((Float)invocation.invokeNext()).floatValue();
}
else
{
return ((float[])array)[index];
}
}
public static long arrayReadLong(Object array, int index) throws Throwable
{
Interceptor[] interceptors = getReadInterceptors();
if (interceptors != null && ArrayRegistry.getInstance().isRegistered(array))
{
LongArrayElementReadInvocation invocation = new LongArrayElementReadInvocation(interceptors, (long[])array, index);
return ((Long)invocation.invokeNext()).longValue();
}
else
{
return ((long[])array)[index];
}
}
public static short arrayReadShort(Object array, int index) throws Throwable
{
Interceptor[] interceptors = getReadInterceptors();
if (interceptors != null && ArrayRegistry.getInstance().isRegistered(array))
{
ShortArrayElementReadInvocation invocation = new ShortArrayElementReadInvocation(interceptors, (short[])array, index);
return ((Short)invocation.invokeNext()).shortValue();
}
else
{
return ((short[])array)[index];
}
}
private static class ChainCreator
{
private static ReadWriteLock lock = new ReentrantReadWriteLock();
static Interceptor[] readInterceptors;
static Interceptor[] writeInterceptors;
public static void addBinding(ArrayBinding arrayBinding)
{
Lock writeLock = lock.writeLock();
writeLock.lock();
try
{
bindings.add(arrayBinding);
updated = true;
}
finally
{
writeLock.unlock();
}
}
public static void removeBinding(ArrayBinding arrayBinding)
{
Lock writeLock = lock.writeLock();
writeLock.lock();
try
{
bindings.remove(arrayBinding);
updated = true;
}
finally
{
writeLock.unlock();
}
}
public static Interceptor[] getReadInterceptors()
{
return getInterceptors(true);
}
public static Interceptor[] getWriteInterceptors()
{
return getInterceptors(false);
}
private static Interceptor[] getInterceptors(boolean read)
{
Lock readLock = lock.readLock();
readLock.lock();
boolean lockedRead = true;
try
{
if (updated)
{
readLock.unlock();
lockedRead = false;
Lock writeLock = lock.writeLock();
writeLock.lock();
try
{
if (updated)
{
//Create the interceptor chains
ArrayList<Interceptor> newReadInterceptors = new ArrayList<Interceptor>();
ArrayList<Interceptor> newWriteInterceptors = new ArrayList<Interceptor>();
for (ArrayBinding binding : bindings)
{
InterceptorFactory[] factories = binding.getInterceptorFactories();
for (int i = 0; i < factories.length; i++)
{
Interceptor icptr = createInterceptor(factories[i]);
if (icptr != null)
{
if (binding.isWrite())
{
newWriteInterceptors.add(icptr);
}
if (binding.isRead())
{
newReadInterceptors.add(icptr);
}
}
}
}
readInterceptors = finalizeChain(newReadInterceptors);
writeInterceptors = finalizeChain(newWriteInterceptors);
updated = false;
return (read) ? readInterceptors : writeInterceptors;
}
}
finally
{
writeLock.unlock();
}
}
return (read) ? readInterceptors : writeInterceptors;
}
finally
{
if (lockedRead)
{
readLock.unlock();
}
}
}
private static Interceptor[] finalizeChain(ArrayList<Interceptor> newinterceptors)
{
Interceptor[] interceptors = (newinterceptors.size() > 0) ? newinterceptors.toArray(new Interceptor[newinterceptors.size()]) : null;
if (interceptors != null)
{
if (interceptors[0] instanceof GeneratedAdvisorInterceptor)
{
}
interceptors = PrecedenceSorter.applyPrecedence(interceptors, AspectManager.instance());
}
return interceptors;
}
private static Interceptor createInterceptor(InterceptorFactory factory)
{
//TODO: must be fixed!
// if (factory instanceof GeneratedOnly)
// {
// throw new RuntimeException("Before/After/Throwing is only supported for Generated Advisors");
// }
try
{
if (factory.isDeployed())
{
if (factory instanceof AdviceFactory)
{
return PerVmAdvice.generateOptimized(null, AspectManager.instance(), factory.getAdvice(), factory.getAspect());
}
else if (factory instanceof ScopedInterceptorFactory)
{
return (Interceptor) AspectManager.instance().getPerVMAspect(factory.getAspect());
}
}
return null;
}
catch (Exception e)
{
throw new RuntimeException(e);
}
}
}
}