p:commandButton does not open window with update - ajax

I generate an exportList in my Bean:
public void exportExcel() throws WriteException {
try {
FacesContext context = FacesContext.getCurrentInstance();
HttpServletResponse response = (HttpServletResponse) context.getExternalContext().getResponse();
response.setContentType("application/vnd.ms-excel");
response.setHeader("Content-Disposition", "attachment; filename=\"hours.xls\";");
OutputStream out = response.getOutputStream();
WorkbookSettings ws = new WorkbookSettings();
ws.setLocale(new Locale("de", "DE"));
WritableWorkbook workbook = Workbook.createWorkbook(out, ws);
WritableSheet sheet = workbook.createSheet("Sheet1", 0);
sheet.addCell(new Label(0, 0, "ID", bold));
int row = 1;
for (Hour hour : this.listHours) {
sheet.addCell(new Label(0, row, String.valueOf(hour.getId())));
row++;
}
SheetFormatter.setOptimalColumnWidth(sheet);
workbook.write();
workbook.close();
response.flushBuffer();
context.responseComplete();
context.addMessage(null, new FacesMessage("Liste Exportiert"));
}
catch (Exception e) {
}
}
In my page I call method in p:commandButton
<p:commandButton value="#{msg.export}" update="growl"
immediate="true" action="#{hoursView.exportExcel()}" />
My page will not open the excel-List... If if add the attribute ajax="false" it works but then update will not execute...
For information my Bean is SessionScoped if this makes some differences

Your first mistake is that you're trying to download a file using ajax. That just isn't possible. Ajax is executed by JavaScript code which has due to security reasons no facilities to force a "Save as" dialogue and/or write the retrieved response to the local disk file system. That would otherwise open doors to various nasty security breach possibilities.
So, using ajax="false" is absolutely necessary.
Your second mistake is that you're trying to mix different responses into a single response. That just isn't possible. You can only return either a file download, or an ajax update, not both. To retrieve two different responses, you basically need to let the client send two different requests. You could approach this as follows:
Let client send an ajax request to backing bean.
Let server create and save Excel file in server's temporary storage system and generate an unique URL so that it could be accessed by a servlet.
Let server send an ajax response containing the message and the URL.
Let client display the message and invoke a new GET request on the URL. You could use window.location=url; to let JavaScript invoke a new GET request on the URL.

Related

Primefaces call URL and get data

I'm trying to call a URL which should return me a XML file.
I want to Display this XML file.
Right now I don't know how to make the call to get the return file.
I tryied it with a <p:commandButton process="#this" action="http://..." value="Test" /> but I get 2 warnings.
Warning for file: Couldn't find a Mime-Type, add a Mime-Type mapping in your web.xml
Warning for Ressource: Can not be found or operated.
You need to use for example restfull client to get your xml and parse it.
Here's your content of actionlistener that will be called from your button:
Client client = ClientBuilder.newClient( new ClientConfig().register( LoggingFilter.class ) );
WebTarget webTarget = client.target("http://localhost:8080/JerseyDemos/rest").path("employees");
Invocation.Builder invocationBuilder = webTarget.request(MediaType.APPLICATION_XML);
Response response = invocationBuilder.get();
Employees employees = response.readEntity(Employees.class);
List<Employee> listOfEmployees = employees.getEmployeeList();
(from http://howtodoinjava.com/jersey/jersey-restful-client-examples/#get-list)
Try this:
action=#{yourBean.yourAction}
Inside your bean:
public void yourAction() {
FacesContext fc = FacesContext.getCurrentInstance();
fc.getExternalContext().redirect("http://...");
fc.responseComplete();
}
I'm not sure if it's needed, but also you might wanna set ajax=false on p:commandButton

Capture current JSF page content

I want to capture the current page and send it to an application that converts it to pdf.
This is what I am using:
FacesContext facesContext=FacesContext.getCurrentInstance();
HttpServletResponse response = (HttpServletResponse)
facesContext.getExternalContext().getResponse();
HttpServletRequest request = (HttpServletRequest) facesContext.getExternalContext().getRequest();
// RequestPrinter.debugString();
response.reset();
// download a pdf file
response.setContentType("application/pdf");
response.setHeader("Content-Disposition","attachment;filename="+new Date().toString()+".pdf");
prince.setVerbose(true);
prince.setLog(logFile);
try{
//getPath() to the page the user is currently on
URL pagePath=new URL(this.getPath());
URLConnection urlConnection = pagePath.openConnection();
urlConnection.setDoOutput(true);
int length = urlConnection.getContentLength();
//Lets use inputStream
BufferedInputStream bis=new BufferedInputStream(urlConnection.getInputStream());
response.setContentLength(length);
//this.getPageUsingJSoup().data().getBytes();
//call prince and pass params for inputstream outputStream
prince.convert(bis,response.getOutputStream());
urlConnection.getInputStream().close();
}catch(MalformedURLException mu){
mu.printStackTrace();
}
catch(IOException ioe){
ioe.printStackTrace();
}
facesContext.responseComplete();
Since the website requires authentication, the pdf generated is the loging error page.
Is there a way to capture the page's content that uses the current user's session?
Thank you in advance.
Just request the page in the same HTTP session as the current request. If your webapp supports URL rewriting (as by default), then just append session ID as jsessionid path fragment:
String sessionId = ((HttpSession) externalContext.getSession()).getId();
InputStream input = new URL("http://localhost:8080/context/page.jsf;jsessionid=" + sessionId).openStream();
// ...
Or if your webapp doesn't accept URL rewriting, but accepts cookies only, then set it as a request cookie the usual way:
URLConnection connection = new URL("http://localhost:8080/context/page.jsf").openConnection();
connection.setRequestProperty("Cookie", "JSESSIONID=" + sessionId);
InputStream input = connection.getInputStream();
// ...
Note that I removed setDoOutput() since you don't seem to be interested in performing a POST request.
I do not know how to capture the page's content using the current user's session, but I can suggest another way to do it - you could move the pdf conversion logic inside a Selenium test-case and use the test-case to navigate and login to the page requiring authentication. After the automated tc has logged in, you could call your pdf conversion logic...?
Yes of course there is. You are sending this content, so you have it. You should store the Content Object. If you dont have it, inspect your byte streams. The content should be there ;)
There of couple of websites which allow you to convert the entire page to pdf and save it as .pdf file. Try out the site http://pdfcrowd.com/ Hope this helps you.

ICEfaces libary in classpath prevents Save As dialog from popping up on file download

As soon as I add the librarys icefaces.jar icepush.jar icefaces_ace.jar to my classpath in order to use ACE components, my SaveAs dialog won't popup? I'm not sure if this is a bug but without the librarys in classpath it works. Here's my save as method :
public void downloadFile(String propertyPath) throws IOException {
ProxyFile fileToDownload = repBean.downloadFile(propertyPath);
FacesContext facesContext = FacesContext.getCurrentInstance();
ExternalContext externalContext = facesContext.getExternalContext();
HttpServletResponse response = (HttpServletResponse) externalContext.getResponse();
response.reset(); response.setContentType(fileToDownload.getContentType());
response.setHeader("Content-Length", String.valueOf(fileToDownload.getLength()));
response.setHeader("Content-disposition", "attachment; filename=\"" + fileToDownload.getName() + "\"");
BufferedInputStream input = null;
BufferedOutputStream output = null;
try {
input = new BufferedInputStream(fileToDownload.getContent());
output = new BufferedOutputStream(response.getOutputStream());
byte[] buffer = new byte[10240];
for (int length; (length = input.read(buffer)) > 0;) {
output.write(buffer, 0, length);
}
} finally {
output.close();
input.close();
facesContext.responseComplete();
}
}
You can't download files using ajax.
Ajax is under the covers executed by JavaScript's XMLHttpRequest object. The request will be successfully executed and the response will be successfully retrieved. However, JavaScript has no facility to write the response to client's disk file system, nor to force a Save As dialogue with the given response. That would be a huge security breach.
The cause of your concrete problem is ICEfaces itself. Namely, when you integrate ICEfaces in a JSF web application, all standard <h:commandXxx> links/buttons will silently be turned into ajax-enabled ones which indeed causes confusion among starters. Make sure that the download link/button isn't implicitly using ICEfaces-introduced ajax facility. As per their wiki page on the subject, you need to explicitly nest a <f:ajax disabled="true"> to disable this.
Disable Ajax for a Component
You can also disable Ajax at the level of the individual component:
<h:commandButton value="Send" actionListener="#{bean.sendMessage}">
<f:ajax disabled="true"/>
</h:commandButton>
Apply it on your download link/button.

JSP: RequestDispatcher.forward() not forwarding when servlet called via Ajax POST

I have a login form (login.jsp) with two input fields, username and password.
I am using POST via Ajax to access the login servlet.
I want the user to login, and if the login is successful, be redirected to another page called 'search.jsp'. If unsuccessful, a 'login failed' message is returned as the Ajax responseText to be inserted into a paragraph in the 'login.jsp' page.
I have everything working, my login servlet accesses the database via a separate bean, and an object of that bean is returned with its properties ready to use. So all is good to there.
But, after the username and password pass muster with the database, I'm then using RequestDispatcher to forward to the new landing page (search.jsp).
Here is my doPost()
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username,password;
username = request.getParameter("p");
password = request.getParameter("q");
try {
LoginService ls = new LoginService(username,password);
User user = ls.getUserDetails();
if(user.getUsername()!=null && user.getPassword()!=null){
FormService filler = new FormService();
Form fields = filler.getFields();
request.setAttribute("user",user);
request.setAttribute("fields1",fields);
request.setAttribute("fields2",fields);
request.setAttribute("fields3",fields);
HttpSession session = request.getSession(true);
//set attribute for the session
session.setAttribute("user",user.getUsername());
//Now, the RequestDispatcher.forward() is not forwarding to the new page!
//The whole 'search.jsp' page is being stuffed back into the 'login.jsp' page
RequestDispatcher rd = request.getRequestDispatcher("search.jsp");
rd.forward(request,response);
return;
}
else{
PrintWriter out = response.getWriter();
out.println("login failed!");
return;
}
} catch (Exception e) {
e.printStackTrace();
}
}
But instead of forwarding the request and response to the new jsp page 'search.jsp', the whole search.jsp page is being stuffed back into the the original login.jsp page - in the html element which holds the Ajax responseText in when login fails.
The forward() method in the servlet works when the servlet is called from the form action attribute, but not when the servlet is called the javascript file containing the Ajax code.
But instead of forwarding the request and response to the new jsp page 'search.jsp', the whole search.jsp page is being stuffed back into the the original login.jsp page - in the html element which holds the Ajax responseText in when login fails.
That's indeed the expected behaviour. You're handling the request/response using JavaScript. Your JavaScript code has retrieved the response of search.jsp as responseText and is putting it in the HTML element.
You need to change this approach. You need to let the response return the necessary data which sufficiently informs JavaScript so that it can handle the response properly. A commonly used data format for this is JSON.
Something like
response.setContentType("application/json");
if (user != null) {
// ...
response.getWriter().write("{ 'success': true, 'location': 'search.jsp' }");
} else {
response.getWriter().write("{ 'success': false, 'message': 'Unknown login' }");
}
and in JS:
var responseJson = eval('(' + xhr.responseText + ')');
if (responseJson.success) {
window.location = responseJson.location;
} else {
document.getElementById('message').innerHTML = responseJson.message;
}
If you want to handle this unobtrusively, so that the same servlet is reuseable on normal (non-ajax) HTTP requests (so that your webapp still works when the client has JS disabled!) then you could check if the X-Requested-With header equals to XmlHTTPRequest.
if ("XMLHttpRequest".equals(request.getHeader("X-Requested-With")) {
// Handle ajax response (e.g. return JSON data object).
} else {
// Handle normal response (e.g. forward and/or set message as attribute).
}
See also:
How to use Servlets and Ajax?
Simple calculator with JSP/Servlet and Ajax

JSF 2.0: cookies are not persisted immediately after AJAX call, HTTP request required

Ajax call performed in order to remove item from shopping cart - removeOrder() method is called
UI removeOrder() call(JSF&Primefaces):
<p:commandButton value="clean" actionListener="#{showProducts.removeOrder}"
process="#form" update="#form,:ccId:cCart:ccSizeId,:ccId:cCart:ccTotId" immediate="true">
<f:attribute name="remove" value="#{cart.name}"/>
</p:commandButton>
Backend removeOrder() call(managed bean)
public void removeOrder(ActionEvent e) {
String productName = (String) e.getComponent().getAttributes().get("remove");
Product p = getProductByName(productName);
inCart.remove(p);
persistCookies();
emptyCartNotifier();
totalRendered();
}
Here cookies is persisted,output of this method as is expected,Cookie array contains cookies with empty values,that's OK:
private void persistCookies() {
HttpServletResponse httpServletResponse = (HttpServletResponse) FacesContext.getCurrentInstance().getExternalContext().getResponse();
String ids = "";
for (Product prod : inCart) {
// TODO change logic to support real count,for now 1 is available only
ids += prod.getId() + ";" + prod.getCount() + "_";
}
Cookie cookie = new Cookie(SC_COOKIE, ids);
Cookie cookie2 = new Cookie(SC_SIZE, String.valueOf(inCart.size()));
Cookie cookie3 = new Cookie(SC_TOTAL_PRICE, String.valueOf(subTotal));
cookie3.setPath("/");
cookie3.setMaxAge(TWO_WEEKS);
httpServletResponse.addCookie(cookie3);
cookie.setPath("/");
cookie.setMaxAge(TWO_WEEKS);
cookie2.setPath("/");
cookie2.setMaxAge(TWO_WEEKS);
httpServletResponse.addCookie(cookie);
httpServletResponse.addCookie(cookie2);
}
Here problem occurred, the method emptyCartNotifier() see non-empty "previous" Cookies array
private String emptyCartNotifier() {
HttpServletRequest httpServletRequest = (HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest();
Cookie[] cookies = httpServletRequest.getCookies();
boolean isCookiePresent = false;
if (cookies != null) {
for (Cookie c : cookies) {
if (SC_COOKIE.equals(c.getName()) && (!c.getValue().isEmpty())) {
isCookiePresent = true;
}
}
}
if (inCart.isEmpty() && (!isCookiePresent)) {
emptyCartNotifier = "you cart is empty";
isFormRendered = false;
} else {
emptyCartNotifier = "";
isFormRendered = true;
}
return emptyCartNotifier;
}
After any HTTP request performed, that Cookie array is really cleaned up.
As I see , clash is:after AJAX call cleans cookie, that HttpServletRequest contains non-empty cookie until new HTTP request performed(user submit button or go by link).
Is there solution or good practice for immediate cookie management,when web-app combines AJAX and non-AJAX calls?
Thank you.
Your cookie problem is one thing, but I think there is a much easier way to get things done.
In your page, just replace the pages you mention with this:
<p:commandButton value="clean" action="#{showProducts.removeOrder(cart)}"
process="#form" update="#form,:ccId:cCart:ccSizeId,:ccId:cCart:ccTotId" immediate="true" />
Backing bean (e.g. session scoped):
private double subtotal; // don't forget getter and setter
public void removeOrder(Product product) {
inCart.remove(product);
// calculate subtotal
}
I guess you show the subtotal and maybe list all products after calling removeOrder. Just recalculate subtotal after removing the product, and make sure the shopping cart is refreshed (update attribute).
This code is simple enough and easy to understand, and yet does everything you need. No need to "manually" manage cookies.
Wait, what? In emptyCartNotifier, You are calling getCookies on the HttpRequest object, which is supposed to contain the cookies that were in the HTTP request that triggered your method, so of course you don't see changes until the next request.
You could try setting your cookies setHttpOnly(true), but the question is why would you need "ajax-scoped" cookie persistence at all?
Why not use local variables in your view/request/session scoped beans? They're actually designed for that sort of tasks. If you want additional cookie persistence you do it in corresponding setters or action methods.

Resources