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.
Related
I do have a EJB ActionService which I can inject into other EJBs, that is working fine.
Now I created another EJB:
#Stateless
public class ActionsPerDateDataSet extends ScriptedDataSetEventAdapter {
#EJB
ActionService actionService;
#Override
public void open(IDataSetInstance dataSet) {
actionService.foo() // However actionService is null here!
}
}
Where the ScriptedDataSetEventAdapter comes from another framework (BIRT).
However now my actionService is always null. I can not understand why
You should introduce the lib as ejbModule in ear file , so that container search the jar file and deploy it and inject it whenever it needs
ActionService has an interface with the #local annotation or if this is a class it has to have the annotation #LocalBean.
(this to be able to access the instance of it at runtime)
In case it is an interface and if it has multiple implementations you will have to reference the implementation you need using #EJB (beanName = "nameOfImplementation") in case it is a class where #LocalBean is used to use #EJB (name = "nameEjb")
Interface with #Local
Class with #LocalBean
In the aggregation class
Interface with multiple implementations #EJB(beanName="nameOfImplementation")
Class #EJB(name="nameEjb")
note: implements an interface for ActionService with #Local and test
note: add trace the console log to know if the class is being initialized as an ejb:ActionService
Did you try using CDI? I think it is worth a shot. You need to place an empty beans.xml inside your meta-inf folder and change #EJB to #Inject. But the only way this could work is if you have the external lib and your war/jar file in the same deployment unit.
if this does not work you will need to use JNDI for looking up your bean:
http://docs.oracle.com/javaee/6/tutorial/doc/gipjf.html
It is possible that the class ScriptedDataSetEventAdapter
can not be initialized in the EJB Container (First part of the cycle)
and as the initialization is not correct, the dependency injection (#EJB and #Inject) is not made.
What you could do is change the Design of your EJB and instead it's extends "ScriptedDataSetEventAdapter"
change it to a composition.
#Stateless
public class ActionsPerDateDataSet {
ScriptedDataSetEventAdapter scriptedDataSetEventAdapter;
#EJB
ActionService actionService;
#PostConstruct
public void init (){
try {
scriptedDataSetEventAdapter = new ScriptedDataSetEventAdapter();
} catch( AppException e){
}
}
#Override
public void open(IDataSetInstance dataSet) {
actionService.foo() // However actionService is null here!
}
}
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
I'm trying to figure out how to setup a Service/Dao layer in my application. I've found a few dozen resources all with different ways on how to do it and decided to follow the model found here: How should EntityManager be used in a nicely decoupled service layer and data access layer?
I can't figure out what I'm missing that's causing this NPE.
Usage:
#Path("/helloworld")
public class MyController {
#GET
#Produces(MediaType.TEXT_PLAIN)
public String TestRequest() {
Workflow workflow = new Workflow();
workflow.setName("test");
WorkflowService workflowService = new WorkflowService();
workflowService.save(workflow);
return "Workflow ID:";
}
}
My Dao:
#Stateless
public class WorkflowDao {
#PersistenceContext(unitName = "unit")
private EntityManager entityManager;
public int save(Workflow workflow) {
entityManager.persist(workflow);
return workflow.getId();
}
}
My Service:
#Stateless
public class WorkflowService {
#EJB
WorkflowDao workflowDao;
public int save(Workflow workflow) {
int id = workflowDao.save(workflow); //throws NullPointerException because workflowDao is null
return id;
}
}
This is my first time setting up a Java project (only have worked on 1 before and it used Spring) so please keep that in mind if this looks horribly wrong.
WorkflowDao is not an EJB, it's a POJO with a#Stateless annotation. So naturally, injecting it with #EJB fails, creating a null workflowDao attribute and eventually producing a NullPointerException.
To make WorkflowDao into a full-fledged EJB, besides having a #Stateless or #Stateful annotation it needs to implement a local, remote or both interfaces, and those interfaces must be annotated respectively with #Local and #Remote. Please refer to the tutorial for details.
Also, quite possibly (this can be application server-dependent) you'll have to register the EJB in the application's xml descriptor files - for instance, in web.xml's <ejb-local-ref> element.
As a side note - it's not a good idea to use an EJB as a DAO, an EJB is typically used for implementing business logic (true, persist/merge operations can be called from here) but the actual persistence layer nowadays is implemented using JPA. In other words, WorkflowService should be the EJB service, there's no need to inject an EJB into it, and there's no need for a separate DAO layer - JPA entities fulfill this role.
If you instantiate your WorkflowService manually, the container wont perform any injection, since your WorkflowService is not managed by the Container.
I suggest you:
Annotate your Jax-RS Resource #Stateless
Inject your WorkfloService via #EJB as a member
Implementing a Local or Remote Interface is not necessary anymore
#Path("workflows")
#Stateless
public class WorkFlowResource{
#EJB
WorkflowService workflowService;
#GET
#Produces(MediaType.TEXT_PLAIN)
public String TestRequest() {
Workflow workflow = new Workflow();
workflow.setName("test");
workflowService.save(workflow);
return "Workflow ID:";
}
}
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>
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