In the oficial Java EE documentation https://docs.oracle.com/javaee/6/tutorial/doc/gkkqg.html says "Session beans can implement asynchronous methods".
Following this tutorial I'm not able to execute a method asynchronously.
It actually Works, but like any other synchronous method. (It doesn´t start any other thread).
#ManagedBean(name = "inicioSSCCBean")
#SessionScoped
public class InicioSSCCBean implements Serializable {
...
#Asynchronous
public Future<String> sendMessage() {
String status;
try {
// Call to SAP server...
} catch (MessagingException ex) {
// Error handler
}
return new AsyncResult<String>(status);
}
public void otherMethod() {
String result = sendMessage().get(); // The result is what I expect
System.out.println(result);
}
...
}
Do anyone know how to implement an async call from a session bean?
The main goal is to make a call to a SAP service, and get the results asynchronously.
I´m using JSF 2.2, PrimeFaces.
Thanks in advance.
You misunderstood the Java EE tutorial. The term "Session bean" refers to enterprise session beans (EJBs), not to session scoped managed beans (JSF/CDI beans).
The #javax.ejb.Asynchronous annotation, as its package already hints, works only in EJBs. EJBs are recognizable by having a #javax.ejb.Xxx annotation on the class, such as #Stateless or #Stateful.
Below is the correct kickoff example:
#Stateless
public class YourService {
#Asynchronous
public void asyncDoSomething() {
// ...
}
}
#ManagedBean
public class YourBean {
#EJB
private YourService yourService;
public void submit() {
yourService.asyncDoSomething();
}
}
See also:
When is it necessary or convenient to use Spring or EJB3 or all of them together?
Is it safe to start a new thread in a JSF managed bean?
JSF Controller, Service and DAO
How can server push asynchronous changes to a HTML page created by JSF?
Aside from the fact that you're trying to use the wrong kind of bean, you'll have to understand how Future works.
When you call Future.get(), the thread will block until the Future has a result. Therefore sendMessage().get() will act exactly like a synchronous call.
However if you call sendMessage() and then perform other tasks before calling get(), it will be performed asynchronously. As you realize an asynchronous call is only useful when you don't need the result right away, making it less useful than it seems in most cases.
Related
I have a class
#RequestScoped
public class AFactory {
private final HttpServletRequest request;
protected AFactory () {
this.request = null;
}
#Inject
public AFactory (HttpServletRequest request) {
this.request = request;
}
#Produces
public A getA() {
int random = ...;
A a = new A(request);
a.setRandom(random);
return a;
}
}
I understand since I am doing new A(), I return the real instance.
Is this the expected way to use producers?
Is there a way to return the proxied instance?
CDI installs proxies for beans of all scopes except for the #Dependent pseudo scope. A #Produces annotated method (short producer) is called by CDI whenever it needs to get a (new) instance of a bean. This instance is then put into some pool for the corresponding scope.
The proxy will always return the bean-instance from the pool. Try adding some debug message to the producer method to see how often it will be called. A producer for an #ApplicationScoped bean should be called only once, and a #RequestScoped producer should be called once per request.
In the examples above no scope is given to the producer method (the scope of the factory class is not used for the producer method), so the default scope (#Dependent) will be used. And this means (as no proxy is used for this scope), a new instance will be injected every time #Inject A is found by CDI. See also this Question and Answer and the referenced documentation for more details.
So to your concrete questions:
The observed behavior is expected
Proxies will only be used if scope is not #Dependet which is the default.
I don't really have a good knowledge of EJB components, CDI and stuff. I'm having a lot of trouble with asynchronous EJB. I tried some tutorials but I couldn't understand or make them work adapting to the code I'm maintaining here.
I would like my EJB to return an answer to my Managed Bean when it finishes executing. ManagedBean should listen to it and then do other stuff (in my case I need to update view components, which could be done with a redirect), but it can't block navigation and other actions from user.
So far, this is what I got:
1 - this is my managed Bean:
#Named
#ViewScoped
public class MyManagedBean {
#Inject
private ExecutionService executionService;
public void executeAsyncTask(Suite suite) {
try {
Future<Boolean> result = executionService.executeStuff(suite);
if (result.isDone()) {
// send a redirect if he is in the same page of the button that calls this method. It doesn't work, it's result is always false because execution is not finished and method moves on with his life
}
}
2 - And this is my EJB:
#Singleton
#ConcurrencyManagement(ConcurrencyManagementType.BEAN)
public class ExecutionService extends BaseBean implements Serializable {
//
#Inject private ExecutionDAO execDao;
//
#Asynchronous
public Future<Boolean> executeStuff(Suite suite) throws BusinessException {
Boolean areStiffExecuted = false;
Process process;
try {
process = Runtime.getRuntime().exec(new String[]{ });
// and other crazy stuff that take some time.
} catch (Exception e) {
// log exception and throw it to managed bean
} finally {
// do some stuff like destroying process
return new AsyncResult<>(true);
}
}
}
Do you guys have any idea of how to get what I want?
Following the links provided by Kukeltje I found stuff about websockets (yeah, I had never heard of them) and went with Omnifaces version since it was already a part of the project. I followed the websocket instructions from Omnifaces showcase.
I also followed BalusC explanation about them and complemented my complete ignorance on this subject in this post about Consumer objects. It looks like I made something that works out of it all!
I would like to rollback transaction not inside EJB but inside JSF managed bean. Inside EJB we can use SessionContext.setRollBackOnly() but what can I use in managed bean ?
#Stateless
#Local(AccountLocal.class)
public class AccountBean implements AccountLocal {
public void test1() throws CustomException(){
...
}
public void test2() throws CustomException(){
...
throw new CustomException();
}
public void test3() throws CustomException(){
...
}
public void all() throws CustomException(){
test1();
test2();
test3();
}
}
In my managed bean :
#SessionScoped
public class LoginBean implements Serializable{
public void test(){
try{
accountBean.test1();
accountBean.test2();
accountBean.test3();
}catch(CustomException e){
// WHAT HERE TO ROLLBACK TRANSACTION ?
}
}
}
EDIT : How can I ensure that if one of the test1, test2 or test3 rolls back, others will roll back too ?
I tested this code and accountBean.test1(); is validated even if accountBean.test2(); rolls back.
Could the solution be only to nest this 3 methods inside one EJB method ?
#SessionScoped
public class LoginBean implements Serializable{
public void test(){
try{
accountBean.all();
}catch(CustomException e){
...
}
}
}
Transactions are automatically rolled back by the EJB container if an unchecked exception is thrown (note that JPA's PersistenceException is such one). Your CustomException seems to be a checked exception. If changing it to extend RuntimeException as follows
public class CustomException extends RuntimeException {
// ...
}
or creating a new one is not an option, then you need to set the #ApplicationException annotation on the class with the rollback attribute set to true.
E.g.
#ApplicationException(rollback=true)
public class CustomException extends Exception {
// ...
}
Please note that the concrete problem has nothing to do with JSF. The service layer and managing transactions is completely outside the responsibility of JSF. It's the responsibility of EJB instead. JSF should merely act as "view" in this perspective.
See also:
JSF Service Layer
Handling service layer exception in Java EE frontend method
I'm playing the Devil's advocate here, since BalusC's advice that you should not let your backing beans act as services is absolutely true.
But, purely as a technical excersise, it -is- possible to start a JTA transaction in a backing bean and then control start and commit or rollback programmatically.
You can do this by injecting a UserTransaction via #Resource. Prior to calling your EJB methods, call start on this instance, and after the last call either commit or rollback.
Again, this is a purely theoretical answer. In practice, don't do this and let the backing bean call 1 EJB method that calls out to other EJB beans if needed.
I have a navigation managed bean for each user.
and I need it to initialize first before any other bean because a value is required from the bean.
May I know how do I perform that?
I have tried eager="true" but it doesn't work.
any quick and easy solution via faceconfig would be greatly appreciated.
Just perform the desired initialization job in bean's #PostConstruct.
#PostConstruct
public void init() {
// Here.
}
It'll be invoked when the bean is injected/referenced from another bean for the first time.
The eager=true works only on application scoped beans.
From what I see you should reference the other bean. Let's assume a have a utility class that can pull a bean from the context.
Basically ->
//Bean A
public void doSomething()
{
String required = Utility.getBeanB().getRequiredValue();
use(required);
}
...
//Bean B
public String getRequiredValue()
{
return "Hi, I'm a required value";
}
I have several large web apps that have a "Session Bean" that stores stuff like user preferences, shared objects etc... and this method works perfectly. By using a reference to the bean you eliminate the need to chain the initialization. That method will always DEPEND on the method in the other bean, thus guaranteeing the order of initialization.
There's a variety of ways to access the bean but I usually go through the EL route ->
Get JSF managed bean by name in any Servlet related class
Best of luck, I try to stay "functionally pure" when I can--and I hope that get's a laugh considering the language!
Here's some cool hax for ya, in case other solutions aren't working for you due to various circumstances...
Let's say I have a class Alpha that I want initialized first:
public class Alpha {
#PostConstruct
public void init() {
}
}
I can put the following method in Alpha:
#ManagedBean(name = "Alpha", eager = true)
public class Alpha {
public static void requireAlpha() {
FacesContext context = FacesContext.getCurrentInstance();
Object alpha = context.getApplication().evaluateExpressionGet(context, "#{Alpha}", Object.class);
System.out.println("Alpha required: " + alpha.toString());
}
#PostConstruct
public void init() {
}
}
Then, in any classes that are initializing too early, simply call:
Alpha.requireAlpha();
// ...
// (Code that requires Alpha to be initialized first.)
And if there's a ChildAlpha class that extends Alpha that you really want to be initialized (rather than the parent), make sure to use "ChildAlpha" instead, in both the name="" and the EL Expression ("#{}").
See here for more infos: Get JSF managed bean by name in any Servlet related class
This question already has an answer here:
Using special auto start servlet to initialize on startup and share application data
(1 answer)
Closed 7 years ago.
For university project I am developing a webapplication with JSF. My excercise is to do the frontend. A fellow studend is supposed to do backend stuff. Both parts are designed to be seerate applications. Both communicate through RMI. I want to open the connection once at deployment.
I am at the point to settle up the connection now. I tried to do that with a #ApplicationScoped ManagedBean:
//Constructor of ApplicationScoped ManagedBean
public Communication() {
this.connect();
}
Is that way possible? I tried it but the managedBean seems not to be called..
Can you advice a Best Practice?
#Brian: Unfortunately I don't use EJB at all -.-
#BalusC's pot:
I created a communicationbean:
#ManagedBean(name="communication")
#ApplicationScoped
public class Communication {
public static FrontendCommInterface server;
public Communication() {
this.connect();
}
Then I created the LoginBean:
#ManagedBean
#ViewScoped
public class Login {
#ManagedProperty(value="#{communication}")
private Communication communicationBean;
public FrontendCommInterface server;
private String username;
private String password;
public Login() {
server = communicationBean.getConnection();
}
public String login(){
HttpSession session = (HttpSession) FacesContext.getCurrentInstance().getExternalContext().getSession(true);
String sessionId = session.getId();
try {
server.login(getUsername(), getPassword(), sessionId);
return "start.xhtml";
} catch (RemoteException e) {
e.printStackTrace();
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR,"Anmeldung nicht erfolgreich: ", getUsername()+", "+getPassword()+", "+sessionId));
return "login.xhtml";
}
}
But unfortunately it throws exceptions:
com.sun.faces.mgbean.ManagedBeanCreationException: Klasse org.dhbw.stg.wwi2008c.mopro.ui.managedBeans.Login can not be instanciated.
java.lang.NullPointerException
org.dhbw.stg.wwi2008c.mopro.ui.managedBeans.Login.<init>(Login.java:28)
After debuging I found out that my ManagedProperty is Null ! It hasn't been created! How to do that? I thought referencing via managedproperty would create it -.-
The managed bean is only auto-created whenever it's been referenced by EL #{managedBeanName}, which can happen by either accessing as-is in view, or by being injected as managed property of another bean, or being manually EL-resolved by e.g. Application#evaluateExpressionGet().
In your particular case, you actually want to intialize some stuff during webapp's startup. You rather want to use ServletContextListener for this.
#WebListener
public class Config implements ServletContextListener {
public void contextInitialized(ServletContextEvent event) {
// Do stuff during webapp's startup.
}
public void contextDestroyed(ServletContextEvent event) {
// Do stuff during webapp's shutdown.
}
}
You could even pre-create an application scoped managed bean there whenever necessary (if your intent is to be able to access it from other beans by #ManagedProperty).
public void contextInitialized(ServletContextEvent event) {
event.getServletContext().setAttribute("bean", new Bean());
}
JSF stores application scoped beans as an attribute of the ServletContext and JSF won't auto-create another one when one is already present, so the one and the same as created by the above code example will be used by JSF as well.
If you can use EJB 3.1 lite {1} in your web app, then you can use a Singleton Session Bean, annotated with #Startup, and a #PostConstruct method. I have one that looks like:
#Singleton
#Startup
public class CachePrimer {
#PostConstruct
public void loadOpenRequests() {
...
}
}
{1}: EJB 3.1 lite is included in the Web Profile of JavEE 6, and is provided by web profile servers like Glassfish, JBoss 6, and Resin. When using such a web profile server, you simply include your EJBs in your .war file, no additional work is required.