Differences between revisions 7 and 8
Revision 7 as of 2011-12-16 21:39:56
Size: 7399
Editor: LennyPrimak
Comment:
Revision 8 as of 2011-12-17 23:11:40
Size: 1585
Editor: LennyPrimak
Comment:
Deletions are marked like this. Additions are marked like this.
Line 6: Line 6:
Keep in mind this only works for Stateless session beans,
for stateful beans, see the usage example below.
Code excerpts are from [[http://code.google.com/p/flowlogix/wiki/TapestryLibrary|Flow Logix Tapestry Library]]
Line 26: Line 25:
{{{ [[http://code.google.com/p/flowlogix/source/browse/tapestry-services/src/main/java/com/flowlogix/web/services/internal/EJBAnnotationWorker.java|EJBAnnotationWorker.java]]
Line 28: Line 27:
/**
 * Inject an EJB into tapestry sources
 *
 * @author Magnus
 * Enhancements by Lenny Primak
 */
public class EJBAnnotationWorker implements ComponentClassTransformWorker2
{
    @Override
    @SneakyThrows(NamingException.class)
    public void transform(PlasticClass plasticClass,
            TransformationSupport support, MutableComponentModel model)
    {
        for (PlasticField field : plasticClass.getFieldsWithAnnotation(EJB.class))
        {
            final EJB annotation = field.getAnnotation(EJB.class);
            String fieldType = field.getTypeName();
            String lookupname = null;

            //try lookup
            if (!isBlankOrNull(annotation.lookup()))
            {
                lookupname = annotation.lookup();
            } //try name
            else if(!isBlankOrNull(annotation.name()))
            {
                lookupname = annotation.name();
            }
            else if(!isBlankOrNull(annotation.beanName()))
            {
                lookupname = annotation.beanName();
            }

            //use type
            if (lookupname == null)
            {
                lookupname = guessByType(fieldType);
            }

            lookupname = prependPortableName(lookupname);

            Object injectionValue = locator.getJNDIObject(lookupname);

            if (injectionValue != null)
            {
                field.inject(injectionValue);
                field.claim(annotation);
            }
        }
    }
    
    
    public static String guessByType(String type)
    {
        String lookupname = type.substring(type.lastIndexOf(".") + 1);
        // support naming convention that strips Local/Remote from the
        // end of an interface class to try to determine the actual bean name,
        // to avoid @EJB(beanName="myBeanName"), and just use plain old @EJB
        String uc = lookupname.toUpperCase();
        if (uc.endsWith(LOCAL) || uc.endsWith(REMOTE)) {
            lookupname = StripLocalPattern.matcher(lookupname).replaceFirst("");
        }
        return lookupname;
    }
    
    
    public static String prependPortableName(String lookupname)
    {
        //convert to jndi name
        if (!lookupname.startsWith("java:"))
        {
            lookupname = "java:module/" + lookupname;
        }
        return lookupname;
    }
    

    private boolean isBlankOrNull(String s)
    {
        return s == null || s.trim().equals("");
    }
    

    private final JNDIObjectLocator locator = new JNDIObjectLocator();
    private static final String REMOTE = "REMOTE";
    private static final String LOCAL = "LOCAL";
    public static final Pattern StripLocalPattern = Pattern.compile(LOCAL + "|" + REMOTE, Pattern.CASE_INSENSITIVE);
}


}}}

== Using @EJB in your client code ==
== Using Stateless @EJB in your client code ==
Line 137: Line 44:
    @SetupRender
    private void init()
    {
        if(statefulBeanExists == false)
        {
            statefulBean = new JNDIObjectLocator().getObject(MyStatefulBeanLocal.class);
        }

        if(statefulBean2 == null)
        {
            // A new instance of JNDIObjectLocator has to be used for each lookup,
            // because it caches lookups, and for stateful session beans this is a no-no
            statefulBean2 = new JNDIObjectLocator().getObject(MyStatefulBeanLocal.class);
        }
    }


    @SessionState(create = false) private MyStatefulBeanLocal statefulBean;
    private boolean statefulBeanExists;

    // another example
    private @Persist MyStatefulBeanLocal statefulBean2;
  private @EJB @Stateful MyStatefulBeanLocal statefulBean; // stored as SessionState object
  private @EJB @Stateful(isSessionAttribute = true) MyStatefulBeanLocal statefulBean2; // stored as SessionAttribute object
Line 163: Line 50:
There is an even simpler way to use Stateful beans, if you are using
the [[http://code.google.com/p/flowlogix/wiki/TapestryLibrary|Flow Logix Tapestry Library]]:
{{{
  private @EJB @Stateful MyStatefulBeanLocal statefulBean; // its that simple!
}}}
Line 171: Line 52:
{{{
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import javax.naming.InitialContext;
import javax.naming.NameNotFoundException;
import javax.naming.NamingException;
import lombok.Getter;
import lombok.SneakyThrows;

/**
 * JNDIObjectLocator is used to centralize all JNDI lookups. It minimises the overhead of JNDI lookups by caching the
 * objects it looks up.
 *
 * @author Geoff Callendar
 * Enhancements by Lenny Primak
 */
public class JNDIObjectLocator
{
    @SneakyThrows(NamingException.class)
    public JNDIObjectLocator()
    {
        initialContext = new InitialContext();
    }
    
    
    @SneakyThrows(NamingException.class)
    public<T> T getObject(Class<T> beanClass)
    {
        String name = EJBAnnotationWorker.guessByType(beanClass.getName());
        return getObject(EJBAnnotationWorker.prependPortableName(name));
    }
    
    
    @SuppressWarnings("unchecked")
    public<T> T getObject(String jndiName) throws NamingException
    {
        return (T)getJNDIObject(jndiName);
    }

    
    public synchronized void clear()
    {
        jndiObjectCache.clear();
    }
    

    public Object getJNDIObject(String jndiName) throws NamingException
    {
        Object jndiObject = jndiObjectCache.get(jndiName);

        if (jndiObject == null && !jndiObjectCache.containsKey(jndiName))
        {
            try
            {
                jndiObject = lookup(jndiName);
                jndiObjectCache.put(jndiName, jndiObject);
            } catch (NamingException e)
            {
                clear();
                throw e;
            }
        }
        return jndiObject;
    }

    
    private synchronized Object lookup(String name) throws NamingException
    {

        // Recheck the cache because the name we're looking for may have been added while we were waiting for sync.

        if (!jndiObjectCache.containsKey(name))
        {
            try
            {
                return getInitialContext().lookup(name);
            } catch (NameNotFoundException e)
            {
                clear();
                throw e;
            }
        } else
        {
            return jndiObjectCache.get(name);
        }
    }

    
    @Getter private final InitialContext initialContext;
    private final Map<String, Object> jndiObjectCache = Collections.synchronizedMap(new HashMap<String, Object>());
}

}}}
[[http://code.google.com/p/flowlogix/source/browse/tapestry-services/src/main/java/com/flowlogix/ejb/JNDIObjectLocator.java|JNDIObjectLocator.java]]

How to make @EJB Annotation work in Tapestry pages and components

This works on Tapestry 5.3 or higher. Has been tested with Glassfish 3.1.

Code excerpts are from Flow Logix Tapestry Library

In your AppModule.java file, add the following method

    @Contribute(ComponentClassTransformWorker2.class)
    @Primary
    public static void provideClassTransformWorkers(OrderedConfiguration<ComponentClassTransformWorker2> configuration)
    {
        configuration.addInstance("EJB", EJBAnnotationWorker.class, "before:Property");
    }

EJBAnnotationWorker.java

(add this to one of your non-tapestry packages)

EJBAnnotationWorker.java

Using Stateless @EJB in your client code

class SomeTapestryPage
{
  private @EJB MyStatelessBeanLocal localBean;
}

Using Stateful Session Beans in your client code

class SomeTapestryPage
{
  private @EJB @Stateful MyStatefulBeanLocal statefulBean;  // stored as SessionState object
  private @EJB @Stateful(isSessionAttribute = true) MyStatefulBeanLocal statefulBean2;  // stored as SessionAttribute object
}

JNDIObjectLocator.java, variant taken from Tapestry JumpStart

JNDIObjectLocator.java

JEE-Annotation (last edited 2011-12-17 23:11:40 by LennyPrimak)