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.
Related
I've replaced the f:ajax tag with an homemade solution that doesn't put inline script. It works wonder for actionButton. However I cannot make it work for a listener on a panelGroup. The reason is that it is specified nowhere what the bean target method resulting from the ajax request should be. In other words with a commandButton I can specify the target bean method in action, but there is no such attribute for panelGroup; as I don't want to use f:ajax listener, I want to replace it.
<h:commandButton data-widget="jsfajax" value="ajax" action="#{someAction}"/>
$(document).ready(function(){
(function(widgets){
document.body.addEventListener("click", function(e) {
var w = e.target.getAttribute("data-widget");
if(w){
e.preventDefault();
widgets[w](e.target);
}
});
})(new Widgets);
});
function Widgets(){
this.jsfajax = function jsfajax(elem){
if(elem.id == ""){
elem.id = elem.name;
}
mojarra.ab(elem,"click",'action','#form',0);
}
}
This works.
But this obviously doesn't (it does but it doesn't invoke anything) :
<h:panelGroup>
<f:passThroughAttribute name="data-widget" value="jsfajax"/>
Click here
</h:panelGroup>
But this does :
<h:panelGroup>
<f:ajax event="click" listener="#{someAction}"/>
Click here
</h:panelGroup>
Both those panelGroup result in the same HTML output, so I assume it's the jsf container which "remembers" the click on that panelGroup is linked to #{someAction}.
What I'd like to do is recreate that link without using f:ajax listener. At the moment I've to use an hidden commandButton which is less elegant.
So maybe a composite component panelGroup which would save the "action link", I've no idea.
What you want to achieve is only possible on UICommand components, not on ClientBehaviorHolder components. One solution would be to create a custom component extending HtmlCommandLink which renders a <div> instead of <a> and use it like so <your:div action="#{bean.action}">.
The most ideal solution would be to replace the standard renderers. E.g. for <h:panelGorup>:
<render-kit>
<renderer>
<component-family>javax.faces.Panel</component-family>
<renderer-type>javax.faces.Group</renderer-type>
<renderer-class>com.example.YourPanelGroupRenderer</renderer-class>
</renderer>
</render-kit>
Basically, those renderers should skip rendering <f:ajax>-related on* attributes and instead render your data-widget attribute (and preferably also other attributes representing existing <f:ajax> attributes such as execute, render, delay, etc). You should also program against the standard API, not the Mojarra-specific API. I.e. use jsf.ajax.request() directly instead of mojarra.ab() shortcut.
This way you can keep your view identical conform the JSF standards. You and future developers would this way not even need to learn/think about a "proprietary" API while writing JSF code. You just continue using <h:panelGroup><f:ajax>. You simply plug in the custom renders and script via a JAR in webapp and you're done. That JAR would even be reusable on all other existing JSF applications. It could even become popular, because inline scripts are indeed considered poor practice.
It's only quite some code and not necessarily trivial for a starter.
A different approach is to replace the standard response writer with a custom one wherein you override writeAttribute() and check if the attribute name starts with on and then handle them accordingly the way you had in mind. E.g. parsing it and writing a different attribute. Here's a kickoff example which also recognizes <h:panelGroup><f:ajax>.
public class NoInlineScriptRenderKitFactory extends RenderKitFactory {
private RenderKitFactory wrapped;
public NoInlineScriptRenderKitFactory(RenderKitFactory wrapped) {
this.wrapped = wrapped;
}
#Override
public void addRenderKit(String renderKitId, RenderKit renderKit) {
wrapped.addRenderKit(renderKitId, renderKit);
}
#Override
public RenderKit getRenderKit(FacesContext context, String renderKitId) {
RenderKit renderKit = wrapped.getRenderKit(context, renderKitId);
return (HTML_BASIC_RENDER_KIT.equals(renderKitId)) ? new NoInlineScriptRenderKit(renderKit) : renderKit;
}
#Override
public Iterator<String> getRenderKitIds() {
return wrapped.getRenderKitIds();
}
}
public class NoInlineScriptRenderKit extends RenderKitWrapper {
private RenderKit wrapped;
public NoInlineScriptRenderKit(RenderKit wrapped) {
this.wrapped = wrapped;
}
#Override
public ResponseWriter createResponseWriter(Writer writer, String contentTypeList, String characterEncoding) {
return new NoInlineScriptResponseWriter(super.createResponseWriter(writer, contentTypeList, characterEncoding));
}
#Override
public RenderKit getWrapped() {
return wrapped;
}
}
public class NoInlineScriptResponseWriter extends ResponseWriterWrapper {
private ResponseWriter wrapped;
public NoInlineScriptResponseWriter(ResponseWriter wrapped) {
this.wrapped = wrapped;
}
#Override
public ResponseWriter cloneWithWriter(Writer writer) {
return new NoInlineScriptResponseWriter(super.cloneWithWriter(writer));
}
#Override
public void writeAttribute(String name, Object value, String property) throws IOException {
if (name.startsWith("on")) {
if (value != null && value.toString().startsWith("mojarra.ab(")) {
super.writeAttribute("data-widget", "jsfajax", property);
}
}
else {
super.writeAttribute(name, value, property);
}
}
#Override
public ResponseWriter getWrapped() {
return wrapped;
}
}
The most important part where you have your freedom is the writeAttribute() method in the last snippet. The above kickoff example just blindly checks if the on* attribute value starts with Mojarra-specific "mojarra.ab(" and then instead writes your data-widget="jsfajax". In other words, every single (naturally used!) <f:ajax> will be rewritten this way. You can continue using <h:commandLink><f:ajax> and <h:panelGroup><f:ajax> the natural way. Don't forget to deal with other <f:ajax> attributes while you're at it.
In order to get it to run, register as below in faces-config.xml:
<factory>
<render-kit-factory>com.example.NoInlineScriptRenderKitFactory</render-kit-factory>
</factory>
You only still need to take into account existing implementation-specific details (fortunately there are only two: Mojarra and MyFaces).
See also:
How do I determine the renderer of a built-in component
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.
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.
A simple question on the title.
My case is that I want to listen to "before RENDER_RESPONSE" phase, and alter some components internal state.
Is PhaseListener the "right way" to do this in SEAM applications?
If you want alter JSF component internal state, rely on JSF phase listener. Seam way of declaring JSF phase listener is shown bellow
#Name("applicationPhaseListener")
#Scope(ScopeType.APPLICATION)
public class ApplicationPhaseListener {
/**
* Called TRANSPARENTLY by Seam
*/
#Observer("org.jboss.seam.beforePhase")
public void beforePhase(PhaseEvent event) {
}
/**
* Called TRANSPARENTLY by Seam
*/
#Observer("org.jboss.seam.afterPhase")
public void afterPhase(PhaseEvent event) {
}
}
But if you want to alter Seam contextual component state, use
#Name("applicationPhaseListener")
public class ApplicationPhaseListener {
#Observer("applicationListener")
public void applicationListener() {
}
}
You can
Call your event programatically
Events.instance().raiseEvent("applicationListener");
By using #RaiseEvent annotation which is placed aboved some action method
#RaiseEvent("applicationListener")
public void doSomething() {
}
pages.xml
<page id="<PAGE_ID_GOES_HERE>">
<raise-event type="applicationListener"/>
</page>
What all phases of the JSF Life cycle should get called on page refresh
That entirely depends on the kind of request (POST or GET) and the available parameters. A plain vanilla GET request for example would fire the first and last phases only. A "double submit" (refreshing a form submit) would by default go through all phases, but depending on the presence of the immediate="true" in UIInput and/or UICommand components, some phases might be skipped.
You can create yourself a simple PhaseListener and play around with it to learn which phases are executed and which not.
package mypackage;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;
public class LifeCycleListener implements PhaseListener {
public PhaseId getPhaseId() {
return PhaseId.ANY_PHASE;
}
public void beforePhase(PhaseEvent event) {
System.out.println("START PHASE " + event.getPhaseId());
}
public void afterPhase(PhaseEvent event) {
System.out.println("END PHASE " + event.getPhaseId());
}
}
Register it as follows in faces-config.xml to get it to run:
<lifecycle>
<phase-listener>mypackage.LifeCycleListener</phase-listener>
</lifecycle>
See also:
Debug JSF lifecycle