Announcement

Collapse
No announcement yet.
X
  • Filter
  • Time
Clear All
new posts

    Using CDI with DMI with JBoss / J2EE6 (possible RFE)

    Hi All,

    Version: SmartGWTEE 4.1

    We are using DMI as a thin layer to call EJB where our application logic resides. The EJB returns JPA beans which are converted by SmartGWT Enterprise into data for the client. We wanted to annotate our EJB on the DMI usign CDI @Inject rather than having to use JNDI to lookup our EJBs. This also removes the requirement to add <resource> for every ejb to our web.xml. CDI is new in J2EE6 and quite helpful. http://cdi-spec.org/

    This is a modest improvement for us but may be helpful to others. This code has not been thoroughly analyzed so use at your own risk - of course feedback is welcome. The code requires that a DataSource named User has a DMI named UserDMI as a convention.

    The following code shows the DataSource using the factory lookup.


    Code:
    <DataSource ID="User" serverType="generic">
    	<fields>
    		<field name="id" type="integer" title="Id" hidden="true" primaryKey="true" length="20"/>
    		<field name="employeeId" type="text" title="Employee Id" required="true" length="20"/>
    		<field name="firstName" type="text" title="First Name" required="true" length="20"/>
    		<field name="lastName" type="text" title="Last Name" required="true" length="30"/>
    	</fields>
    
    	<serverObject lookupStyle="factory" className="com.ray.sc.DmiFactory" />
        
    </DataSource>
    The SmartGWT server calls the create(...) method on the factory and passes the DSRequest to the method. The create method looks up the CDI and passes back to SC so it can call fetch or one of the other DMI request methods.

    Code:
    package com.ray.sc;
    
    import javax.enterprise.context.spi.CreationalContext;
    import javax.enterprise.inject.spi.Bean;
    import javax.enterprise.inject.spi.BeanManager;
    import javax.naming.InitialContext;
    import javax.naming.NamingException;
    
    import com.isomorphic.datasource.DSRequest;
    import com.isomorphic.datasource.DataSource;
    
    /**
     * Allow SmartGWT server to lookup a DMI which is a CDI bean.
     * 
     * DMI is named after the DataSource such as User / UserDMI
     * and the CDI bean annotation {@code @Named} is defaulted which make the 
     * bean name {@code userDMI}. Please follow this convention.
     * 
     * @author ekr
     *
     */
    public class DmiFactory {
    	/**
    	 * Cached BeanManager
    	 */
    	private static final BeanManager beanManager = lookup();
    
    	/**
    	 * Required default ctor
    	 */
    	public DmiFactory() {
    	}
    	
    	/**
    	 * SmartGWT server calls this method. Looks at the data
    	 * source name to look up the DMI which is a CDI Managed
    	 * bean. We use the naming convention for the DMI which
    	 * the the DataSource name plus DMI. (User -> UserDMI)
    	 * 
    	 * @param dsRequest - used to get datasource
    	 * @return - the DMI looked up from the BeanManager
    	 * @throws Exception
    	 */
    	public Object create(DSRequest dsRequest) throws Exception {
    		DataSource ds = dsRequest.getDataSource();
    		String dsName = ds.getName();
    		String dmiName = dsName.substring(0, 1).toLowerCase() + dsName.substring(1) + "DMI";
    		return lookupBean(beanManager, dmiName);
    	}
    	
    	private static BeanManager lookup() {
    		BeanManager beanManager = null;
    		try {
    			InitialContext context = new InitialContext();
    			beanManager =  (BeanManager) context.lookup("java:comp/BeanManager");	
    		} catch (NamingException e) {
    			throw new RuntimeException(e);
    		}
    		return beanManager;
    	}
    	
    	private Object lookupBean(BeanManager beanManager, String beanName) {
            	Bean<?> bean = beanManager.getBeans(beanName).iterator().next();
            	CreationalContext<?> ctx = beanManager.createCreationalContext(bean);
            	Object o = beanManager.getReference(bean, bean.getClass(), ctx);
            	return o;
    	}
    
    }
    This is the DMI example which is a CDI bean. Notice the @Local EJB is annotated.

    Code:
    package com.ray.demo.gwt.server.dmi;
    
    import java.util.List;
    import java.util.logging.Logger;
    
    import javax.enterprise.context.RequestScoped;
    import javax.inject.Inject;
    import javax.inject.Named;
    
    import com.isomorphic.datasource.DSRequest;
    import com.isomorphic.datasource.DSResponse;
    import com.ray.demo.entity.User;
    import com.ray.demo.model.UserLocal;
    import com.ray.sc.dmi.DmiUtils;
    
    @Named
    @RequestScoped
    public class UserDMI {
    	
    	@Inject
    	private UserLocal userBean;
        
    	public DSResponse fetch(DSRequest dsRequest) throws Exception {
    		return DmiUtils.prepareResponse(userBean.findAllUsers());
    	}
    	
    	public DSResponse update(DSRequest dsRequest)  throws Exception {
    		User record = DmiUtils.applyRequestToBean(dsRequest, User.class);
    		List<String> roleCodes = getRoleCodesFromRequest(dsRequest);
    		User user = userBean.update(record, roleCodes);
    		return DmiUtils.prepareResponse(user);	
    	}
    	
    	
    	public DSResponse add(DSRequest dsRequest)  throws Exception {
    		User record = DmiUtils.applyRequestToBean(dsRequest, User.class);
    		List<String> roleCodes = getRoleCodesFromRequest(dsRequest);
    		User user = userBean.add(record, roleCodes);
    		return DmiUtils.prepareResponse(user);		
    	}
    	
    	
    	private List<String> getRoleCodesFromRequest(DSRequest dsRequest) {
    		return DmiUtils.getAppliedValue(dsRequest, "roleName");
    	}
    }
    So if something like this was added to SmartClient, I think it would be similar to the "spring" lookup.
    https://www.smartclient.com/smartgwtee/javadoc/com/smartgwt/client/docs/serverds/ServerObject.html#lookupStyle

    <serverObject lookupStyle="dmi" beanName="userDMI" or beanClass="com.ray.dmi.UserDMI" scope="application | session | request />

    Note that CDI for the most part uses type for matching so lookups internally use a type but can also be looked up by name
    like the factory example shown above. Here scope is probably like for the "attribute" lookup style.

    Thanks,
    Eric

    #2
    Hi Isomorphic,

    Does adding a lookup like Spring for CDI seem like something you would like to add? - seems like it could be done pretty nicely.

    Anyway, I found a problem in the lookup code that makes this break in JBoss EAP 6.1 / 6.2 or JBoss AS 7.2+. Please replace the following line of code.

    Code:
            Object o = beanManager.getReference(bean, bean.getBeanClass(), ctx);
    This should also work in any J2EE6/7 container.

    Eric

    Comment


      #3
      Yes, it does make sense to add, after all this is basically the Java core libraries absorbing features of Spring.

      It looks like you've got a solution that's fairly straightforward already (and thanks for sharing the code), but if you wanted support for this added to SmartGWT itself by a specific date, you can use Feature Sponsorship to have that done.

      Comment

      Working...
      X