Sorry for not replying sooner -- been down a rabbit hole with those stateless pooling changes.

On Mar 17, 2010, at 3:14 PM, Jonathan Gallimore wrote:

I've managed to do some work on the no-interface view work. I've managed to
read the localbean ejb-jar.xml config and @LocalBean annotation, and
propagate this through to CoreDeploymentInfo. I've got to a point where I
can lookup an EJB and get a proxy back.

Nice!

There's still some work to do, as
I'm getting this exception:

   //START SNIPPET: localbean
public void testCalculatorViaLocalBeanInterface() throws Exception { Object object = initialContext.lookup("CalculatorImplLocalBean");

       assertNotNull(object);
       assertTrue(object instanceof CalculatorImpl);
       CalculatorImpl calc = (CalculatorImpl) object;
       assertEquals(10, calc.sum(4,6));
       assertEquals(12, calc.multiply(3,4));
   }

java.lang.reflect.UndeclaredThrowableException
   at $LocalBeanProxy0.sum($LocalBeanProxy0.java)
   at

Just a side note. It always annoyed me that the vm used "$Proxy123" instead of even trying to incorporate at least one of my interface names in the generated proxy name. Here, we're subclassing so we only have one clear name we could use to "seed" the generated proxy name, so we could finally offer people more intuitive looking stack traces.

Caused by: java.lang.IllegalStateException: Received method invocation and
cannot determine corresponding business interface: method=public int
$LocalBeanProxy0.sum(int,int)
   at
org .apache .openejb .core .ivm .BaseEjbProxyHandler.getInvokedInterface(BaseEjbProxyHandler.java:188)

Standard issue in that an interface was always required and the code is therefore only written to think in those terms. There'll probably be a few other places where we need to "widen" the understanding of the code. For the most part we should try and get everything done by passing the bean class name as the "interface", but we will have to add LocalBean as an understood view type. In CoreDeploymentInfo we have a list of business local interfaces that we populate when that object is constructed. We'll probably want to do identical processing for the "LocalBean" view.

I believe the code above is trying pull out the interface that was used so that the SessionContext.getInvokedBusinessInterface method will work. This might be one place where we can just pass the ejb class as the "interface".

JNDI:

I've added the following code to JndiBuilder.bind():

       try {

           if (deployment.isLocalbean()) {
               Class beanClass = deployment.getBeanClass();

LocalBeanReference ref = new LocalBeanReference(deployment);

               optionalBind(bindings, ref, "openejb/Deployment/" +
format(deployment.getDeploymentID(), beanClass.getName(), null));

               String internalName = "openejb/Deployment/" +
format(deployment.getDeploymentID(), beanClass.getName(),
InterfaceType.LOCALBEAN);
               bind(internalName, ref, bindings, beanInfo, beanClass);

               String name = strategy.getName(beanClass,
JndiNameStrategy.Interface.LOCALBEAN);
               bind("openejb/local/" + name, ref, bindings, beanInfo,
beanClass);
               bind("openejb/remote/" + name, ref, bindings, beanInfo,
beanClass);
           }
       } catch (NamingException e) {
throw new RuntimeException("Unable to bind localbean deployment
in jndi.", e);
       }

That seems good.

I created a LocalBeanReference class - which what is bound into JNDI. This takes a CoreDeploymentInfo object, and its getObject() creates a proxy.
Here's the code:

package org.apache.openejb.core.ivm.naming;

import org.apache.openejb.core.CoreDeploymentInfo;

import javax.naming.*;

public class LocalBeanReference extends Reference {
   private CoreDeploymentInfo deployment;


   public LocalBeanReference(CoreDeploymentInfo deployment) {
       this.deployment = deployment;
   }

   public Object getObject() throws javax.naming.NamingException {
       return deployment.getLocalBean();
   }
}

Nice.

I'm concerned that this isn't the right approach - I notice that the other interfaces bind a BusinessRemoteHome/BusinessLocalHome class, should this
follow the same pattern

Oh yea, you raise good point. We definitely do need some create method to be called in the reference, basically for the benefit of types like @Stateless and the new @ManagedBean which are instantiated on lookup. The container needs to get invoked on the lookup. I actually fabricated those BusinessRemoteHome/BusinessLocalHome just because all the code to do all this work was already everywhere in the system and the only thing missing was that EJB 3.0 business interfaces didn't have the option to have "home"s (aka a Factory). So I just fabricated one for each type.

So we probably do need some sort of LocalBeanHome kind of thing. Not sure if we could get by just reusing BusinessLocalHome, but if not, all the code that understands BusinessLocalHome will need to understand the new "LocalBeanHome".


Also, the default Global JNDI name is <className>LocalBean - is this ok?

Seems fine to me. The JNDI name <className> is tempting, but knowing the code I know that it complicates things. Users can always change it so it's a good start regardless.

Class loading:

I've added a method to CoreDeploymentInfo, which will create a new proxy. The code I've written to create the proxy using the ASM library returns a byte[]. To load this class I'm creating a new ClassLoader, which is a child
of the ClassLoader that the bean itself is created from, and calling
defineClass(). I'm not sure if this is right, I'd appreciate any thoughts
anyone has.

Not sure of other options on that front. I'd investigate what CGLib does, might be an idea there worth copying.

In terms of plain memory, we could probably share that fabricated classloader accross the whole app. Maybe create it in the assembler and stuff the same one in all the DeploymentInfo objects... or better yet create the proxies themselves in the assembler and stuff the proxy class in the DeploymentInfo as the "localBean" class.


-David

Reply via email to