Handle exceptions in JSF (view) and save this exception message - jsf

I have two Classes that extend from ExceptionHandlerWrapper & ExceptionHandlerFactory
for catching any exceptions that occur in the view layer
and navigate to error page and show this error.
So I need store or save this exceptions somewhere
but I can`t get this exception message.
I tried to get this message in session but I need another way to save this message with out ViewScope and PageFlowScope and RequestScope since the latter ones return 'null'
import javax.faces.context.ExceptionHandler;
import javax.faces.context.ExceptionHandlerFactory;
public class CustomExceptionHandlerFactory extends ExceptionHandlerFactory {
private ExceptionHandlerFactory parent;
public CustomExceptionHandlerFactory(ExceptionHandlerFactory parent) {
this.parent = parent;
}
#Override
public ExceptionHandler getExceptionHandler() {
ExceptionHandler result = new CustomExceptionHandler(parent.getExceptionHandler());
return result;
}
}
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 iterator = getUnhandledExceptionQueuedEvents().iterator();
while (iterator.hasNext()) {
ExceptionQueuedEvent event = (ExceptionQueuedEvent) iterator.next();
ExceptionQueuedEventContext context = (ExceptionQueuedEventContext) event.getSource();
Throwable throwable = context.getException();
FacesContext fc = FacesContext.getCurrentInstance();
// Map<String, Object> sessionMap = fc.getExternalContext().getSessionMap();
NavigationHandler navigator = fc.getApplication().getNavigationHandler();
AdfFacesContext adfc = AdfFacesContext.getCurrentInstance();
Map<String,Object> valueViewScope = adfc.getViewScope();
try {
// Flash flash = fc.getExternalContext().getFlash();
// Put the exception in the flash scope to be displayed in the error
// page if necessary ...
// flash.put("errorDetails", throwable.getMessage());
ADFContext.getCurrent().getSessionScope().put("errorDetails", throwable.getMessage());
valueViewScope.put("errorDetails", throwable.getMessage());
System.out.println("the error is put in the Session: " + throwable.getMessage());
//NavigationHandler navigationHandler = fc.getApplication().getNavigationHandler();
navigator.handleNavigation(fc, null, "ErrorPage?faces-redirect=true");
// navigationHandler.handleNavigation(fc, null, "ErrorPage?faces-redirect=true");
fc.renderResponse();
} finally {
iterator.remove();
}
}
// Let the parent handle the rest
getWrapped().handle();
}

Related

JSF ExceptionHandler - Response already committed in some cases

We have implemented a custom ExceptionHandler that attempts to intercept any NonexistentConversationException that are thrown if a user requests a URL with an old cid in it (eg. bookmarked link etc.).
The handler simply removed the cid from the URL and performs a redirect to the updated URL.
This solution seems to work in most cases, we have a number of different user 'roles' within the App each with their own login areas and for one of these user types it seems that fc.getExternalContext().isResponseCommitted() is always true when the ExceptionHandler fires which means we are unable to perform the redirect. All other user types it works ok.
I am not sure exactly what the difference is with this user type for this to happen, I am guessing some CDI bean we using setup differently
Is there a way to unsure the ExceptionHandler kicks in earlier before the response is committed?
Below is the handler...
public class ConversationExpiredExceptionHandler
extends ExceptionHandlerWrapper {
final static Logger log = LoggerFactory.getLogger(ConversationExpiredExceptionHandler.class);
private ExceptionHandler wrapped;
private static String CONVERSATION_PARAM = "cid";
public ConversationExpiredExceptionHandler(ExceptionHandler wrapped) {
this.wrapped = wrapped;
}
#Override
public ExceptionHandler getWrapped() {
return this.wrapped;
}
#Override
public void handle() throws FacesException {
for (Iterator<ExceptionQueuedEvent> i =
getUnhandledExceptionQueuedEvents().iterator();
i.hasNext();) {
ExceptionQueuedEvent event = i.next();
ExceptionQueuedEventContext context = (ExceptionQueuedEventContext) event.getSource();
Throwable t = context.getException();
if (t instanceof javax.enterprise.context.NonexistentConversationException
|| t instanceof org.jboss.weld.contexts.NonexistentConversationException
|| t.getCause() instanceof org.jboss.weld.contexts.NonexistentConversationException /* can be wrapped in FacesException */) {
final FacesContext fc = FacesContext.getCurrentInstance();
try {
if (!fc.getExternalContext().isResponseCommitted()) {
if(Faces.getRequestQueryStringMap().containsKey(CONVERSATION_PARAM)) {
String requestedUrl = Faces.getRequestURLWithQueryString();
String updatedUrl = this.removeQueryParameter(requestedUrl,CONVERSATION_PARAM);
log.debug("No conversation active for {}, redirecting to {}",requestedUrl,updatedUrl);
fc.getExternalContext().redirect(updatedUrl);
}
}
fc.renderResponse();
break;
} catch (Exception ex) {
throw new FacesException(ex);
} finally {
i.remove();
}
}
}
getWrapped().handle();
}
private String removeQueryParameter(String url, String parameterName) throws URISyntaxException {
URIBuilder uriBuilder = new URIBuilder(url);
List<NameValuePair> queryParameters = uriBuilder.getQueryParams();
for (Iterator<NameValuePair> queryParameterItr = queryParameters.iterator(); queryParameterItr.hasNext();) {
NameValuePair queryParameter = queryParameterItr.next();
if (queryParameter.getName().equals(parameterName)) {
queryParameterItr.remove();
}
}
uriBuilder.setParameters(queryParameters);
return uriBuilder.build().toString();
}

SimpleJSFNavigationHandler cannot be cast to javax.faces.application.ConfigurableNavigationHandler

I'm migrating a JSF 1.2 project to JSF 2 and PrimeFaces 6 with Ultima layout.
When using Ultima layout, I get the below exception:
SimpleJSFNavigationHandler cannot be cast to javax.faces.application.ConfigurableNavigationHandler.
How to fix it?
Below is the SimpleJSFNavigationHandler.
import java.io.IOException;
import javax.faces.FacesException;
import javax.faces.application.NavigationHandler;
import javax.faces.application.ViewHandler;
import javax.faces.component.UIViewRoot;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import org.springframework.web.jsf.DecoratingNavigationHandler;
public class SimpleJSFNavigationHandler extends DecoratingNavigationHandler {
public static final String DEFAULT_REDIRECT_PREFIX = "redirect:";
public static final String DEFAULT_FORWARD_PREFIX = "/";
private String redirectPrefix = DEFAULT_REDIRECT_PREFIX;
private String forwardPrefix = DEFAULT_FORWARD_PREFIX;
public SimpleJSFNavigationHandler() {
}
public SimpleJSFNavigationHandler(NavigationHandler originalNavigationHandler) {
super(originalNavigationHandler);
}
#Override
public void handleNavigation(FacesContext facesContext, String fromAction, String outcome, NavigationHandler originalNavigationHandler) {
if (outcome != null && outcome.startsWith(redirectPrefix)) {
ExternalContext externalContext = facesContext.getExternalContext();
ViewHandler viewHandler = facesContext.getApplication().getViewHandler();
String url = outcome.substring(redirectPrefix.length());
String urlParams="";
if(url.indexOf("?")>0)
urlParams = url.substring(url.indexOf("?"));
String redirectPath = viewHandler.getActionURL(facesContext, url);
try {
//System.out.println("MMMMMMMMMMMMMMMM:::::::::::::::::" + urlParams);
externalContext.redirect(externalContext.encodeActionURL(redirectPath+urlParams));
} catch (IOException e) {
throw new FacesException(e.getMessage(), e);
}
facesContext.responseComplete();
} else if (outcome != null && outcome.startsWith(forwardPrefix)) {
ViewHandler viewHandler = facesContext.getApplication().getViewHandler();
//create new view
String newViewId = outcome.substring(forwardPrefix.length());
if (newViewId.length()>0 && newViewId.charAt(0)!='/') {
newViewId = "/" + newViewId;
}
UIViewRoot viewRoot = viewHandler.createView(facesContext, newViewId);
viewRoot.setViewId(newViewId);
facesContext.setViewRoot(viewRoot);
facesContext.renderResponse();
} else {
callNextHandlerInChain(facesContext, fromAction, outcome, originalNavigationHandler);
}
}
}
You don't need Spring's DecoratingNavigationHandler. You can just use JSF's own ConfigurableNavigationHandlerWrapper.
public class SimpleJSFNavigationHandler extends ConfigurableNavigationHandlerWrapper {
private ConfigurableNavigationHandler wrapped;
public SimpleJSFNavigationHandler(ConfigurableNavigationHandler wrapped) {
this.wrapped = wrapped;
}
#Override
public void handleNavigation(FacesContext facesContext, String fromAction, String outcome) {
if (...) {
// Your original code here.
} else if (...) {
// Your original code here.
} else {
// Update only the last else part as below.
getWrapped().handleNavigation(facesContext, fromAction, outcome);
}
}
#Override
public ConfigurableNavigationHandler getWrapped() {
return wrapped;
}
}
In the upcoming JSF 2.3 this can even be further simplified as per spec issue 1429 which should further reduce boilerplate code in FacesWrapper implementations.
public class SimpleJSFNavigationHandler extends ConfigurableNavigationHandlerWrapper {
public SimpleJSFNavigationHandler(ConfigurableNavigationHandler wrapped) {
super(wrapped);
}
#Override
public void handleNavigation(FacesContext facesContext, String fromAction, String outcome) {
if (...) {
// Your original code here.
} else if (...) {
// Your original code here.
} else {
// Update only the last else part as below.
getWrapped().handleNavigation(facesContext, fromAction, outcome);
}
}
}

How to use NavigationHandler during the Render Response phase

I'm using JSF 2.2 (Mojarra) and Faclets in a web application. I use a custom ExceptionHandler to handle exceptions. I leverage the JSF implicit navigation system and cause the server to navigate to the 'error.xhtml' page.
public class FrontEndExceptionHandler extends ExceptionHandlerWrapper {
private ExceptionHandler wrapped;
FrontEndExceptionHandler(ExceptionHandler exception) {
this.wrapped = exception;
}
#Override
public ExceptionHandler getWrapped() {
return wrapped;
}
#Override
public void handle() throws FacesException {
final Iterator<ExceptionQueuedEvent> iter = getUnhandledExceptionQueuedEvents().iterator();
while (iter.hasNext()) {
ExceptionQueuedEvent event = iter.next();
ExceptionQueuedEventContext context = (ExceptionQueuedEventContext) event.getSource();
// get the exception from context
Throwable t = context.getException();
final FacesContext fc = FacesContext.getCurrentInstance();
final Flash flash = fc.getExternalContext().getFlash();
final NavigationHandler nav = fc.getApplication().getNavigationHandler();
try {
// redirect error page
flash.put("erorrDetails", t.getMessage());
nav.handleNavigation(fc, null, "/errors/error.xhtml");
fc.renderResponse();
} finally {
// remove it from queue
iter.remove();
}
}
// parent handle
getWrapped().handle();
}
}
This assumes that the exception to be handling is not happening during the Render Response phase. But the exception occurs within an Facelets page also during the Render Response phase.Therefore following code doesn't work correct:
nav.handleNavigation(fc, null, "/errors/error.xhtml");
Does anybody has a Idea how to convey the desired information? Is there a way to navigate to the error.xhtml without using NavigationHandler?

Custom Viewhandler for JBoss 7.1

I want to implement a custom ViewHandler. Currently I'm only forwarding all calls to the default Viewhandler, but if I enable this ViewHandler in my faces-config.xml, the preRenderView event type (and maybe other functionality) is broken. Does anybody no what I'm doing wrong?
I'm using JBoss AS 7.1.1.
Thanks.
public class ReverseProxyViewHandler extends ViewHandler {
ViewHandler defaultHandler;
public ReverseProxyViewHandler(ViewHandler defaultHandler) {
this.defaultHandler = defaultHandler;
}
#Override
public Locale calculateLocale(FacesContext context) {
return defaultHandler.calculateLocale(context);
}
#Override
public String calculateRenderKitId(FacesContext context) {
return defaultHandler.calculateRenderKitId(context);
}
#Override
public UIViewRoot createView(FacesContext context, String viewId) {
return defaultHandler.createView(context, viewId);
}
#Override
public String getActionURL(FacesContext context, String path) {
return defaultHandler.getActionURL(context, path);
}
#Override
public String getResourceURL(FacesContext context, String path) {
return defaultHandler.getResourceURL(context, path);
}
#Override
public void renderView(FacesContext context, UIViewRoot viewToRender) throws IOException, FacesException {
defaultHandler.renderView(context, viewToRender);
}
#Override
public UIViewRoot restoreView(FacesContext context, String viewId) {
return defaultHandler.restoreView(context, viewId);
}
#Override
public void writeState(FacesContext context) throws IOException {
defaultHandler.writeState(context);
}
}
I had the same error, but by extending the ViewHandlerWrapper instead of the the ViewHandler I got it to work.
Here is an example of my CustomViewHandler (not a complete example, but was built within our app as a proof of concept) :
import javax.faces.application.ViewExpiredException;
import javax.faces.application.ViewHandler;
import javax.faces.application.ViewHandlerWrapper;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
import org.apache.log4j.Logger;
public class CustomViewHandler extends ViewHandlerWrapper {
private ViewHandler wrapped;
private static Logger LOGGER = Logger.getLogger(CustomViewHandler.class);
public CustomViewHandler(ViewHandler wrapped) {
LOGGER.info("CustomViewHandler.CustomViewHandler():wrapped View Handler:"+wrapped.getClass());
this.wrapped = wrapped;
}
#Override
public UIViewRoot restoreView(FacesContext context, String viewId) {
UIViewRoot root;
try {
LOGGER.info("restoring view : " + viewId);
root = wrapped.restoreView(context, viewId);
} catch (ViewExpiredException e) {
LOGGER.error("View Expired : " + e.getMessage() + " -> recreating");
root = wrapped.createView(context, viewId);
}
return root;
}
#Override
public ViewHandler getWrapped() {
return wrapped;
}
}
If you want a more complete and functional custom view handler, look at the OmniFaces RestorableViewHandler. Source code is available on that page.
From Java Doc
javax.faces.application.ViewHandlerWrapper
Provides a simple implementation of ViewHandler that can be subclassed by developers >wishing to provide specialized behavior to an existing ViewHandler instance. The default >implementation of all methods is to call through to the wrapped ViewHandler.
Usage: extend this class and override getWrapped to return the instance we are wrapping.

JSF - custom NavigationHandler outcome values invalid?

I wrote myself a custom NavigationHandler very similar to following one but just using a stack to save the history:
http://jsfatwork.irian.at/book_de/custom_component.html#!idx:/custom_component.html:fig:backnavigationhandler-code
public class HistoryNavigationHandler extends NavigationHandler
{
private final NavigationHandler navigationHandler;
private final Stack<String> outcomes;
public HistoryNavigationHandler(final NavigationHandler navigationHandler)
{
this.navigationHandler = navigationHandler;
this.outcomes = new Stack<String>();
}
#Override
public void handleNavigation(final FacesContext context, final String fromAction, final String outcome)
{
if (outcome != null)
{
if (outcome.equals("back"))
{
final String lastViewId = this.outcomes.pop();
final ViewHandler viewHandler = context.getApplication().getViewHandler();
final UIViewRoot viewRoot = viewHandler.createView(context, lastViewId);
context.setViewRoot(viewRoot);
context.renderResponse();
return;
}
else
{
this.outcomes.push(context.getViewRoot().getViewId());
}
}
this.navigationHandler.handleNavigation(context, fromAction, outcome);
}
}
Registering this one in the faces-config.xml:
<navigation-handler>
package.HistoryNavigationHandler
</navigation-handler>
Results in following log warning and a message where a previously working link was present:
Warning: jsf.outcome.target.invalid.navigationhandler.type
Something like: this link is disabled because a navigation case could not be matched
What is the problem?
Since JSF 2, the NavigationHandler has been replaced by ConfigurableNavigationHandler. All JSF 2 specific tags/components like <h:link> and so on are relying on it. The NavigationHandler is kept for backwards compatibility.
Here's a kickoff example how to properly extend ConfigurableNavigationHandler:
public class HistoryNavigationHandler extends ConfigurableNavigationHandler {
private NavigationHandler wrapped;
public HistoryNavigationHandler(NavigationHandler wrapped) {
this.wrapped = wrapped;
}
#Override
public void handleNavigation(FacesContext context, String from, String outcome) {
// TODO: Do your job here.
wrapped.handleNavigation(context, from, outcome);
}
#Override
public NavigationCase getNavigationCase(FacesContext context, String fromAction, String outcome) {
return (wrapped instanceof ConfigurableNavigationHandler)
? ((ConfigurableNavigationHandler) wrapped).getNavigationCase(context, fromAction, outcome)
: null;
}
#Override
public Map<String, Set<NavigationCase>> getNavigationCases() {
return (wrapped instanceof ConfigurableNavigationHandler)
? ((ConfigurableNavigationHandler) wrapped).getNavigationCases()
: null;
}
}

Resources