Accessing #Local Session Bean from an exposed RESTeasy interface - ejb

What I am trying to do should be very straight forward BUT thus far has been impossible. Can someone tell me how to access a #Local Session Bean from an exposed RESTeasy interface? I have trawled the length and breadth of the internet and all I can find is variations of the same example
I am trying to find out how I can access a session bean in the normal way using RESTeasy. This is what things look like so far:
USING:
EJB 3
RESTeasy 2.1
PUBLISHED EJB INTERFACE:
#Local
#Path("RequestReport")
public interface EReport {
#GET
#Produces({"application/xml"})
#Path("request")
public String requestReport(#QueryParam("reportId") #DefaultValue("") String reportId,
#QueryParam("reportName") #DefaultValue("") String reportName,
#QueryParam("reportType") #DefaultValue("") String reportType);
}
}
BEAN 1:
#Stateless
public class EReportRequest implements EReport {
#EJB
private ReplyStringLocal replyString; // THIS IS WHERE THE PROBLEM LIES.
public String requestReport(#QueryParam("reportId") #DefaultValue("") String reportId,
#QueryParam("reportName") #DefaultValue("") String reportName,
#QueryParam("reportType") #DefaultValue("") String reportType) {
return replyString.getReply(reportId, reportName, reportType);
}
}
UNPUBLISHED EJB INTERFACE:
#Local
public interface ReplyStringLocal {
public String getReply(String reportId, String reportName, String reportType);
}
BEAN 2:
#Stateless
public class ReplyString implements ReplyStringLocal {
public String getReply(String reportId, String reportName, String reportType) {
return "<response><reportId>" + reportId + "</reportId><reportName>" + reportName +
"</reportName><reportType>" + reportType + "</reportType></response>";
}
}
This example is super simplified for the purposes of demonstrating my issue. Thanks in advance for any help.

For: JBoss 5, RESTeasy 2.1 and EJB 3.
Ok so I finally got the full story on EJBs with RESTeasy. So here it is:
A. You can publish a Session bean with a RESTful interface by giving it RESTeasy path annotation and EJB Session bean annotaion.
INTERFACE:
#Local
#Path("MessageMaker")
public interface MessageMakerLocal {
#GET
#Produces({"application/xml"})
#Path("getMessage")
public String getMessage(#QueryParam("message") #DefaultValue("") String message);
}
IMPLEMENTATION:
#Stateless
public class MessageMakerImpl implements MessageMakerLocal {
public String getMessage(#QueryParam("message") #DefaultValue("") String message) {
return "Your Message: " + message;
}
}
.
B. You cannot use #EJB annotation in RESTeasy so using a #Local Session bean reference from a published POJO or published EJB is out of the question. So the example provided in the original post is not valid.
.
C. To access a Session Bean from published POJOs or published Session Bean, you can use the #Remote interface annotation and JAR your Bean classes. When you are building your EAR file add the JAR to the root of your EAR and add a reference to it in your META-INF/application.xml file.
INTERFACE:
#Remote
public interface MessageMakerRemote {
public String getMessage(#QueryParam("message") #DefaultValue("") String message);
}
}
IMPLEMENTATION:
#Stateless
#RemoteBinding(jndiBinding = "MessageMakerRemote")
public class MessageMakerImpl implements MessageMakerRemote {
public String getMessage(String message) {
return "Your Message: " + message;
}
}
In Application.xml:
<module>
<java>MessageMaker.jar</java>
</module>
You can then make reference to it using a JNDI remote call to your jar:
#Local
#Path("Message")
public class Message {
#GET
#Path("requestMessage")
public String requestMessage(#QueryParam("msg") #DefaultValue("") String msg){
// I use a custom JNDI remote call handler class so my call to the JARed beans looks like this:
return JNDIRemote.getRemote(com.message.ejb3.MessageMakerRemote.class).getMessage(msg);
}
}
I am hoping that later versions of RESTeasy will provide better integration of EJBs.

Related

Why do I get an "Ambiguous dependencies for interface" Exception when I'm already uses the #Produces annotation?

I'm using two Messaging Oriented Middleware in my project. RabbitMQ and Apache Kafka. I have an consumer interface IConsume which are implemented by ConsumerRabbitMQ and ConsumerKafka. At startup going through some conditions I use the #Produces annotation to choose an implementation for the Interface Bean that I will inject, but it gives me this error.
Exception 1:
org.jboss.weld.exceptions.DeploymentException: WELD-001409: Ambiguous dependencies for type IConsume with qualifiers #Default
at injection point [BackedAnnotatedField] #Inject private com.mycompany.chatapp.startup.RunConsumers.ct
at com.mycompany.chatapp.startup.RunConsumers.ct(RunConsumers.java:0)
Possible dependencies:
- Session bean [class com.mycompany.chatapp.messagegateway.ConsumerRabbitMQ with qualifiers [#Any #Default]; local interfaces are [IConsume],
- Producer Method [IConsume] with qualifiers [#Any #Default] declared as [[BackedAnnotatedMethod] #Produces public com.mycompany.chatapp.startup.MOMConfigBean.produceIConsume()],
- Session bean [class com.mycompany.chatapp.messagegateway.ConsumerKafka with qualifiers [#Any #Default]; local interfaces are [IConsume]
#Default and #Alternative works, but I want it to choose by checking which of the Middleware is running.
The lookup works, I also tried beanName. I think the problem is with the #Produces, but I can't find to seem what.
import javax.enterprise.inject.Produces;
#Singleton
#Startup
public class MOMConfigBean {
private String mom;
#PostConstruct
public void init() {
mom = "Kafka";
}
#EJB(lookup = "java:global/Chatapp/ConsumerKafka!com.mycompany.chatapp.messagegateway.IConsume")
IConsume kafkaConsumer;
#EJB(lookup = "java:global/Chatapp/ConsumerRabbitMQ!com.mycompany.chatapp.messagegateway.IConsume")
IConsume rabbitConsumer;
#Produces
public IConsume produceIConsume() {
if ("Kafka".equals(mom)) {
return kafkaConsumer;
} else {
return rabbitConsumer;
}
}
public interface IConsume {
// some code
}
#Stateless
public class ConsumerKafka implements IConsume{
// some code
}
#Stateless
public class ConsumerRabbitMQ implements IConsume {
// some code
}
public class runConsumers{
#Inject
private IConsume ct;
}
You have three ambiguous sources of IConsume instances:
a ConsumerKafka EJB
a ConsumerRabbitMQ EJB
an #Produces public IConsume produceIConsume() method.
You need to disambiguate the source of the IConsume instances using a qualifier.
This qualifier would look something like:
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import javax.inject.Qualifier;
#Qualifier
#Retention(RUNTIME)
#Target({METHOD, FIELD})
public #interface ConditionalMom {
}
Then qualify the producer:
#Produces
#ConditionalMom
public IConsume produceIConsume() {
if ("Kafka".equals(mom)) {
return kafkaConsumer;
} else {
return rabbitConsumer;
}
}
and the injection site:
public class runConsumers{
#Inject
#ConditionalMom
private IConsume ct;
}
Now you have a single source of #ConditionalMom IConsume instances so it is no longer ambiguous.
You will find that you will be using qualifiers all over the place as you start to further exploit CDI features.

Can two stateless EJB's share the same request scoped entity manager?

I've decided to use a request scoped container managed EntityManager and I've created a producer for that reason:
#RequestScoped
public class EntityManagerProducer {
#PersistenceContext(unitName = "PU", type = PersistenceContextType.TRANSACTION)
private EntityManager entityManager;
#Produces
public EntityManager getEntityManager() {
return entityManager;
}
}
I've got two EJB's that expose a remote client view:
#Remote
public interface EJB1 {
void createPerson(int id, String firstName, String lastName);
}
#Remote
public interface EJB2 {
void containsEntity(Person person);
}
#Stateless
public class EJB1Impl implements EJB1 {
#Inject
private EntityManager entityManager;
#EJB
private EJB2 ejb2;
#Override
public void createPerson(final int id, final String firstName, final String lastName) {
Person person = new Person();
person.setId(id);
person.setFirstName(firstName);
person.setLastName(lastName);
entityManager.persist(person);
System.out.println("EJB1Impl: persistence context contains entity: " + entityManager.contains(person));
ejb2.containsEntity(person);
}
}
#Stateless
public class EJB2Impl implements EJB2 {
#Inject
private EntityManager entityManager;
#Override
public void containsEntity(final Person person) {
System.out.println("EJB2Impl: PersistenceContext contains entity: " + entityManager.contains(entity));
person.setLastName("new name");
//entityManager.merge(person);
}
}
The EJB's are deployed on WildFly 10. I access them by a remote client using this tutorial. The current version throws this exception: org.jboss.weld.context.ContextNotActiveException: WELD-001303 No active contexts for scope type javax.enterprise.context.RequestScoped. If I remove the #RequestScoped annotation from the producer, I don't get an exception but the EntityManager injected into the second EJB returns false when asked if it contains the entity and if I want the changes made to the entity (the change of the last name) to have any effect, I have to call entityManager.merge(person), which apparently means that the entity is detached. I'm sure that the second EJB executes withing the same transaction because if I inject an EJBContext and call setRollbackOnly() the transaction started in EJB1 is rolled back and a new person is not inserted into the data base.
The documentation of javax.enterprise.context.RequestScoped says that the request scope is active during any remote method invocation of any EJB, so what gives? How can I share an EntityManager during a request across multiple EJB's

Cometd with Spring-MVC for personalized chatting

I am working in a Spring-MVC application and I would like to include personalized chat as a feature in it. After some research I found out Cometd to be a suitable option. After going through the documentation and forever repeating samples, I have a little bit of setup which I have done. I need some help to integrate a personalized chat service in the spring-mvc app, and enabling private chat when user pushes chat button.
So basically, I found out, "/service/chat" can be used for private chat, so I have a class for that, and to use private chat, I must have a mapping of userid<-->sessionId, but I cannot find examples anywhere how to do it. I am posting some of the code I have, kindly let me know what is remaining to do, and if possible, some resources, samples for that.
Controller code:
#Controller
#Singleton
public class MessageController {
private MessageService messageService;
#Autowired(required = true)
#Qualifier(value ="messageService")
public void setMessageService(MessageService messageService){this.messageService=messageService;}
#RequestMapping(value = "/startchatting", produces = "application/text")
#ResponseBody
public String startChattingService(){
return "OK";
}
#RequestMapping(value = "/stopchatting",produces = "application/text")
#ResponseBody
public String stopChatting(){
return "OK";
}
}
Private Message Service :
#Service
public class PrivateMessageService {
#Session
private ServerSession session;
#Listener("/service/private")
public void handlePrivateMessage(ServerSession sender, ServerMessage message){
String userId = (String) message.get("targetUserId");
//Mapping code necessary to map userids to session-id's.
//ServerSession recipient = findServerSessionFromUserId(userId);
//recipient.deliver(session,message.getChannel(),message.getData(),null);
}
}
CometConfigurer :
#Component
#Singleton
public class CometConfigurer {
private BayeuxServer bayeuxServer;
private ServerAnnotationProcessor processor;
#Inject
public void setBayeuxServer(BayeuxServer bayeuxServer){this.bayeuxServer = bayeuxServer;}
#PostConstruct
public void init() {this.processor= new ServerAnnotationProcessor(bayeuxServer);}
public Object postProcessBeforeInitialization(Object bean, String name) throws BeansException {
System.out.println("Configuring service " + name);
processor.processDependencies(bean);
processor.processConfigurations(bean);
processor.processCallbacks(bean);
return bean;
}
public Object postProcessAfterInitialization(Object bean, String name) throws BeansException {
return bean;
}
public void postProcessBeforeDestruction(Object bean, String name) throws BeansException {
processor.deprocessCallbacks(bean);
}
#Bean(initMethod = "start", destroyMethod = "stop")
public BayeuxServer bayeuxServer() {
BayeuxServerImpl bean = new BayeuxServerImpl();
// bean.setOption(BayeuxServerImpl.LOG_LEVEL, "3");
return bean;
}
public void setServletContext(ServletContext servletContext) {
servletContext.setAttribute(BayeuxServer.ATTRIBUTE, bayeuxServer);
}
}
Cometd beans :
<beans:bean id="bayeuxServer" class="org.cometd.server.BayeuxServerImpl" init-method="start" destroy-method="stop"/>
I have directly included the JSP files which have cometd configuration and setup from https://github.com/fredang/cometd-spring-example, and modified them to serve my needs. Kindly let me know what else is remaining, all suggestions are welcome, I am unable to find any examples for same task on net, which are detailed, and have more code then explanation. Thank you.
Using Spring 4.x's new WebSocket feature would definitely work; moreover, this new module ships with lots of very interesting features for your use case:
STOMP protocol support
messaging abstractions
session management
pub/sub mechanisms
etc
You can check this nice chat application that demonstrates all those features.

'Bean does not have a public constructor that does not take parameters' error despite clearly having one?

I have an EmailService EJB that has a very simple 'send_email' method. I'm receving the error in the title despite clearly having a public constructor that does not take parameters. Below is the exact error and the class code. This is very confusing.
Error:
[ERROR ] CNTR5007E: The websphere.jaxrs.service.EmailService bean
class for the WebApiConsole#WebApiConsole.war#EmailService bean does
not have a public constructor that does not take parameters.
See here for error details (not much to see): http://pic.dhe.ibm.com/infocenter/wxdinfo/v6r1/index.jsp?topic=%2Fcom.ibm.websphere.messages.doc%2Fcom.ibm.ejs.container.container.html
Code:
package websphere.jaxrs.service;
import javax.ejb.EJB;
import javax.ejb.LocalBean;
import javax.ejb.Stateless;
import org.apache.commons.mail.Email;
import org.apache.commons.mail.EmailException;
import org.apache.commons.mail.SimpleEmail;
import websphere.jaxrs.helpers.ContextFinder;
/**
* This class provides an interface for emailing messages. It uses server environment variables to configure SMTP server and port and authentication.
*
* #author me
*
*/
#Stateless
#LocalBean
public class EmailService {
#EJB ContextFinder ctf;
private static final String EMAIL_PASSWORD_JNDI_NAME = "EMAIL_PASSWORD";
private static final String EMAIL_USERNAME_JNDI_NAME = "EMAIL_USERNAME";
private static final String SMTP_SERVER_JNDI_NAME = "SMTP_SERVER";
private static final String SMTP_PORT_JNDI_NAME = "SMTP_PORT";
private String username;
private String password;
private String server;
private Integer port;
public EmailService() {
username = (String) ctf.lookup(EMAIL_USERNAME_JNDI_NAME);
password = (String) ctf.lookup(EMAIL_PASSWORD_JNDI_NAME);
server = (String) ctf.lookup(SMTP_SERVER_JNDI_NAME);
port = (Integer) ctf.lookup(SMTP_PORT_JNDI_NAME);
}
/**
* Sends an email to a specific user.
*
* #param sendTo
* #param subject
* #param message
*/
public void sendMail(String sendTo, String subject, String message) {
try {
Email email = new SimpleEmail();
email.setHostName(server);
email.setSmtpPort(port);
email.setAuthentication(username, password);
email.setSSLOnConnect(true);
email.setFrom(username);
email.setSubject(subject);
email.setMsg(message);
email.addTo(sendTo);
email.send();
} catch (EmailException e) {
System.err.println("Failed to email");
e.printStackTrace();
}
}
}
I'm inclined to think this is a bug. I might be wrong but but everything is pretty self-contained in the class above (no other configurations that I know of required) and I keep getting this error. I tried re-building the project.
[EDIT] I did some experimentation and very specifically, removing sendMail() causes it to be error free.
The injection has not yet happened.
In short:
container calls the constructor
then the injected fields get injected
then the postconstruct stuff happens
Thus extract the lookups, as suggested, into a postconstruct method
Why you don't remove initialization from constructor into a #PostConstruct method?
#Stateless
public class EmailService {
#EJB ContextFinder ctf;
private static final String EMAIL_PASSWORD_JNDI_NAME = "EMAIL_PASSWORD";
private static final String EMAIL_USERNAME_JNDI_NAME = "EMAIL_USERNAME";
private static final String SMTP_SERVER_JNDI_NAME = "SMTP_SERVER";
private static final String SMTP_PORT_JNDI_NAME = "SMTP_PORT";
private String username;
private String password;
private String server;
private Integer port;
#PostConstruct
public void init() {
username = (String) ctf.lookup(EMAIL_USERNAME_JNDI_NAME);
password = (String) ctf.lookup(EMAIL_PASSWORD_JNDI_NAME);
server = (String) ctf.lookup(SMTP_SERVER_JNDI_NAME);
port = (Integer) ctf.lookup(SMTP_PORT_JNDI_NAME);
}
public void sendMail(String sendTo, String subject, String message) {
// send mail
}
}
Just in case you are arriving here several years late as I did, it also turns out this error message may be misleading. I got the error,
[ERROR ] CNTR4006E: The EVENT_MANAGER_BEAN enterprise bean in the MySpecialEJB.jar module of the some-ear-DEVELOPMENT-SNAPSHOT application failed to start. Exception: com.ibm.ejs.container.EJBConfigurationException: EJB class com.notarealcompany.event.EventManagerEJB must have a public constructor that takes no parameters : some-ear-DEVELOPMENT-SNAPSHOT#MySpecialEJB.jar#EVENT_MANAGER_BEAN
at com.ibm.ws.ejbcontainer.jitdeploy.EJBUtils.validateEjbClass(EJBUtils.java:346)
at [internal classes]
Caused by: java.lang.NoClassDefFoundError: commonj/work/Work
at java.lang.Class.getDeclaredConstructors0(Native Method)
at java.lang.Class.privateGetDeclaredConstructors(Class.java:2671)
at java.lang.Class.getConstructor0(Class.java:3075)
at java.lang.Class.getConstructor(Class.java:1825)
at com.ibm.ws.ejbcontainer.jitdeploy.EJBUtils.validateEjbClass(EJBUtils.java:337)
... 1 more
I don't think the error message is trying to say that such a method does not exist. Rather it's saying that the loader couldn't find one that does exist that can be successfully loaded. The actual error (caused by) is the NoClassDefFoundError on the class commonj.work.Work. It turns out that there is also a warning earlier in the log:
[WARNING ] CWNEN0047W: Resource annotations on the fields of the com.notarealcompany.app.ApplicationEJB class will be ignored. The annotations could not be obtained because of the exception : java.lang.NoClassDefFoundError: Lcommonj/work/WorkManager;
So the Application EJB didn't get its resources injected either because commonj.work.WorkManager could not be found. Why that's a warning and not an error is a good question. I'm sure the app will do just fine without those resources, right? Basically, the jar with the APIs for commonj.work just didn't get included with the deployment. In our case, that's because the old application server provided it whereas the new application server did not. A little adjustment to the POM file and we got past that issue. Very misleading error message in my opinion.
I would prefer to annotate your constructor with #Inject instead:
#Stateless
#LocalBean
public class EmailService {
....
#Deprecated
public EmailService(){}
#Inject
public EmailService(ContextFinder ctf) {
username = (String) ctf.lookup(EMAIL_USERNAME_JNDI_NAME);
password = (String) ctf.lookup(EMAIL_PASSWORD_JNDI_NAME);
server = (String) ctf.lookup(SMTP_SERVER_JNDI_NAME);
port = (Integer) ctf.lookup(SMTP_PORT_JNDI_NAME);
}
...
}
>> Using #PostConstruct is not exactly the same as you had. init() method will not work outside the container like in JUnit test.
You still need to provide the default not parametrized constructor (part of the The EJB 3.1 spec, section 4.9.2 Bean Classes).

CDI + EJB 3 + EJB Transaction

I need to audit invocations of ejb beans. Saying audit I mean write informations such as current logged user, method name, additional description to a database. I decided to do it by use of CDI decorator:
#Decorator
public class AccountServiceBeanDecorator implements AccountService {
#Inject
#Delegate
#Any
AccountService accountService;
#EJB
private AuditService auditService;
#Override
public Account createAccount(Account account) {
auditService.saveAudit("Method: createAccount", currentUser, "Creating account by admin");
return accountService.createAccount(account);
}
}
and the decorated class:
#Stateless
public class AccountServiceBean implements AccountService {
#Override
public Account createAccount(Account account) {
...
}
}
Now if I call AccountService from another ejb stateless bean, what will happen with transaction?:
#Stateless
public ApplicationFacadeBean implements ApplicationFacade {
#EJB
private AccountService accountService;
#Override
public Account createAccount(Account account) {
return accountService.createAccount(account);
}
}
I wanted to log transaction status in decorator (AccountServiceBeanDecorator) and decorated class (AccountServiceBean), so I injected TransactionSynchronizationRegistry as a resource in both classes:
#Decorator
public class AccountServiceBeanDecorator implements AccountService {
#Inject
#Delegate
#Any
AccountService accountService;
#EJB
private AuditService auditService;
#Resource
private TransactionSynchronizationRegistry reg;
#Override
public Account createAccount(Account account) {
log.info("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%");
log.info("tx ({}): {}", new Object[] {reg.getTransactionStatus(), reg.getTransactionKey()});
log.info("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%");
auditService.saveAudit("Method: createAccount", currentUser, "Creating account by admin");
return accountService.createAccount(account);
}
}
and
#Stateless
public class AccountServiceBean implements AccountService {
#Resource
private TransactionSynchronizationRegistry reg;
#Override
public Account createAccount(Account account) {
log.info("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%");
log.info("tx ({}): {}", new Object[] {reg.getTransactionStatus(), reg.getTransactionKey()});
log.info("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%");
...
}
}
I received strange behavior:
log from decorator
tx (0): JavaEETransactionImpl: txId=6 nonXAResource=null jtsTx=null localTxStatus=0 syncs=[com.sun.ejb.containers.ContainerSynchronization#68fb15d0]]]
NullPointerException on second log (reg is null).
Can anybody explain it to me? Wheter AccountServiceBean class is called within the same transaction as ApplicationFacade?
Thank you
first: i would not mixing ejbs with cdi interceptors. ejbs has it on interceptor implementations.
second: interceptors are executed in the same transaction as the ejb where the interceptor is around.
possible solution:
create a correct ejb interceptor
put the interceptor around the method / class
create a second ejb (MyLoggerBean) with a method like this logToDatabase(String message) and annotate this method with #TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
inside the interceptor create a class member like this: #EJB private MyLoggerBean loggerBean
inside your #AroundInvoke annotated method you could call loggerBean. logToDatabase(...)
this would create a new transaction from inside the current transaction of the ejb where the interceptor is around
--> i know my english is not very good. but i hope that you understand what i think should work. if i have the time, i make e example on github...
Hmm... what container are you using? Generally I wouldn't suspect a CDI decorator to work on an EJB... I can't think of anything in the JEE spec that I've encountered that would give evidence either way.
Faced with your problem though, I did this with an interceptor, not a decorator. These are supported by the EJB spec... Anyway, here's my code, you would need to grab the variables from the context in your case:
import java.lang.reflect.Method;
import javax.inject.Inject;
import javax.interceptor.AroundInvoke;
import javax.interceptor.Interceptor;
import javax.interceptor.InvocationContext;
public class InvocationCountInterceptor {
#Inject
private InvocationCounter counter;
#AroundInvoke
public Object intercept(InvocationContext ctx) throws Exception {
Object returnValue = ctx.proceed();
Class<? extends Object> className = ctx.getTarget().getClass();
Method methodName = ctx.getMethod();
counter.incrementCounter(className, methodName);
return returnValue;
}
}
Then whatever EJB or EJB Method you want to audit, I just added this: #Interceptors(InvocationCountInterceptor.class)

Resources