Wierd problem here. I have a bean with a remote interface declared and an implementation defined as a Stateless bean. Since I want to be able to replace the bean with a different bean depending on the implementation requirements, I have an ejb-reference declared in glassfish-web.xml.
I can successfully inject the bean if I use the name syntax to refer to the reference name like #EJB(name = "BeanReference"). I can also do a lookup: new InitialContext().lookup("java:comp/env/BeanReference").
The weird thing happens when I don't have any injections at all (no #EJB). Then the lookup fails with "javax.naming.NameNotFoundException: No object bound to name java:comp/env/BeanReference". If I list the pairs in "java:comp/env" it confirms that the reference doesn't exist unless an #EJB injection occurs somewhere in the application. I have checked the jndi-name entry and confirmed it matches the output from Glassfish during initialization.
I also tried using #Local instead of #Remote and get the same results. I prefer the #Remote since in an actual deployment the MyBean implementation will likely reside on a remote glassfish instance. It is local only for development and testing.
Using Glassfish 3.1.1 and NetBeans 7.1.2. Code snippets below...exception handling omitted for clarity.
#Remote
public interface MyBean {
public String doSomething();
}
#Stateless
public class MyBeanImpl implements MyBean {
#Override
public String doSomething() {
return "something";
}
}
#Stateless
#LocalBean
public class MyOtherBean {
public MyOtherBean() {
Context ctx = new InitialContext();
MyBean myBean = (MyBean)ctx.lookup("java:comp/env/BeanReference");
String rc = myBean.doSomething();
System.out.println("rc = " + rc);
}
}
<ejb-ref>
<ejb-ref-name>BeanReference</ejb-ref-name>
<jndi-name>java:global/appName/MyBeanImpl!com.test.bean.MyBean</jndi-name>
</ejb-ref>
Related
I am trying to initialize my web service resources by myself overriding the getSingletons method from Application class. I am using Jboss EAP 6.0 app server. Below is the code
#ApplicationPath("/rest")
public class MyApplication extends Application {
private final Set<Object> singletons = new HashSet<Object>();
public MyApplication() {
singletons.add(new StudentFacade());
}
#Override
public Set<Object> getSingletons() {
return null; // Accidentally kept it as null but still my webservice was working. Dont know how it was working.
}
}
#Path("student")
public class StudentFacade {
#Resource(lookup = "java:global/ejb-beginner-sessions-ear/ejb-beginner-sessions-impl/StudentServiceBean!com.ejb.beginner.sessions.api.StudentService")
private StudentService studentService;
private static final Logger LOGGER = Logger.getLogger(StudentFacade.class.getName());
#GET
#Path("list")
#Produces(MediaType.APPLICATION_JSON)
public Student getStudents() {
LOGGER.info("studentService: " + studentService);
return new Student();
}
}
Then I realized that getSingletons method is returning null and wondered how my webservice is still working.
I thought that because I am not returning the singletons, the application server is initializing the webservices by itself. So I removed #Resource and used #Inject and I got a WELD exception saying the dependency is not found.
Then I changed it to return the singletons and then #Resource did not lookup the ejb and studentService was null. So I used InitialContext to lookup the ejb and it worked.
try {
InitialContext initialContext = new InitialContext();
StudentService studentService = (StudentService) initialContext
.lookup("java:global/ejb-beginner-sessions-ear/ejb-beginner-sessions-impl/StudentServiceBean!com.ejb.beginner.sessions.api.StudentService");
LOGGER.info("studentService1: " + studentService);
} catch (NamingException e) {
e.printStackTrace();
}
Can anyone please tell me
Why did the application server initialize the webservice despite I returned null
When I returned null, why #Inject failed and only #Resource was working.
When I returned singletons, why #Resource failed and only InitialContext was working.
"1. Why did the application server initialize the webservice despite I returned null"
When there are no classes/objects (or just empty sets) returned during lookup, the behavior will revert to classpath scanning for resources and providers (this is explained in the JAX-RS spec). Once any resource class or provider is returned from getClasses or getSingleton, it is assumed the developer handles the registration, and classpath registration is disabled.
"2. When I returned null, why #Inject failed and only #Resource was working."
I don't see a success case in any of your arguments for #Inject, so I will not try to make a comment on that. As for #Resource, it seems to be the correct way to inject your resource. When classpath registration is enabled, you don't create the JAX-RS class, the runtime does. The runtime also handles the injection. If you create the instance yourself, the runtime will not try to inject.
"3. When I returned singletons, why #Resource failed and only InitialContext was working."
This is partly answered above. When you instantiate the class, injection is not performed. When the runtime creates the instances, it goes through the injection process.
One thing to note is that when you instantiate, the resource class will be a singleton, meaning only one instance per application. This may or may not be the desired behavior. When the runtime instantiates the class, by default it will be in request scoped, meaning one instance created per request. Again, may or may not be what you want. If you want to maintain the classpath registration, and still be singleton, then annotation the resource class with #javax.inject.Singleton should make it a singleton, and still allow injection, as the runtime still instantiates the class. If you want the classes to be request scoped, and want to register all your classes yourself, the override public Set<Class<?>> getClasses() and add your classes to the set.
I have a decorator on an EJB (so this decorator is CDI if I understand it correct). I need do some business logic depending on the role the logged in user is. So in the EJB I have the SessionContext, but since this is an EJB object, I need to look it up via JNDI.
InitialContext ic = new InitialContext();
SessionContext ctx = (SessionContext) ic.lookup("java:comp/EJBContext");
This works, but creates another error. When I try to call ctx.isCallerInRole("MyRole"); I get this error:
Caused by: java.lang.IllegalStateException: Operation not allowed
at com.sun.ejb.containers.SessionContextImpl.checkAccessToCallerSecurity(SessionContextImpl.java:205)
at com.sun.ejb.containers.EJBContextImpl.isCallerInRole(EJBContextImpl.java:447)
at com.example.decorators.MyDecorator.findAll(MyDecorator.java:46)
What I expect is happening is that a CDI is not allowed to ask the SessionContext if the logged in user is in the specified role. Is there a way how I can solve this? I've been roaming Google for a few days now, without success.
Erates
Edit:
This is what I have at the moment:
Interface:
#Local
public interface StatelessSessionBeanLocal extends Serializable {
<T> Collection<T> findAll(Class<T> clazz);
boolean isCallerInRole(String role);
}
EJB:
#Stateless
public class StatelessSessionBean implements StatelessSessionBeanLocal {
#Resource
private SessionContext ctx;
#Override
public <T> Collection<T> findAll(Class<T> clazz){
...
}
#Override
public boolean isCallerInRole(String role){
return ctx.isCallerInRole(role);
}
}
Decorator:
#Decorator
public abstract class StatelessSessionBeanDecorator implements StatelessSessionBeanLocal {
#Inject
#Delegate
StatelessSessionBeanLocal sb;
#Override
public <T> Collection<T> findAll(Class<T> clazz){
if (sb.isCallerInRole("TestRole")){
return new ArrayList();
} else {
return sb.findAll(clazz);
}
}
}
This gives me a NullPointerException at StatelessSessionBean.isCallerInRole pointing to the fact that the SessionContext is not injected. (I think because of the difference between SessionContext (EJB) and Inject (CDI)) Note, the EJB and Decorator are in different packages in different JARs in the EAR.
Problem was a Classloader issue.
ear
| - lib
| | - custom decorators.jar
| - custom ejb
| - ejb
| - war
I used a producer class that creates the EntityManager and SessionContext using the #Produces annotations on them. This producer class was in the "ejb" jar. On the decorators I used a provided dependency to the "ejb" so at that point, it knew where the #Inject would come from.
But once in runtime, the custom EJB would find the decorator because it is in the libs folder, but the decorator could not find the Produced SessionContext or EntityManager.
Now I've moved the decorators inside the "custom ejb" and all works just fine and dandy.
The SessionContext is created for each bean instance, with the lookup approach you didn't obtain the ctx bind to your bean instance so the method is not allowed.
Try using #Resource injection to obtain the bean context.
I'm currently puzzled with the way Glassfish 3.1.2.2 handles EJBs.
I have an OSGi project, which consists of many OSGi bundles (jars). Also, there are a few WARs, including a Tapestry web application.
In one such bundle, let's call it "interfaces.jar", I have an interface defined:
public interface MyInterface() {
public static final String JNDI_NAME = "java:global/MyInterface";
void myMethod();
}
The implementation of that interface is as following, and it's contained in bundle "beans.jar":
#Stateless
#EJB(name = MyInterface.JNDI_NAME, beanInterface = MyInterface)
public class MyBean implements MyInterface() {
void myMethod() {
...
}
}
I am calling it from my Tapestry WAR app via JNDI lookup:
InitialContext.doLookup(MyInterface.JNDI_NAME);
Now, I was reading EJB 3.1 specification, and it says that I can one of the following scenarios:
Interface has #Local annotation; EJB is implementing this interface.
Interface is a plain Java interface without annotation; EJB with #Local annotation is implementing it.
Interface is a plain Java interface without annotation; EJB is implementing it.
Interface is a plain Java interface without annotation; EJB with #Local annotation is not implementing it.
EJB doesn’t have any special annotations.
So, by elimination:
I don't have #Local on interface
I don't have #Local on EJB
Seems somewhat right
I don't have #Local on EJB
I have #EJB annotation on my EJB
So, it seems that it's case 3:
"Because it’s the only implemented interface of the EJB, a container assumes that it must be a local business interface."
Now, a few questions:
Is my interface a local or remote one, since there is no local or remote annotation?
If it is local, I should be able to inject it with #EJB annotation, but it fails?
If it's remote, it is not in compliance with the explanation a few lines above?
If I add either #Local or #Remote, and perform JNDI lookup, I get a naming exception and NPE telling me there is nothing under that JNDI_NAME. How is that possible?
What exactly does #EJB(name = ..., beanInterface = ...) do on bean class and how does it interact with #Local and #Remote annotations?
1.)
First, let's see your example without the "unnecessary" #EJB annotation
#Stateless
public class MyBean implements MyInterface() {
void myMethod() {
...
}
}
You can now see clearly, that the EJB implements only one interface. As you mentioned in the 3rd points, "Interface is a plain Java interface without annotation; EJB is implementing it.", so MyInterface is a Local Business Interface of the MyBean EJB.
2.)
You use wrong JNDI name for the lookup:
#Stateless
public class MyBean implements MyInterface() {
...
The global JNDI name of your EJB:
java:global[/app-name][/module-name]/MyBean[!interface-name]
The interface-name is "MyInterface", but it is optional if there are no other business interfaces for your beans, like here, so you can skip it.
You have to figure out what the application- and module-name is for your bean in case of OSGI.
In a simple EJB application, the application-name is the name of the .ear file, and the module-name is the name of the .war/.jar file. application-name can be skipped if your module is not packaged in an ear.
So for example:
new InitialContext().lookup("java:global/myModuleName/MyBean");
5.)
#Stateless
#EJB(name = "MyInterface", beanInterface = MyInterface.class)
public class MyBean implements MyInterface() {
void myMethod() {
...
}
}
Here the #EJB annotation creates an EJB and put a reference to it into the Environment Naming Context (ENC). So it does nothing with the MyBean EJB, just expand its ENC with a new entry.
So from the business method of the current bean you can lookup for that new entry:
void myMethod() {
new InitialContext().lookup("java:comp/env/MyInterface")
}
You can locate the bean's ENC by "java:comp/env/" JNDI name.
As you can see, the name parameter defines the name of the entry in the ENC.
The beanInterface defines the Business Interface of the created Bean. If the Bean has more business interfaces, then you have to define beanName too, so the container could determine wich Bean you would like to create.
You can read about this topic here:http://thegreyblog.blogspot.hu/2010/09/introduction-to-ejb-30-injection-and.html
Could you please tell me how to lookup EJB on Weblogic?
I have following bean:
#Stateless
#EJB(name = "DataAccess", beanInterface = DataAccessLocal.class)
public class DataAccess implements DataAccessLocal {
...
}
I need this bean in other class which is not part of managed content (just simple class), so I guess it should be done like this:
DataAccessLocal dataAccess = DataAccessLocal.class.cast((new InitialContext()).lookup("%SOME_JNDI_NAME%"));
The question is what should be used as %SOME_JNDI_NAME% in case of Weblogic 10.x.x AS?
Any help will be appreciated.
I would update your EJB class to look like this:
#Stateless(name="DataAccessBean", mappedName="ejb/DataAccessBean")
#Remote(DataAccessRemote.class)
#Local(DataAccessLocal.class)
public class DataAccess implements DataAccessLocal, DataAccessRemote {
...
}
Looking up the EJB from a class deployed in the same EAR (using the local interface):
InitialContext ctx = new InitialContext(); //if not in WebLogic container then you need to add URL and credentials.
// use <MAPPED_NAME>
Objet obj = ctx.lookup("java:comp/env/ejb/DataAccessBean");
EJB injection is usually preferred, and you can do it as follows:
#EJB(name="DataAccessBean")
DataAccessLocal myDataAccessBean;
If you are trying to use the EJB remotely then you will need to use the remote interface and the following JNDI name:
DataAccessBean#<package>.DataAccessRemote
I'm developping simple app where one EJB should be injected into another. I'm developping in IDEA Jetbrains IDE. But after i make #EJB annotation in Ejb local statless class my IDE highlight it with error:
EJB '' with component interface 'ApplicationController' not found.
Can anyone tell Why?
Injection of an EJB reference into another EJB can be done using the #EJB annotation. Here is an example taken from Injection of other EJBs Example from the OpenEJB documentation:
The Code
In this example we develop two simple
session stateless beans (DataReader
and DataStore), and show how we can
use the #EJB annotation in one of
these beans to get the reference to
the other session bean
DataStore session bean
Bean
#Stateless
public class DataStoreImpl implements DataStoreLocal, DataStoreRemote{
public String getData() {
return "42";
}
}
Local business interface
#Local
public interface DataStoreLocal {
public String getData();
}
Remote business interface
#Remote
public interface DataStoreRemote {
public String getData();
}
DataReader session bean
Bean
#Stateless
public class DataReaderImpl implements DataReaderLocal, DataReaderRemote {
#EJB private DataStoreRemote dataStoreRemote;
#EJB private DataStoreLocal dataStoreLocal;
public String readDataFromLocalStore() {
return "LOCAL:"+dataStoreLocal.getData();
}
public String readDataFromRemoteStore() {
return "REMOTE:"+dataStoreRemote.getData();
}
}
Note the usage of the #EJB annotation
on the DataStoreRemote and
DataStoreLocal fields. This is the
minimum required for EJB ref
resolution. If you have two beans that
implement the same business
interfaces, you'll want to the
beanName attribute as follows:
#EJB(beanName = "DataStoreImpl")
private DataStoreRemote dataStoreRemote;
#EJB(beanName = "DataStoreImpl")
private DataStoreLocal dataStoreLocal;
Local business interface
#Local
public interface DataReaderLocal {
public String readDataFromLocalStore();
public String readDataFromRemoteStore();
}
(The remote business interface is not
shown for the sake of brevity).
If it doesn't work as expected, maybe show some code.
I believe it's an IntelliJ IDEA bug. This thread solved the problem for me:
adding a EJB Facet (in project structure > modules) helped