Search code examples
dependency-injectionstruts2jpa-2.0jboss7.xwebsphere-7

EntityManager injection works in JBoss 7.1.1 but not WebSphere 7


I've built an EJB that will manage my database access. I'm building a web app around it that uses Struts 2.

The problem I'm having is when I deploy the ear, the EntityManager doesn't get injected into my service class (and winds up null and results in NullPointerExceptions).

The weird thing is, it works on JBoss 7.1.1 but not on WebSphere 7.

You'll notice that Struts doesn't inject the EJB, so I've got some intercepter code that does that. My current working theory right now is that the WS7 container can't inject the EntityManager because of Struts for some unknown reason. My next step is to try Spring next, but I'd really like to get this to work if possible.

I've spent a few days searching and trying various things and haven't had any luck. I figured I'd give this a shot. Let me know if I can provide additional information.

<?xml version="1.0" encoding="UTF-8"?>
<persistence    xmlns="http://java.sun.com/xml/ns/persistence"
                xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.0"
                xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
    <persistence-unit name="JPATestPU" transaction-type="JTA">
        <description>JPATest Persistence Unit</description>
        <jta-data-source>jdbc/Test-DS</jta-data-source>
        <class>org.jaredstevens.jpatest.db.entities.User</class>
        <properties>
            <property name="hibernate.hbm2ddl.auto" value="update"/>
        </properties>
    </persistence-unit>
</persistence>
package org.jaredstevens.jpatest.db.entities;

import java.io.Serializable;
import javax.persistence.*;

@Entity
@Table
public class User implements Serializable {
    private static final long serialVersionUID = -2643583108587251245L;

    private long id;
    private String name;
    private String email;

    @Id
    @GeneratedValue(strategy = GenerationType.TABLE)
    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    @Column(nullable=false)
    public String getName() {
        return this.name;
    }

    public void setName( String name ) {
        this.name = name;
    }

    @Column(nullable=false)
    public String getEmail() {
        return this.email;
    }

    @Column(nullable=false)
    public void setEmail( String email ) {
        this.email= email;
    }
}
package org.jaredstevens.jpatest.db.services;
import java.util.List;

import javax.ejb.Remote;
import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.PersistenceContextType;
import javax.persistence.Query;

import org.jaredstevens.jpatest.db.entities.User;
import org.jaredstevens.jpatest.db.interfaces.IUserService;

@Stateless(name="UserService",mappedName="UserService")
@Remote
public class UserService implements IUserService {
    @PersistenceContext(unitName="JPATestPU",type=PersistenceContextType.TRANSACTION)
    private EntityManager em;

    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    public User getUserById(long userId) {
        User retVal = null;
        if(userId > 0) {
            retVal = (User)this.getEm().find(User.class, userId);
        }
        return retVal;
    }

    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    public List<User> getUsers() {
        List<User> retVal = null;
        String sql;
        sql = "SELECT u FROM User u ORDER BY u.id ASC";
        Query q = this.getEm().createQuery(sql);
        retVal = (List<User>)q.getResultList();
        return retVal;
    }

    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    public void save(User user) {
        this.getEm().persist(user);
    }

    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    public boolean remove(long userId) {
        boolean retVal = false;
        if(userId > 0) {
            User user = null;
            user = (User)this.getEm().find(User.class, userId);
            if(user != null) this.getEm().remove(user);
            if(this.getEm().find(User.class, userId) == null) retVal = true;
        }
        return retVal;
    }

    public EntityManager getEm() {
        return em;
    }

    public void setEm(EntityManager em) {
        this.em = em;
    }
}
package org.jaredstevens.jpatest.actions.user;

import javax.ejb.EJB;
import org.jaredstevens.jpatest.db.entities.User;
import org.jaredstevens.jpatest.db.interfaces.IUserService;

import com.opensymphony.xwork2.ActionSupport;

public class UserAction extends ActionSupport {
    @EJB(mappedName="UserService")
    private IUserService userService;

    private static final long serialVersionUID = 1L;
    private String userId;
    private String name;
    private String email;

    private User user;

    public String getUserById() {
        String retVal = ActionSupport.SUCCESS;
        this.setUser(userService.getUserById(Long.parseLong(this.userId)));
        return retVal;
    }

    public String save() {
        String retVal = ActionSupport.SUCCESS;
        User user = new User();
        if(this.getUserId() != null && Long.parseLong(this.getUserId()) > 0) user.setId(Long.parseLong(this.getUserId()));
        user.setName(this.getName());
        user.setEmail(this.getEmail());
        userService.save(user);
        this.setUser(user);
        return retVal;
    }

    public String getUserId() {
        return this.userId;
    }

    public void setUserId(String userId) {
        this.userId = userId;
    }

    public String getName() {
        return this.name;
    }

    public void setName( String name ) {
        this.name = name;
    }

    public String getEmail() {
        return this.email;
    }

    public void setEmail( String email ) {
        this.email = email;
    }

    public User getUser() {
        return this.user;
    }

    public void setUser(User user) {
        this.user = user;
    }
}
package org.jaredstevens.jpatest.utils;

import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.Interceptor;

public class EJBAnnotationProcessorInterceptor implements Interceptor {
    private static final long serialVersionUID = 1L;

    public void destroy() {
    }

    public void init() {
    }

    public String intercept(ActionInvocation ai) throws Exception {
        EJBAnnotationProcessor.process(ai.getAction());
        return ai.invoke();
    }
}
package org.jaredstevens.jpatest.utils; 

import java.lang.reflect.Field;

import javax.ejb.EJB;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

public class EJBAnnotationProcessor {
    public static void process(Object instance)throws Exception{
        Field[] fields = instance.getClass().getDeclaredFields();
        if(fields != null && fields.length > 0){
            EJB ejb;
            for(Field field : fields){
                ejb = field.getAnnotation(EJB.class);
                if(ejb != null){
                    field.setAccessible(true);
                    field.set(instance, EJBAnnotationProcessor.getEJB(ejb.mappedName()));
                }
            }
        }
    }

    private static Object getEJB(String mappedName) {
        Object retVal = null;
        String path = "";
        Context cxt = null;
        String[] paths = {"cell/nodes/virgoNode01/servers/server1/","java:module/"};
        for( int i=0; i < paths.length; ++i )
        {
            try {
                path = paths[i]+mappedName;
                cxt = new InitialContext();
                retVal = cxt.lookup(path);
                if(retVal != null) break;
            } catch (NamingException e) {
                retVal = null;
            }
        }
        return retVal;
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
    "http://struts.apache.org/dtds/struts-2.0.dtd">

<struts>
    <constant name="struts.devMode" value="true" />

    <package name="basicstruts2" namespace="/diagnostics" extends="struts-default">
        <interceptors>
            <interceptor name="ejbAnnotationProcessor"
                class="org.jaredstevens.jpatest.utils.EJBAnnotationProcessorInterceptor"/>
            <interceptor-stack name="baseStack">
            <interceptor-ref name="defaultStack"/>
            <interceptor-ref name="ejbAnnotationProcessor"/>
            </interceptor-stack>
        </interceptors>
        <default-interceptor-ref name="baseStack"/>
    </package>

    <package name="restAPI" namespace="/conduit" extends="json-default">
        <interceptors>
            <interceptor name="ejbAnnotationProcessor"
                class="org.jaredstevens.jpatest.utils.EJBAnnotationProcessorInterceptor" />
            <interceptor-stack name="baseStack">
                <interceptor-ref name="defaultStack" />
                <interceptor-ref name="ejbAnnotationProcessor" />
            </interceptor-stack>
        </interceptors>
        <default-interceptor-ref name="baseStack" />

        <action name="UserAction.getUserById"
            class="org.jaredstevens.jpatest.actions.user.UserAction" method="getUserById">
            <result type="json">
                <param name="ignoreHierarchy">false</param>
                <param name="includeProperties">
                    ^user\.id,
                    ^user\.name,
                    ^user\.email
                </param>
            </result>
            <result name="error" type="json" />
        </action>

        <action name="UserAction.save"
            class="org.jaredstevens.jpatest.actions.user.UserAction" method="save">
            <result type="json">
                <param name="ignoreHierarchy">false</param>
                <param name="includeProperties">
                    ^user\.id,
                    ^user\.name,
                    ^user\.email
                </param>
            </result>
            <result name="error" type="json" />
        </action>
    </package>
</struts>

Stack Trace

java.lang.NullPointerException
    org.jaredstevens.jpatest.actions.user.UserAction.save(UserAction.java:38)
    sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:60)
    sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:37)
    java.lang.reflect.Method.invoke(Method.java:611)
    com.opensymphony.xwork2.DefaultActionInvocation.invokeAction(DefaultActionInvocation.java:453)
    com.opensymphony.xwork2.DefaultActionInvocation.invokeActionOnly(DefaultActionInvocation.java:292)
    com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:255)
    org.jaredstevens.jpatest.utils.EJBAnnotationProcessorInterceptor.intercept(EJBAnnotationProcessorInterceptor.java:21)
    com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:249)
    org.apache.struts2.interceptor.debugging.DebuggingInterceptor.intercept(DebuggingInterceptor.java:256)
    com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:249)
    com.opensymphony.xwork2.interceptor.DefaultWorkflowInterceptor.doIntercept(DefaultWorkflowInterceptor.java:176)
    com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:98)
    com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:249)
    com.opensymphony.xwork2.validator.ValidationInterceptor.doIntercept(ValidationInterceptor.java:265)
    org.apache.struts2.interceptor.validation.AnnotationValidationInterceptor.doIntercept(AnnotationValidationInterceptor.java:68)
    com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:98)
    com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:249)
    com.opensymphony.xwork2.interceptor.ConversionErrorInterceptor.intercept(ConversionErrorInterceptor.java:138)
    com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:249)
    com.opensymphony.xwork2.interceptor.ParametersInterceptor.doIntercept(ParametersInterceptor.java:211)
    com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:98)
    com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:249)
    com.opensymphony.xwork2.interceptor.ParametersInterceptor.doIntercept(ParametersInterceptor.java:211)
    com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:98)
    com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:249)
    com.opensymphony.xwork2.interceptor.StaticParametersInterceptor.intercept(StaticParametersInterceptor.java:190)
    com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:249)
    org.apache.struts2.interceptor.MultiselectInterceptor.intercept(MultiselectInterceptor.java:75)
    com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:249)
    org.apache.struts2.interceptor.CheckboxInterceptor.intercept(CheckboxInterceptor.java:90)
    com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:249)
    org.apache.struts2.interceptor.FileUploadInterceptor.intercept(FileUploadInterceptor.java:243)
    com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:249)
    com.opensymphony.xwork2.interceptor.ModelDrivenInterceptor.intercept(ModelDrivenInterceptor.java:100)
    com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:249)
    com.opensymphony.xwork2.interceptor.ScopedModelDrivenInterceptor.intercept(ScopedModelDrivenInterceptor.java:141)
    com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:249)
    com.opensymphony.xwork2.interceptor.ChainingInterceptor.intercept(ChainingInterceptor.java:145)
    com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:249)
    com.opensymphony.xwork2.interceptor.PrepareInterceptor.doIntercept(PrepareInterceptor.java:171)
    com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:98)
    com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:249)
    com.opensymphony.xwork2.interceptor.I18nInterceptor.intercept(I18nInterceptor.java:176)
    com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:249)
    org.apache.struts2.interceptor.ServletConfigInterceptor.intercept(ServletConfigInterceptor.java:164)
    com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:249)
    com.opensymphony.xwork2.interceptor.AliasInterceptor.intercept(AliasInterceptor.java:192)
    com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:249)
    com.opensymphony.xwork2.interceptor.ExceptionMappingInterceptor.intercept(ExceptionMappingInterceptor.java:187)
    com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:249)
    org.apache.struts2.impl.StrutsActionProxy.execute(StrutsActionProxy.java:54)
    org.apache.struts2.dispatcher.Dispatcher.serviceAction(Dispatcher.java:511)
    org.apache.struts2.dispatcher.ng.ExecuteOperations.executeAction(ExecuteOperations.java:77)
    org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter.doFilter(StrutsPrepareAndExecuteFilter.java:91)
    com.ibm.ws.webcontainer.filter.FilterInstanceWrapper.doFilter(FilterInstanceWrapper.java:188)
    com.ibm.ws.webcontainer.filter.WebAppFilterChain.doFilter(WebAppFilterChain.java:116)
    com.ibm.ws.webcontainer.filter.WebAppFilterChain._doFilter(WebAppFilterChain.java:77)
    com.ibm.ws.webcontainer.filter.WebAppFilterManager.doFilter(WebAppFilterManager.java:908)
    com.ibm.ws.webcontainer.filter.WebAppFilterManager.invokeFilters(WebAppFilterManager.java:997)
    com.ibm.ws.webcontainer.extension.DefaultExtensionProcessor.invokeFilters(DefaultExtensionProcessor.java:1062)
    com.ibm.ws.webcontainer.extension.DefaultExtensionProcessor.handleRequest(DefaultExtensionProcessor.java:982)
    com.ibm.ws.webcontainer.webapp.WebApp.handleRequest(WebApp.java:3935)
    com.ibm.ws.webcontainer.webapp.WebGroup.handleRequest(WebGroup.java:276)
    com.ibm.ws.webcontainer.WebContainer.handleRequest(WebContainer.java:931)
    com.ibm.ws.webcontainer.WSWebContainer.handleRequest(WSWebContainer.java:1583)
    com.ibm.ws.webcontainer.channel.WCChannelLink.ready(WCChannelLink.java:186)
    com.ibm.ws.http.channel.inbound.impl.HttpInboundLink.handleDiscrimination(HttpInboundLink.java:452)
    com.ibm.ws.http.channel.inbound.impl.HttpInboundLink.handleNewRequest(HttpInboundLink.java:511)
    com.ibm.ws.http.channel.inbound.impl.HttpInboundLink.processRequest(HttpInboundLink.java:305)
    com.ibm.ws.http.channel.inbound.impl.HttpInboundLink.ready(HttpInboundLink.java:276)
    com.ibm.ws.tcp.channel.impl.NewConnectionInitialReadCallback.sendToDiscriminators(NewConnectionInitialReadCallback.java:214)
    com.ibm.ws.tcp.channel.impl.NewConnectionInitialReadCallback.complete(NewConnectionInitialReadCallback.java:113)
    com.ibm.ws.tcp.channel.impl.AioReadCompletionListener.futureCompleted(AioReadCompletionListener.java:165)
    com.ibm.io.async.AbstractAsyncFuture.invokeCallback(AbstractAsyncFuture.java:217)
    com.ibm.io.async.AsyncChannelFuture.fireCompletionActions(AsyncChannelFuture.java:161)
    com.ibm.io.async.AsyncFuture.completed(AsyncFuture.java:138)
    com.ibm.io.async.ResultHandler.complete(ResultHandler.java:204)
    com.ibm.io.async.ResultHandler.runEventProcessingLoop(ResultHandler.java:775)
    com.ibm.io.async.ResultHandler$2.run(ResultHandler.java:905)
    com.ibm.ws.util.ThreadPool$Worker.run(ThreadPool.java:1604)

Solution

  • The real problem here was the EJB wasn't injecting. My interceptor code couldn't successfully lookup the EJB in JNDI and was setting my local reference to it to null. Once this was fixed, the EntityManager gets injected and everything is happy again.

    The solution to this was two-fold:

    1. The JNDI lookup in my interceptor class was failing because the path I hardcoded was wrong for the server I'm on.
    2. The WS7 configuration needed an alias in the EJB settings for the application so that my interceptor class could find the correct EJB.

    To fix #1 - I modified my code. I need a better solution for this though that is server agnostic.

    To fix #2:

    1. Log into the console. (http://servername:9080/admin)
    2. Applications->Application Types->Websphere Enterprise Applications.
    3. Click on your application name (in my case, JPATestEAR).
    4. On the right hand side there are links. Click 'EJB JNDI Names' (It should list all of your EJBs).
    5. On the right side, there are radio buttons and form fields where you can enter an alias for each EJB. I used 'JNDI Name For All Interfaces' and entered my EJB name in the field 'UserService'.
    6. Click 'Save' and then save the settings again.

    Thanks @bkail for getting me on the right track.