How to programatically open a dialog box in primefaces without user click? - jsf

I am working on an exam management session using Java EE 7 and Primefaces. Tests are timed and once the time is over, I want to display a dialog box automatically(i.e. without user clicking any button). I successfully used Primefaces Dialog framework on the click of a button, but can't do it automatically.
For timing the test I am using TimerService, which calls a stateless EJB's method on timeout. I have a session scoped CDI bean which creates the timer.
This is the post construct method of the CDI bean.
public void init() {
studentAnswers = new ArrayList<StudentAnswer>();
examSections=ePaper.getSections();
examTimerBean.createExamPaperDuration(10,RequestContext.getCurrentInstance());
}
The stateless EJB is as follows -
#Stateless
public class ExamTimerBean {
#Resource TimerService timerService;
RequestContext reqCtx;
public void createExamPaperDuration(int duration,RequestContext reqCtx){
this.reqCtx=reqCtx;
timerService.createTimer(duration*1000, null);
}
#Timeout
public void examTimeExpired(){// throws IOException{
try{
reqCtx.openDialog("thanks");
}
catch(EJBException e){
System.out.println(e.getMessage());
}
}
}
I am passing the request context to the EJB, but the dialogBox doesn't open. Kindly help.

Related

How to display Primefaces dialog (dialog framework) as part of CustomExceptionHandler

I have a CustomExceptionHandler from which I want to display a modal dialog to get some information from the user, using Primefaces dialog framework.
The CustomExceptionHandler#handle method is being called as expected when I deliberately cause a Null Pointer exception. And I can display and interact with the dialog's .xhtml page if I directly invoke it from a p:commandButton's actionListener on a xhtml page (both cases with ajax enabled and disabled).
When I invoke a p:commandButton (with ajax disabled to eliminate a possible ajax error from complicating the scenario) on a page that leads to the NPE, both before/after log messages are generated from CustomExceptionHandler#handle, but the dialog page is not displayed. The window flashes like it's being refreshed, however.
From CustomExceptoinHandler.java:
public class CustomExceptionHandler
extends ExceptionHandlerWrapper
{
private ExceptionHandler wrapped;
public CustomExceptionHandler(ExceptionHandler wrapped)
{
this.wrapped = wrapped;
}
#Override
public ExceptionHandler getWrapped()
{
return wrapped;
}
#Override
public void handle()
throws FacesException
{
Iterator<ExceptionQueuedEvent> iterator = getUnhandledExceptionQueuedEvents().iterator();
while (iterator.hasNext()) {
boolean removeError = false;
Throwable t = iterator.next().getContext().getException();
while (t.getCause() != null)
t = t.getCause();
if (t instanceof NullPointerException) {
Logging.devLogger.warn("NPE exception handler before dialog");
Map<String, Object> options = new HashMap<>();
options.put("modal", true);
String page = "/home/dialogs/exceptionDialog.xhtml";
RequestContext.getCurrentInstance().openDialog(page, options, null);
Logging.devLogger.warn("NPE exception handler after dialog");
removeError = true;
} // TBD: other cases and refactoring ....
if (removeError) iterator.remove();
}
getWrapped().handle();
}
}
The RequestContext....openDialog() invocation is the same when handling the exception and when the dialog is directly opened via a commandButton. I've determined that the backing bean for the exception dialog (view scoped) gets constructed when the dialog is opened outside the exception situation, but does not get constructed when opened while handling the exception (#PostConstruct method not called).
How would I change the way the exception is handled so the dialog behaves as expected? I'm using Mojarra 2.2.5, Primefaces 4.0, Glassfish 4.0.
I'm woring with PrimeFaces 5.2, i've solved this problem in other way.
To show the dialog, i've created the error dialog in my template named "errorDialog" and te show it by my CustomExceptionHandler i've used, this command
RequestContext.getCurrentInstance().execute("PF('errorDialog').show();");
You can't use the command below, becose there is already an exception, so you can rendered the dialog.
RequestContext.getCurrentInstance().openDialog(page, options, null);
Concerning the exception handling by #PostConstruct method, i've used :
#PostConstruct
private void init() {
try {
initController();
} catch (Exception ex) {
logger.error(ex, null);
//Show the error dialog
MyUtil.openErrorDialog();
}
}
Note : I have still a problem, when an exception (Error 500) is occurred and handled by the App Server and not my CustomHandlerClass, i can't show the dialog becose there is already a redirection by the App Server.

Rollback transaction inside managed bean

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.

Adding faces message to redirected page using ExternalContext.redirect()

I am using ExternalContext.redirect(String); method to redirect user to another page:
FacesContext.getCurrentInstance().addMessage(new FacesMessage("Bla bla bla..."));
FacesContext.getCurrentInstance().getExternalContext().getFlash().setKeepMessages(true);
ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext();
ec.redirect(ec.getRequestContextPath() + "/scenario.xhtml");
As Matt Handy mentioned in his answer, I used Flash.setKeepMessages(true); but it does not seem to work with ExternalContext.redirect. (Although it works when I redirect by returning a page name from bean's action method.)
Now how can I add FacesMessage so that it is visible in the redirected (scenario.xhtml) page?
This seems to be a timing problem. This listener method is invoked during the preRenderView event. According to the source code of ELFlash (Mojarra's Flash implementation as returned by ExternalContext#getFlash()) it turns out that it won't set the flash cookie when you're currently sitting in the render response phase and the flash cookie hasn't been set yet for the current request:
Here are the relevant lines from ELFlash:
if (currentPhase.getOrdinal() < PhaseId.RENDER_RESPONSE.getOrdinal()) {
flashInfo = flashManager.getPreviousRequestFlashInfo();
} else {
flashInfo = flashManager.getNextRequestFlashInfo(this, true);
maybeWriteCookie(context, flashManager);
}
The maybeWriteCookie would only set the cookie when the flash cookie needs to be passed through for the second time (i.e. when the redirected page in turn redirects to another page).
This is an unfortunate corner case. This ELFlash logic makes sense, but this isn't what you actually want. Basically you need to add the message during INVOKE_APPLICATION phase instead. There is however no such event as postInvokeAction. With the new JSF 2.2 <f:viewAction> tag it should be possible as it really runs during invoke application phase.
<f:viewAction action="#{bean.onload}" />
As long as you're not on JSF 2.2 yet, you'd need to look for alternate ways. The easiest way would be to create a custom ComponentSystemEvent.
#NamedEvent(shortName="postInvokeAction")
public class PostInvokeActionEvent extends ComponentSystemEvent {
public PostInvokeActionEvent(UIComponent component) {
super(component);
}
}
Now you need somewhere a hook to publish this event. The most sensible place is a PhaseListener listening on after phase of INVOKE_APPLICATION.
public class PostInvokeActionListener implements PhaseListener {
#Override
public PhaseId getPhaseId() {
return PhaseId.INVOKE_APPLICATION;
}
#Override
public void beforePhase(PhaseEvent event) {
// NOOP.
}
#Override
public void afterPhase(PhaseEvent event) {
FacesContext context = FacesContext.getCurrentInstance();
context.getApplication().publishEvent(context, PostInvokeActionEvent.class, context.getViewRoot());
}
}
If you register it as follows in faces-config.xml
<lifecycle>
<phase-listener>com.example.PostInvokeActionListener</phase-listener>
</lifecycle>
then you'll be able to use the new event as follows
<f:event type="postInvokeAction" listener="#{bean.onload}" />
Update this is also available in the JSF utility library OmniFaces, so you don't need to homebrew the one and other. See also the InvokeActionEventListener showcase example.
Use the flash to keep messages over a redirect.
Add these two lines to your code before redirecting:
FacesContext context = FacesContext.getCurrentInstance();
context.getExternalContext().getFlash().setKeepMessages(true);
Note that the there are some issues with Mojarra's flash scope implementation. Keep this in mind if you use it.
Using Matt Handy's example as a reference, I created the method below that worked very well for me.
public static void Message(String message) {
FacesMessage fm = new FacesMessage(FacesMessage.SEVERITY_INFO, mensagem, null);
FacesContext context = FacesContext.getCurrentInstance();
context.getExternalContext().getFlash().setKeepMessages(true);
context.addMessage(null, fm);
}

Invoking action method in the application startup (JSF)

We need to call a action method while invoking the first page of the application. For example, the first page is index.jsp, when we are directly calling this page the action method is not called. To achieve that, we have written another page where it uses java script to click on the button and call the action method, that navigates to the index.jsp.
I feel that there should be proper way in JSF to achieve this task. What is the bet way to do that? I have told the team that we can call the action method in the constructor while loading the page. Is it the correct way? What are the possible solutions?
Just do the job in #PostConstruct method of an application scoped bean which is is eagerly constructed or which is at least bound to the page.
#ManagedBean(eager=true)
#ApplicationScoped
public class Bean {
#PostConstruct
public void init() {
// Here.
}
}
Alternatively, if JSF (read: the FacesContext) has no relevant role in the actual job, you can also use a ServletContextListener.
#WebListener
public class Config implements ServletContextListener {
public void contextInitialized(ServletContextEvent event) {
// Do stuff during webapp startup.
}
public void contextDestroyed(ServletContextEvent event) {
// Do stuff during webapp shutdown.
}
}
If you're not on Servlet 3.0 yet, register it in web.xml as follows.
<listener>
<listener-class>com.example.Config</listener-class>
</listener>
See also:
Using special auto start servlet to initialize on startup and share application data
Using JSF 2.0
If you want to take some action when your application starts (even if is not yet accesed ), you can use a SystemEventListener and subscribe it to PostConstructApplicationEvent.
Example of the listener:
package listeners;
import javax.faces.application.Application;
import javax.faces.event.AbortProcessingException;
import javax.faces.event.ListenerFor;
import javax.faces.event.PostConstructApplicationEvent;
import javax.faces.event.SystemEvent;
import javax.faces.event.SystemEventListener;
public class MySystemListener implements SystemEventListener{
#Override
public void processEvent(SystemEvent event) throws AbortProcessingException {
System.out.println("started");
}
#Override
public boolean isListenerForSource(Object source) {
return source instanceof Application;
}
}
To suscribe you have to include this fragment in the faces-config.xml
<application>
<system-event-listener>
<system-event-listener-class>
listeners.MySystemListener
</system-event-listener-class>
<system-event-class>
javax.faces.event.PostConstructApplicationEvent
</system-event-class>
</system-event-listener>
</application>
And if you want to take the action when the user enters to a specific page, you could use another system event and f:event tag to receive a notification before the page is displayed.
For example:
...
<h:body>
<f:event type="preRenderView" listener="#{bean.action}"/>
<h:form>
<!--components-->
</h:form>
</h:body>
...
Here are more details on using system events: http://andyschwartz.wordpress.com/2009/07/31/whats-new-in-jsf-2/#system-events.
In JSF 1.2, one way I think you could receive a notification will be with PhaseListener's and check the id of the view currently rendering.

JSF2: How to initiate services at deployment [duplicate]

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.

Resources