i followed instructions from for implementing exception handling in jsf web app.
my problem is to show attribute value, that i set in ExceptionHandler
here is ExceptionHandler.java
#Override
public void handle() throws FacesException {
final Iterator<ExceptionQueuedEvent> i = getUnhandledExceptionQueuedEvents().iterator();
while (i.hasNext()) {
ExceptionQueuedEvent event = i.next();
ExceptionQueuedEventContext context = (ExceptionQueuedEventContext) event.getSource();
Throwable t = context.getException();
FacesContext fc = FacesContext.getCurrentInstance();
Map<String, Object> requestMap = fc.getExternalContext().getRequestMap();
NavigationHandler nav = fc.getApplication().getNavigationHandler();
try {
// Push some useful stuff to the request scope for
// use in the page
System.err.println("DefaultExceptionHandler.handle()...exceptionMessage = " + t.getMessage());
requestMap.put("exceptionMessage", t.getMessage());
nav.handleNavigation(fc, null, "error/500");
fc.renderResponse();
} finally {
i.remove();
}
}
getWrapped().handle();
}
and 500.xhtml
<ui:composition
xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core">
The error message is:
<br />
<b>
3. : #{exceptionMessage}
</b>
<br />
<b>
1. : #{requestScope['exceptionMessage']}
</b>
<br />
<b>
2. : #{param['exceptionMessage']}
</b>
and page in browser looks like:
The error message is:
3. :
1. :
2. :
thanks in advance!!
First of all, that's not a request parameter at all. Request parameters are the data which the enduser has sent to the server along with the HTTP request. In GET requests they are visible in query string part of the URL. In POST requests, they are hidden in request body (but visible with a HTTP traffic monitor such as browser's builtin one accessible by F12 key). What you were setting is a request attribute. So, from the attempts, only #{exceptionMessage} and #{requestScope['exceptionMessage']} should have worked, but #{param['exceptionMessage']} definitely not.
Coming back to your concrete problem, this can happen when you're sending a redirect on navigation by either <redirect> in navigation case, or by appending ?faces-redirect=true to the outcome somewhere in a custom navigation handler. A redirect basically instructs the client to create a brand new HTTP request, hereby trashing the initial HTTP request including all of its attributes.
If that's also not the problem, then the (customized) FacesContext or ExternalContext implementation being used is likely broken. Hard to tell as you didn't tell anything about the JSF impl/version used nor about the server used nor any "3rd party" libraries. E.g. Spring Web Flow's ExternalContext implementation is known to be broken like that in some versions.
Related
I've the understanding that Spring Integration (SI) will wrap any exception (under the SI domain) to a MessageException instance and place it on the "error-channel".
Following are few snippets from my spring config file :
<int:channel-interceptor pattern="ersServiceReqRcvPostValidationChannel,ersServiceResRcvPostValidationChannel" order="1">
<bean class="com.bnym.ecs.report.service.orchestration.interceptors.MsgJSONSyntaxValidationInterceptor"/>
</int:channel-interceptor>
<int:channel-interceptor pattern="ersServiceReqRcvPostValidationChannel,ersServiceResRcvPostValidationChannel" order="2">
<bean class="com.bnym.ecs.report.service.orchestration.interceptors.MsgMetaDataValidationInterceptor"/>
</int:channel-interceptor>
<!-- Gateways -->
<int:gateway id="ersServiceReqRcvGateway"
service-interface="com.bnym.ecs.report.service.orchestration.gateway.ERSOrchestrationSvcReqGateway"
error-channel="reqRcvExceptionHandlerChannel">
<int:method name="processRequest" request-channel="ersServiceReqRcvPostValidationChannel" />
</int:gateway>
<!-- Chain to handle all incoming request *after* doing all validations -->
<int:chain input-channel="ersServiceReqRcvPostValidationChannel">
<int:service-activator ref="msgReqAuditDetailDAOIntegrator" method="persist" />
<!-- Router -->
<int:router ref="ersServiceReqRcvRouter" />
</int:chain>
<!-- 6) Pass the message through ERS svc to Exec svc ADH chain - Chain2 -->
<int:chain input-channel="ersSvc2execSvcQMRChannel" output-channel="ersServiceResRcvPostValidationChannel">
<int:transformer ref="json2ObjTransformer" method="transformToERSOrchestrationSvcReq" />
<int:service-activator ref="executionSvcReqMsgBuilder" method="getRptExecutionSvcReqForDataEngine" />
<int:transformer ref="obj2JsonTransformer" method="transformFromRptExecutionSvcReqForDataEngine" />
<int:service-activator ref="msgReqAuditDAOIntegrator" method="persist" />
<int:service-activator ref="msgReqAuditDetailDAOIntegrator" method="persist" />
<int:service-activator ref="executionSvcRESTStub" method="executeReportJSON" />
</int:chain>
<int:chain input-channel="reqRcvExceptionHandlerChannel">
<int:transformer ref="exceptionTransformer" method="handleError"/>
</int:chain>
The client makes a REST call to my implementation class which inturn places the received request on the Gateway defined in above spring config file
#Path("/reportExecutor")
public class ERSOrchestrationServiceImpl {
#Autowired
private ReportInstanceDAO reportInstanceDAO;
private static final ERSOrchestrationSvcDiagnosticLogger _logger =
ERSOrchestrationSvcDiagnosticLogger.getInstance(ERSOrchestrationServiceImpl.class);
#Context
HttpServletRequest request;
#Context
HttpServletResponse response;
#POST
#Path("/executeOnlineReport")
#Produces({MediaType.APPLICATION_JSON})
public String executeOnlineReport(String jsonRequest) {
ApplicationContext appCtx = SpringApplicationContextUtil.getApplicationContext();
ERSOrchestrationSvcReqGateway ersOrchestrationSvcReqGateway =
(ERSOrchestrationSvcReqGateway) appCtx.getBean("ersServiceReqRcvGateway");
Message<String> inputMsg = MessageBuilder.withPayload(jsonRequest)
.setHeader(ERSServiceConstants.KEY_MSG_CORRELATION_ID, correlationId)
.setHeader(ERSServiceConstants.KEY_MSG_REPORT_INSTANCE_ID, reportInstanceId)
.build();
Message<String> returnMsg = ersOrchestrationSvcReqGateway.processRequest(inputMsg);
return returnMsg.getPayload();
}
As mentioned in above spring config file, the error-channel is read by a Transformer that creates a valid failed response message for the client and returns the message.
public class ErrorMessageUnwrapTransformer {
#Autowired
private Gson gsonUtil;
#Autowired
private ReportInstanceDAO reportInstanceDAO;
#Autowired
private ERSOrchestrationSvcFailedResMsgBuilder executionSvcFailedMsgBuilder;
private static final ERSOrchestrationSvcDiagnosticLogger _log =
ERSOrchestrationSvcDiagnosticLogger.getInstance(ErrorMessageUnwrapTransformer.class);
#Transformer
public Message<?> handleError(Message<?> message) {
try{
failedMsg = ((MessagingException) message.getPayload()).getFailedMessage();
//some code logic to build a valid failed response message goes here
Message<?> failedResponseMsg = executionSvcFailedMsgBuilder.getERSOrcSvcFailedResMsg(failedMsg );
return failedResponseMsg;
}
All seems to work fine when I get an exception, i.e., the exception is wrapped as MessagingException, put on the error-channel, the Transformer is able to read the channel, get failedMessage out of it, able to create a valid failed response message and return it.
However, the only issue I get is the call does not go back to the caller. In other words, the handle does not go back to the following code that had initiated the processing flow:
Message<String> returnMsg = ersOrchestrationSvcReqGateway.processRequest(inputMsg);
Can someone pls let me know why is the message returned by error-channel-read-Transformer not returning back to the class that invoked the Gateway method ?
Your problem here that you return the entire Message<?> from the transformer. This one is a component which doesn't care about headers, when the returned object is Message<?> already. You should worry about them on your own, like copy all headers from the failedMsg to your own failedResponseMsg.
Why is that so important?
Since you use request/reply gateway you are expecting for the return on that method invocation, something on the background ensure that for you. And it is a classical replyChannel algorithm.
Any AbstractReplyProducingMessageHandler sends its result to the replyChannel, if you don't have an outputChannel configured, like your reqRcvExceptionHandlerChannel <chain> here.
With other components we can rely on the copy-header-from-request function, but not here with the <transformer>.
From other side ErrorMessage may be created in some context where we don't have headers, but we exactly may have the failedMessage in the MessagingException for which the ErrorMessage has been caused. So, we have to ensure headers from that failedMessage.
Hope I am clear.
I have two pages myaccount.xhtml and selectbank.xhtml
In my account page there is one option for recharge account in which user will enter the amount when user will press submit button then it will goto the select bank page using following bean method.
public String gotoPayMethod() {
FacesMessage doneMessage=new FacesMessage("Redirecting to Payment Type Page");
FacesContext.getCurrentInstance().addMessage(null, doneMessage);
return "SelectBank";
}
When user will goto to selectbank there user will have to submit payment method but in this page it shows the amount as null which was entered in the previous page.
Both the pages are using the same bean and the scope of the bean is request scope.
So how can I access that value without passing this values through URL GET method.
Just for my satisfaction I used session scope then it was working but I know thats not the proper way because I start using session scope for each pages then it will not be efficient.
Thanks
Well, if your beans are RequestScoped than you don't have same bean for both pages. These beans are recreated for every request, so you should pass parameters. Change return statement of your gotoPayMethod() to:
return "SelectBank?faces-redirect=true&includeViewParams=true";
and on selectbank.xhtml add:
<f:metadata>
<f:viewParam name="amount" value="#{bean.amount}" />
</f:metadata>
Adapt this to your property and bean name.
If using parameters is not a solution you can add this parameter in the session, and remove it from session in second bean when you retrieve it:
FacesContext.getCurrentInstance().getExternalContext().getSessionMap().put("amount", amount);
((HttpServletRequest)FacesContext.getCurrentInstance().getExternalContext().getRequest()).getSession().removeAttribute("amount");
Second construction for removing the attribute is necessary as Map returned from getSessionMap() is immutable.
You can use the #{flash} object that will keep your data until the next view. This way you won't need to deal with view parameters.
Details from myaccount.xhtml:
<h:form>
<h:outputText value="Enter amount: " />
<h:inputText value="#{flash.amount}" />
<br/>
<h:commandButton value="Go to payment method" action="#{bean.gotoPayMethod}" />
<h:form>
Bean of both views:
#ManagedBean
#RequestScoped
public class Bean {
#ManagedProperty("#{flash}")
private Flash flash;
private int amount = -1;
public Bean () { }
public String getAmount() {
if(amount == -1) {
int val = Integer.parseInt((String)flash.get("amount"));
flash.keep("amount");
amount = val;
}
return amount;
}
public Flash getFlash() {
return flash;
}
public void setFlash(Flash flash) {
this.flash = flash;
}
public String gotoPayMethod() {
//do business job
return "SelectBank?faces-redirect=true";
}
}
Details from selectbank.xhtml:
<h:outputText value="Amount entered by user is #{bean.amount}" />
Your use case is not of simple request/response cycle, the life span is more than one request response which makes it candidate for session scope.
Using hidden variable or GET parameters in URL is not good practice especially for a banking application. Where security is so important dont compromise on small memory foot print.
If flash scope map simplifies the case you can use it, but I would not go for such a thing.
Update: Forgot to mention you can check Conversation scope too.
Background
Generate a report in various formats (e.g., PDF, delimited, HTML) using an ADF Task Flow.
Problem
HTTP headers are being sent twice: once by the framework and once by a bean.
Source Code
The source code includes:
Button Action
Managed Bean
Task Flow
Button Action
The button action:
<af:commandButton text="Report" id="submitReport" action="Execute" />
Managed Bean
The Managed Bean is fairly complex. The code to responseComplete is getting called, however it does not seem to be called sufficiently early to prevent the application framework from writing the HTTP headers.
HTTP Response Header Override
/**
* Sets the HTTP headers required to indicate to the browser that the
* report is to be downloaded (rather than displayed in the current
* window).
*/
protected void setDownloadHeaders() {
HttpServletResponse response = getServletResponse();
response.setHeader( "Content-Description", getContentDescription() );
response.setHeader( "Content-Disposition", "attachment, filename="
+ getFilename() );
response.setHeader( "Content-Type", getContentType() );
response.setHeader( "Content-Transfer-Encoding",
getContentTransferEncoding() );
}
Issue Response Complete
getFacesContext().responseComplete();
Bean Run and Configure
public void run() {
try {
Report report = getReport();
configure(report.getParameters());
report.run();
} catch (Exception e) {
e.printStackTrace();
}
}
private void configure(Parameters p) {
p.put(ReportImpl.SYSTEM_REPORT_PROTOCOL, "http");
p.put(ReportImpl.SYSTEM_REPORT_HOST, "localhost");
p.put(ReportImpl.SYSTEM_REPORT_PORT, "7002");
p.put(ReportImpl.SYSTEM_REPORT_PATH, "/reports/rwservlet");
p.put(Parameters.PARAM_REPORT_FORMAT, "pdf");
p.put("report_cmdkey", getReportName());
p.put("report_ORACLE_1", getReportDestinationType());
p.put("report_ORACLE_2", getReportDestinationFormat());
}
Task Flow
The Task Flow calls Execute, which refers to the bean's run() method:
entry -> main -> Execute -> ReportBeanRun
Where:
<method-call id="ReportBeanRun">
<description>Executes a report</description>
<display-name>Execute Report</display-name>
<method>#{reportBean.run}</method>
<outcome>
<fixed-outcome>success</fixed-outcome>
</outcome>
</method-call>
The bean is assigned to the request scope, with a few managed properties:
<control-flow-rule id="__3">
<from-activity-id>main</from-activity-id>
<control-flow-case id="ExecuteReport">
<from-outcome>Execute</from-outcome>
<to-activity-id>ReportBeanRun</to-activity-id>
</control-flow-case>
</control-flow-rule>
<managed-bean id="ReportBean">
<description>Executes a report</description>
<display-name>ReportBean</display-name>
<managed-bean-scope>request</managed-bean-scope>
...
</managed-bean>
The <fixed-outcome>success</fixed-outcome> strikes me as incorrect -- I don't want the method call to return to another task.
Restrictions
The report server receives requests from the web server exclusively. The report server URL cannot be used by browsers to download directly, for security reasons.
Error Messages
The error message that is generated:
Duplicate headers received from server
Error 349 (net::ERR_RESPONSE_HEADERS_MULTIPLE_CONTENT_DISPOSITION): Multiple distinct Content-Disposition headers received. This is disallowed to protect against HTTP response splitting attacks.
Nevertheless, the report is being generated. Preventing the framework from writing the HTTP headers would resolve this issue.
Question
How can you set the HTTP headers in ADF while using a Task Flow to generate a PDF by calling a managed bean?
Ideas
Some additional ideas:
Override the Page Lifecycle Phase Listener (ADFPhaseListener + PageLifecycle)
Develop a custom Servlet on the web server
Related Links
http://www.oracle.com/technetwork/middleware/bi-publisher/adf-bip-ucm-integration-179699.pdf
http://www.slideshare.net/lucbors/reports-no-notes#btnNext
http://www.techartifact.com/blogs/2012/03/calling-oracle-report-from-adf-applications.html?goback=%2Egde_4212375_member_102062735
http://docs.oracle.com/cd/E29049_01/web.1112/e16182/adf_lifecycle.htm#CIABEJFB
Thank you!
The problem was an incorrect implementation of RFC 2183:
response.setHeader( "Content-Disposition", "attachment; filename="
+ getFilename() );
The ; cannot be a ,.
I am working with creating PDF from XHTML with JSF 2.0 and iText. The page is simple registration form. When User enters all the data in the page and click on submit I have to get the whole HTML page source with user entered values into the bean. I used Jsoup to get the HTML but I got only HTML source not the values entered by the user.How can I do this?
My current code is
FacesContext facesContext = FacesContext.getCurrentInstance();
ExternalContext externalContext = facesContext.getExternalContext();
HttpSession session = (HttpSession) externalContext.getSession(true);
String url = "http://localhost:8080/Pro/faces/part1.xhtml;JSESSIONID=" + session.getId();
try {
Document doc = Jsoup.connect(url).get();
Your Jsoup approach would only work if the managed bean holding the model data associated with the view is been stored in the session scope. Jsoup is namely firing a fresh new HTTP GET request, which thus means that it would in case of a request or view scoped bean get a brand new and entirely distinct instance, with all properties set to default.
If you want to keep your bean in the request or view scope (very reasonable), then you'd need to temporarily put the model data in the session before the Jsoup call and remove it after the Jsoup call.
Your another mistake is that you uppercased the JSESSIONID URL path fragment. It's case sensitive and it should in fact be all lowercase: jsessionid.
So, all with all, this should do if you want to keep your bean request or view scoped:
#ManagedBean
#ViewScoped
public class Bean {
#ManagedProperty("#{beanModel}") // Must match (unique!) session attribute name.
private Model model;
public void submit() throws IOException {
ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
HttpServletRequest request = (HttpServletRequest) externalContext.getRequest();
HttpSession session = (HttpSession) externalContext.getSession(true);
String url = request.getRequestURL().append(";jsessionid=").append(session.getId()).toString();
session.setAttribute("beanModel", model);
Document doc = Jsoup.connect(url).get();
session.removeAttribute("beanModel");
// ...
}
}
I have a JSF Phase Listerner that checks to see if the user is logged in, and if not, redirects them to the login page. This is working fine for non-ajax requests. However, if the user is on a page, in my case, one that has a primefaces data table, and clicks on a button that invokes an ajax request -- but their session has timed out -- the code gets executed that issues the redirect (using ExternalContext#redirect), however the user is not navigated to the login page.
Any idea why this is not working?
Here is my phase listener:
private static final String IS_LOGGED_IN_INDICATOR = "loggedIn";
private static final String LOGIN_PAGE = "/login.jsp";
public PhaseId getPhaseId() {
return PhaseId.RESTORE_VIEW;
}
public void beforePhase(PhaseEvent event) {
ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext();
HttpSession session = (HttpSession)ec.getSession(false);
if (session==null || session.getAttribute(IS_LOGGED_IN_INDICATOR) == null) {
try {
ec.redirect(LOGIN_PAGE);
}
catch(IOException e) {
// log exception...
}
}
}
public void afterPhase(PhaseEvent event) {
// no-op
}
}
It failed because the ajax context is trying to obtain the render kit from the view root, while there is no view root at all. It has not been restored at that point yet. This resulted in a NullPointerException in PartialViewContext#createPartialResponseWriter(). This exception is in turn not been thrown, but instead been put in an ajax exception queue which is supposed to be handled by a custom ExceptionHandler. You apparently don't have any one. This exception is visible if you create/use such one like the FullAjaxExceptionHandler (see also this blog for more detail).
To fix the particular problem, do the job in afterPhase() instead. The view root is then fully restored and the ajax context can obtain the render kit from it in order to write a specialized XML response which instructs the JSF ajax engine in JavaScript to change the window location. Without ajax, a render kit was not necessary as a redirect is basically just a matter of setting a response header.
Whether the particular NullPointerException is in turn a bug in Mojarra or not is a different question which can better be posted in flavor of an issue report at their own issue tracker.
this is because you have to send a special response in XML for Ajax request in order to do redirect (check this answer) , I have implemented this in a Filter like this..
// Check if it's an Ajax Request
if ("partial/ajax".equals(((HttpServletRequest) request).getHeader("Faces-Request"))) {
//redirect
response.setContentType("text/xml");
response.getWriter()
.append("<?xml version= \"1.0\" encoding=\"UTF-8\"?>")
.printf("<partial-response><redirect url=\"%s\"></redirect></partial-response>",url);
you should port this to your Phase Listener.