Hot questions for Using Enterprise JavaBeans in jax rs

Question:

I get NullPointerException when I try to add mappings by creating my custom mapper in some container-managed objects. I tried it in @Stateless EJB service and @RequestScoped rest service. The error is similar and occurs in line:

modelMapper.addMappings(skipCdeMap);

Is it a bug? I assume that mapper is trying to do some reflection stuff on managed bean and because of that get NullPointerException.

This is my code:

PropertyMap<ObjectAbcDto, ObjectAbc> skipCdeMap =
    new PropertyMap<ObjectAbcDto, ObjectAbc>() {
        protected void configure() {
            skip().setObjectCde(null);
        }
    };

modelMapper.addMappings(skipCdeMap);

This is the error:

13:37:03,453 ERROR [stderr] (default task-12) org.modelmapper.ConfigurationException: ModelMapper configuration errors:
13:37:03,453 ERROR [stderr] (default task-12) 
13:37:03,453 ERROR [stderr] (default task-12) 1) Error reading class com.app.SomeRestEasyService$1
13:37:03,453 ERROR [stderr] (default task-12) 
13:37:03,453 ERROR [stderr] (default task-12) 2) Failed to configure mappings
13:37:03,454 ERROR [stderr] (default task-12) 
13:37:03,454 ERROR [stderr] (default task-12) 2 errors
13:37:03,454 ERROR [stderr] (default task-12) at org.modelmapper.internal.Errors.throwConfigurationExceptionIfErrorsExist(Errors.java:241)
13:37:03,454 ERROR [stderr] (default task-12) at org.modelmapper.internal.ExplicitMappingBuilder.build(ExplicitMappingBuilder.java:206)
13:37:03,454 ERROR [stderr] (default task-12) at org.modelmapper.internal.TypeMapImpl.addMappings(TypeMapImpl.java:72)
13:37:03,454 ERROR [stderr] (default task-12) at org.modelmapper.internal.TypeMapStore.getOrCreate(TypeMapStore.java:101)
13:37:03,454 ERROR [stderr] (default task-12) at org.modelmapper.ModelMapper.addMappings(ModelMapper.java:93)
13:37:03,454 ERROR [stderr] (default task-12) at com.app.SomeRestEasyService.create(SomeRestEasyService.java:129)
13:37:03,454 ERROR [stderr] (default task-12) at com.app.SomeRestEasyService$Proxy$_$$_WeldClientProxy.create(Unknown Source)        
[...]   
13:37:03,459 ERROR [stderr] (default task-12) Caused by: java.lang.NullPointerException
13:37:03,459 ERROR [stderr] (default task-12) at org.modelmapper.internal.ExplicitMappingBuilder.validateVisitedMappings(ExplicitMappingBuilder.java:236)
13:37:03,459 ERROR [stderr] (default task-12) at org.modelmapper.internal.ExplicitMappingBuilder.visitPropertyMap(ExplicitMappingBuilder.java:227)
13:37:03,459 ERROR [stderr] (default task-12) at org.modelmapper.PropertyMap.configure(PropertyMap.java:380)
13:37:03,459 ERROR [stderr] (default task-12) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
13:37:03,459 ERROR [stderr] (default task-12) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
13:37:03,459 ERROR [stderr] (default task-12) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
13:37:03,460 ERROR [stderr] (default task-12) at java.lang.reflect.Method.invoke(Method.java:606)
13:37:03,460 ERROR [stderr] (default task-12) at org.modelmapper.internal.ExplicitMappingBuilder.build(ExplicitMappingBuilder.java:194)
13:37

Answer:

I can answer on my own question. My assumptions was correct. When I created PropertyMap class in Pojo or in its own, dedicated class - mappings worked well. So I suppose that my previous error occurs because of reflection which ModelMapper does on class in which PropertyMap was created. If it is some Menaged Bean that would result with NullPointerException.

Question:

I have a web application with JAX-RS, CDI and EJB. In each resource I inject a Stateless SessionBean, and my question is whether it is possible to inject the same instances into a provider of JAX-RS and the Stateless SesionBean. I am trying to pass some data that come in each request to the Stateless SesionBean from a ContainerRequestFilter. All EJB components are accessed only by jax rs resources.

Example:

public class Bean {
    private String attr;

    // getter and setter
}

@Stateless
public class BeanService {

    @Inject
    Bean bean;

    public void doStuff() {
        bean.getAttr();
        // do something with bean.attr
    }
}

@Path("/bean")
public class BeanResource {

    @Inject
    BeanService service;

    @GET
    public void doStuff() {
        service.doStuff():
    }
}

@Provider
public class BeanRequestFilter implements ContainerRequestFilter {

    @Inject
    Bean bean;

    @Override
    public void filter(ContainerRequestContext containerRequestContext) throws IOException {
        String data = null; // <- get data from request
        bean.setAttr(data);
    }
}

Update

Change the Bean for Pojo, my only intention is use a class that hold some state that come in every request and can be transmited in each invocation, since the PojoResource to PojoService. I want to do it in this way because all the services retrive this data and I don't want to pass this as parameter on every method.


Answer:

This looks like your Bean class is essentially request scoped, so changing it to:

@RequestScoped
public class Bean {
    ...
}

should have the desired effect. The same instance will be injected in both the BeanRequestFilter and the BeanService.

However, I think you may also get what you're looking for by injecting the ContainerRequestContext directly into the BeanService and forgetting about Bean altogether.

@Stateless
public class BeanService {

   @Context
   ContainerRequestContext containerRequestContext;

   public void doStuff() {
      // <- get data from request
   }
}

Question:


Answer:

If I understand your request properly you look for a way to call your EJB from outside the container from a java main class?

In that case you need to

  • get an JNDI context with the correct properties for your container
  • lookup a reference to the EJB within the container
  • execute the business method on it

Here's an example for the WildFly:

public static void main(String[] args) throws NamingException {
        Properties jndiProps = new Properties();
        jndiProps.put(Context.INITIAL_CONTEXT_FACTORY, "org.jboss.naming.remote.client.InitialContextFactory");
        jndiProps.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming");
        jndiProps.put("jboss.naming.client.ejb.context", true);
        jndiProps.put(Context.PROVIDER_URL, "http-remoting://localhost:8080");
        jndiProps.put(Context.SECURITY_PRINCIPAL, "user");
        jndiProps.put(Context.SECURITY_CREDENTIALS, "xxx");
        Context ctx = new InitialContext(jndiProps);
        MyBeanRemote myBean = (MyBeanRemote) ctx.lookup("/<appname>/<beanname>!<fullqualifiedremoteinterface>");
        myBean.doSomething();
    }

Note the following:

  • You need to have a remote interface for your EJB
  • You may need to add an application user via script in WildFly's bin folder
  • You need to adapt the lookup string to your needs, you'll see it in WildFly logs during deployment (just leave out namespace prefix)

I included a full answer here for completeness, credits belong to user theisuru for my own question