Skip to content

Commit

Permalink
Introduce marker interface to make signature conflicts very unlikely
Browse files Browse the repository at this point in the history
git-svn-id: http://anonsvn.jboss.org/repos/weld/ri/trunk@3234 1c488680-804c-0410-94cd-c6b725194a0e
  • Loading branch information
pmuir committed Jul 25, 2009
1 parent d53a65a commit 9071d1a
Show file tree
Hide file tree
Showing 8 changed files with 101 additions and 37 deletions.
Expand Up @@ -38,6 +38,7 @@
import org.jboss.webbeans.DefinitionException;
import org.jboss.webbeans.bean.proxy.EnterpriseBeanInstance;
import org.jboss.webbeans.bean.proxy.EnterpriseBeanProxyMethodHandler;
import org.jboss.webbeans.bean.proxy.Marker;
import org.jboss.webbeans.bootstrap.BeanDeployerEnvironment;
import org.jboss.webbeans.ejb.InternalEjbDescriptor;
import org.jboss.webbeans.ejb.api.SessionObjectReference;
Expand Down Expand Up @@ -256,13 +257,13 @@ public void destroy(T instance, CreationalContext<T> creationalContext)
}
EnterpriseBeanInstance enterpiseBeanInstance = (EnterpriseBeanInstance) instance;

if (enterpiseBeanInstance.isDestroyed())
if (enterpiseBeanInstance.isDestroyed(Marker.INSTANCE))
{
return;
}
else
{
enterpiseBeanInstance.destroy(this, creationalContext);
enterpiseBeanInstance.destroy(Marker.INSTANCE, this, creationalContext);
}
creationalContext.release();
}
Expand Down
@@ -0,0 +1,28 @@
/*
* JBoss, Home of Professional Open Source
* Copyright 2008, Red Hat Middleware LLC, and individual contributors
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* 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.jboss.webbeans.bean.proxy;

/**
* @author pmuir
*
*/
public interface ClientProxyInstance
{

public void touch(Marker marker);

}
Expand Up @@ -42,17 +42,18 @@
*/
public class ClientProxyMethodHandler implements MethodHandler, Serializable
{

private static final long serialVersionUID = -5391564935097267888L;
// The log provider
private static transient LogProvider log = Logging.getLogProvider(ClientProxyMethodHandler.class);
// The bean
private transient Bean<?> bean;
// The bean index in the manager
private final int beanIndex;
private final BeanManagerImpl manager;
private static final ThreadLocal<CreationalContextImpl<?>> currentCreationalContext = new ThreadLocal<CreationalContextImpl<?>>();

private final BeanManagerImpl manager;

private static final ThreadLocal<CreationalContextImpl<?>> currentCreationalContext = new ThreadLocal<CreationalContextImpl<?>>();

/**
* Constructor
Expand All @@ -69,31 +70,37 @@ public ClientProxyMethodHandler(Bean<?> bean, BeanManagerImpl manager, int beanI
}

/**
* Invokes the method on the correct version of the instance as obtained by
* a context lookup
* Invokes the method on the correct version of the instance as obtained by a
* context lookup
*
* @param self the proxy instance.
* @param thisMethod the overridden method declared in the super class or
* interface.
* @param proceed the forwarder method for invoking the overridden method. It
* is null if the overridden mehtod is abstract or declared in the
* interface.
* @param args an array of objects containing the values of the arguments
* passed in the method invocation on the proxy instance. If a
* parameter type is a primitive type, the type of the array
* element is a wrapper class.
* @return the resulting value of the method invocation.
*
* @param self the proxy instance.
* @param thisMethod the overridden method declared in the super
* class or interface.
* @param proceed the forwarder method for invoking the overridden
* method. It is null if the overridden mehtod is
* abstract or declared in the interface.
* @param args an array of objects containing the values of
* the arguments passed in the method invocation
* on the proxy instance. If a parameter type is
* a primitive type, the type of the array element
* is a wrapper class.
* @return the resulting value of the method invocation.
*
* @throws Throwable if the method invocation fails.
* @throws Throwable if the method invocation fails.
*/
public Object invoke(Object self, Method proxiedMethod, Method proceed, Object[] args) throws Throwable
{
if (bean == null)
{
bean = manager.getBeans().get(beanIndex);
}
Object proxiedInstance = getProxiedInstance(bean);
Object proxiedInstance = getProxiedInstance(bean);
if ("touch".equals(proxiedMethod.getName()) && Marker.isMarker(0, proxiedMethod, args))
{
// Our "touch" method, which simply ensures the proxy does any object
// instantiation needed, to avoid the annoying side effect of an object
// getting lazy created
return null;
}
try
{
Object returnValue = Reflections.lookupMethod(proxiedMethod, proxiedInstance).invoke(proxiedInstance, args);
Expand All @@ -105,7 +112,7 @@ public Object invoke(Object self, Method proxiedMethod, Method proceed, Object[]
throw e.getCause();
}
}

private <T> T getProxiedInstance(Bean<T> bean)
{
CreationalContextImpl<T> creationalContext;
Expand Down
Expand Up @@ -78,6 +78,7 @@ private static <T> T createClientProxy(Bean<T> bean, BeanManagerImpl manager, in
ClientProxyMethodHandler proxyMethodHandler = new ClientProxyMethodHandler(bean, manager, beanIndex);
Set<Type> classes = new LinkedHashSet<Type>(bean.getTypes());
classes.add(Serializable.class);
classes.add(ClientProxyInstance.class);
ProxyFactory proxyFactory = Proxies.getProxyFactory(classes);
proxyFactory.setHandler(proxyMethodHandler);
Class<?> clazz = proxyFactory.createClass();
Expand Down Expand Up @@ -108,7 +109,7 @@ private static <T> T createClientProxy(Bean<T> bean, BeanManagerImpl manager, in
*/
public <T> T getClientProxy(final BeanManagerImpl manager, final Bean<T> bean)
{
return pool.putIfAbsent(bean, new Callable<T>()
T instance = pool.putIfAbsent(bean, new Callable<T>()
{

public T call() throws Exception
Expand All @@ -122,6 +123,9 @@ public T call() throws Exception
}

});
// TODO Break circular injection. Can we somehow support both?
//((ClientProxyInstance) instance).touch(Marker.INSTANCE);
return instance;
}

/**
Expand Down
Expand Up @@ -29,16 +29,15 @@
public interface EnterpriseBeanInstance
{

// TODO These methods may conflict :-(
/**
* Indicated if a remove method has been invoked by the application
*
* @return True if invoked, false otherwise
*/
public boolean isDestroyed();
public boolean isDestroyed(Marker marker);

public void setDestroyed(boolean destroyed);
public void setDestroyed(Marker marker, boolean destroyed);

public void destroy(EnterpriseBean<?> enterpriseBean, CreationalContext<?> creationalContext);
public void destroy(Marker marker, EnterpriseBean<?> enterpriseBean, CreationalContext<?> creationalContext);

}
Expand Up @@ -128,29 +128,29 @@ public EnterpriseBeanProxyMethodHandler(EnterpriseBean<T> bean, CreationalContex
public Object invoke(Object self, Method method, Method proceed, Object[] args) throws Throwable
{
// EnterpriseBeanInstance methods
if ("isDestroyed".equals(method.getName()))
if ("isDestroyed".equals(method.getName()) && Marker.isMarker(0, method, args))
{
return destroyed;
}
else if ("setDestroyed".equals(method.getName()))
else if ("setDestroyed".equals(method.getName()) && Marker.isMarker(0, method, args))
{
if (args.length != 1)
if (args.length != 2)
{
throw new IllegalArgumentException("enterpriseBeanInstance.setDestroyed() called with >1 argument");
throw new IllegalArgumentException("enterpriseBeanInstance.setDestroyed() called with >2 argument");
}
if (!(args[0].getClass().equals(boolean.class) || args[0].getClass().equals(Boolean.class)))
if (!(args[1].getClass().equals(boolean.class) || args[1].getClass().equals(Boolean.class)))
{
throw new IllegalArgumentException("enterpriseBeanInstance.setDestroyed() called with non-boolean argument");
}
destroyed = ((Boolean) args[0]).booleanValue();
destroyed = ((Boolean) args[1]).booleanValue();
}

if (destroyed)
{
return null;
}

if ("destroy".equals(method.getName()))
if ("destroy".equals(method.getName()) && Marker.isMarker(0, method, args))
{
reference.remove();
return null;
Expand Down
24 changes: 24 additions & 0 deletions impl/src/main/java/org/jboss/webbeans/bean/proxy/Marker.java
@@ -0,0 +1,24 @@
package org.jboss.webbeans.bean.proxy;

import java.lang.reflect.Method;

/**
* A marker class we can use to ensure that our method will not collide with a
* user provided method
*
* @author pmuir
*
*/
public class Marker
{

public static final Marker INSTANCE = new Marker();

private Marker () {}

public static boolean isMarker(int position, Method method, Object[] args)
{
return method.getParameterTypes().length >= position && method.getParameterTypes()[position].equals(Marker.class) && INSTANCE.equals(args[position]);
}

}
Expand Up @@ -30,6 +30,7 @@
import org.jboss.webbeans.bean.EnterpriseBean;
import org.jboss.webbeans.bean.proxy.EnterpriseBeanInstance;
import org.jboss.webbeans.bean.proxy.EnterpriseBeanProxyMethodHandler;
import org.jboss.webbeans.bean.proxy.Marker;
import org.jboss.webbeans.log.Log;
import org.jboss.webbeans.log.Logging;

Expand Down Expand Up @@ -82,7 +83,7 @@ public void preDestroy(InvocationContext invocationContext) throws Exception
EnterpriseBeanInstance instance = getEnterpriseBeanInstance(bean);
if (instance != null)
{
instance.setDestroyed(true);
instance.setDestroyed(Marker.INSTANCE, true);
}
}
invocationContext.proceed();
Expand Down

0 comments on commit 9071d1a

Please sign in to comment.