Ajax status errors while trying to refresh data - java

I need to refresh my data at certain points in my application (after a pop up form is closed). The application in essence allows users to submit forms, save data, and reopen the forms and view/edit the data.
I'm calling an ajax request from a javascript function. The ajax then calls the java function, which in debugging appears to execute without issue, but right after that's performed, I got an ajax error with status 200. I read some things online, and instead of using a POST type, changed it to a GET, but now I get a 500 status, and can't access the data anymore; my belief is that I'm being logged out.
This is the javascript/ajax function:
function refreshData(){
$.ajax({
url: "./profileEntriesAction.do",
data: "toMethod=refreshData",
type: "GET",
success: function(data){
alert('success :: ' + data);
}
});
}
On the java side (profileEntriesAction), I have:
#SuppressWarnings("unchecked")
public ActionForward refreshProfile(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response)
throws Exception{
HttpSession objSession = request.getSession();
User user = (User) objSession.getAttribute(Constants.USER_KEY);
ActionErrors errors = new ActionErrors();
ActionMessages messages = new ActionMessages();
ProfileBean pBean = (ProfileBean)objSession.getAttribute("pBean");
ProfileForm pForm = (ProfileForm)objSession.getAttribute("ProfileForm");
if (user == null)
return (mapping.findForward("logon"));
//get connection to db from the pool using a static method
Connection objConn = StartUpServlet.getPoolConnection();
try{
System.out.println("refreshProfile 2");
/////////////////////////////
/////////////////////////////
IntDAO iDAO = new IntDAO();
// get lists data
if (!pForm.isNoInts()) {
Object[] arr = iDAO.getLists(objConn, pBean.getProfileId());
pForm.setList((ArrayList<Ints>) arr[0]);
}
/////////////////////////////
/////////////////////////////
}catch(SQLException ex){
if(ex.getErrorCode() == Constants.SQL_ERROR_CODE_UNACCESSIBLE_RESOURCE && ex.getSQLState().equalsIgnoreCase(Constants.SQL_SQL_STATE_UNACCESSIBLE_RESOURCE)){
objLogger.error("DB maintenance :\n" + ex.getMessage());
errors.add(Globals.ERROR_KEY, new ActionMessage("error.db.maintenance"));
} else {
objLogger.error("Error while refreshing the profile - Profile Id "+ pBean.getProfileId()+" :\n" + ex.getMessage());
errors.add(Globals.ERROR_KEY, new ActionMessage("error.entry.refresh.profile", "Profile"));
}
if(objConn != null)
objConn.rollback();
}catch(Exception e){
objLogger.error("Error while refreshing the profile - Profile Id "+ pBean.getProfileId()+" :\n" + e.getMessage());
errors.add(Globals.ERROR_KEY, new ActionMessage("error.entry.refresh.profile", "Profile"));
if(objConn != null)
objConn.rollback();
}finally {
if(objConn!= null && !objConn.getAutoCommit())
objConn.setAutoCommit(true);
// return the connection to the pool using a static method
StartUpServlet.rtnPoolConnection(objConn);
}
if (!errors.isEmpty()) {
saveErrors(objSession, errors);
}
if(!messages.isEmpty()){
saveMessages(objSession, messages);
}
return mapping.findForward("success");
//return null;
}
I've tried commenting out the entire contents of the java function and just returning the mapping.findForward("success"); but I still get the same errors.

Related

Servlet JDBC Inserts old values too

I have an web application with an Ajax request to a Servlet. When an user clicks a button it sends an ajax request to the servlet which will have to add a list of records to the DB.
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
HttpSession session = request.getSession();
String username = session.getAttribute("username").toString();
response.setContentType("text/html");
response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate, post-check=0, pre-check=0");
response.setHeader("Pragma","no-cache");
String parameter = request.getParameter("items");
out = response.getWriter();
out.println(testVariable);
try {
Class.forName(JDBC_DRIVER);
conn = DriverManager.getConnection(DB_URL, USER, PASS);
}catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
insertRecords(parameter, username);
}
private void insertRecords(String records, String user){
ArrayList<String> items = new ArrayList<>(); //This is the list of records i want to add into DB
if(records.contains("-")){
String[] split = records.split("-");
for(String item : split){
items.add(item);
}
}
else{
items.add(records);
}
try {
out.println("LIST: " + items); //This is just for test
PreparedStatement stmt = conn.prepareStatement("INSERT INTO records(productName, productCategory, user) VALUES (?, ?, ?)");
for(String record : items) {
String parent = getParentForSubproduct(record);// This method does two selects into DB without closing the connection afterwards.
stmt.setString(1, record);
if(parent.equals(""))
stmt.setString(2, record);
else
stmt.setString(2, parent);
stmt.setString(3, user);
out.println("RECORD: " + record);//This is just for test
testVariable++;
stmt.executeUpdate();
}
if(stmt != null)
stmt.close();
conn.close();
} catch (SQLException e) {
e.printStackTrace(out);
}
out.println("TIMES EXECUTED LOOP: " + testVariable);
}
The problem is that after doing more than one insert (after calling "insertRecords" more than one time) it inserts ALL of the already inserted values + the new one . Every time. I have no idea how to resolve this. I wasted one day on this.
//EDIT: I just tested out and the loop is execute more times. After the first button(first servlet call) the output would be ""TIMES EXECUTED LOOP: 1". After the second one, the output would be: "TIMES EXECUTED LOOP: 3".
As #JacekCz mentions HTTP GET has some problems.
Here I guess that on the page something like the following (not the following) is used
<a href="#" onclick="...">
This could do a page reload twice (the href and in javascript). Other variants are possible. Also an HTML element could do a GET of almost the same URL and effect the same servlet.
With Ajax something similar could happen. The usage of a dash-separated list points to JavaScript. A bit of logging will find the cause - I hope.
Thanks to #JoopEggen tips i managed to solve the problem. There was a coding error with my Ajax. Here is the code i used:
The action on the button was to call a method where it had the ajax code for the servlet too inside:
$(function(){
var list = translateArrayToString(array);
$('#finish').on('click', function (event) {
alert("test");
$.ajax({
url : 'myServlet',
async: false,
data : {
items : list
},
success : function(responseText) {
$('#ajaxGetUserServletResponse').text(responseText);
}
});
});
});
So as you can see it had another on click event so that is why it was called more than one time. Here is the working Ajax code:
$(function(){
var list = translateArrayToString(array);
alert("test");
$.ajax({
url : 'MyServlet',
async: false,
data : {
items : list
},
success : function(responseText) {
$('#ajaxGetUserServletResponse').text(responseText);
}
});
});

How to authenticate logged in user when refreshing single page application using AngularJS without "Routing"?

I searched a lot of resources but none was appropriate to my problem.I am working on single page application (SPA) project ,and I want a logged in user to stay logged in whenever he refreshes the page but without routing.
I have tried to call session authentication servlet in the main controller of the page(this servlet checks whether the session exists or not),but it did not work.
Note: The session is created once the user log in or sing up.
Here is SessionAuthServlet.java:
HttpSession session = request.getSession(true);
User u=(User) session.getAttribute("usersession");
try{
response.setContentType("application/json; charset=UTF-8");
PrintWriter out = response.getWriter();
if(u != null)
{
out.println("{\"+success+\"}");
out.close();
}
else
{
out.println("{ \"result\": \"fail\"}");
out.close();
}
}catch (IOException e) {
e.printStackTrace();
}
MainController in HTML single page application:
appvar.controller('MianController',['$scope','$http','$rootScope',function($scope, $http,$rootScope) {
$rootScope.sessionvalid=function(){
$http.get("http://localhost:8080/MyProject/SessionAuthServlet")
.success(function(response) {
if (response.result=="fail")
{
//***Show the view for not logged user
}
//***Show the view for logged user
}
$rootScope.sessionvalid();
});
}
}]);
Any ideas how to deal with this?
Please guide me
Thanks
Here is how you can stay logged after page refresh without using routing.
You will need below three things
A angular service to hold user information and if he is authenticated or not.
A window sessionstorage to save user information. Even if the page is refereshed the user information will persist in sessionstorage
An interceptor to set request and response.
Service code -
app.service('AuthenticationService', function() {
var auth = {
isLogged: false,
email:"",
isAdmin:false
}
return auth;
});
In your MainController, once user is logged in set the Service AuthenticationService.isLogged = true and $window.sessionStorage = userInfo
Interceptor code-
app.service('TokenInterceptor', function ($q, $window, $location, AuthenticationService) {
return {
request: function (config) {
config.headers = config.headers || {};
if ($window.sessionStorage.token) {
config.headers.Authorization = 'Bearer ' + $window.sessionStorage.token;
}
return config;
},
/* Set Authentication.isAuthenticated to true if 200 received */
response: function (response) {
if (response != null && response.status == 200 && $window.sessionStorage.token && !AuthenticationService.isAuthenticated) {
AuthenticationService.isAuthenticated = true;
}
return response || $q.when(response);
}
};
});
and in your app.config block add this -
app.config(function($httpProvider){
$httpProvider.interceptors.push(TokenInterceptor);
})
Now your AuthenticationService.isLogged will remain true even if the page is refershed and you can get the logged in user info in the service.

How can I update my session scoped data model in a front controller java servlet web app page, without reloading the web page?

I have a MVC- structured web application. I have both application-, session-, and request scoped data, And I use a custom made Request Based MVC framework, similar to the ones in Spring and Struts. I have used this question's answer as a tutorial.
I have a java object called ShowModel, which is passed as session scoped data. I use this to keep track of the users selection of visible components on the webpages.
All of the possible visibility selections are represented by a checkbox. They are all set to default of visible/checked when first setting the session data.
I have a listener on all of the checkboxes, that registers change, by class name "toggle", and sends it's id and checked/unchecked status by ajax to the server/servlet. See code example 1. I want to state that my experience with ajax is very limited.
As all of my calls are intercepted by my Front Controller Servlet, I needed to make a corresponding action, to execute the ajax POST-request. This code has been successfully reached, and executed. See code example 2.
My issue, however, is that my action pattern forces redirections. And in some mysterious way, the ajax object responsetext turns out to be the entire html of my index page.
My datamodel is already updated, but as it turns out, this is a faulty approach, due to the front controller strategy pattern.
So does anyone know of a way I can update my session scoped object's variables, without reloading the entire page?
Code example 1
$(document).ready(
function() {
$('.toggle').change(function() {
var id = this.value;
var checked = this.checked;
var json = new Object();
json.id = id;
json.checked = checked;
$.ajax({
url: "selectionmodelupdate",
type: 'POST',
dataType: 'json',
data: JSON.stringify(json),
contentType: 'application/json',
mimeType: 'application/json',
success: function (data) {
$('.event').each(function (index, event) {
//Here I will put code, to update the ".event"'s to be visible or not
});
},
error:function(data,status,er) {
console.log(arguments);
alert("error: "+data+" status: "+status+" er:"+er);
}
});
});
});
});
Code Example 2
public class SelectionModelUpdateAction implements Action {
#Override
public String execute(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpSession session = request.getSession();
ShowModel showModel = (ShowModel) session.getAttribute("showmodel");
AppData appData = AppData.getInstance();
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
BufferedReader br = new BufferedReader(new InputStreamReader(request.getInputStream()));
String json = "";
if(br != null){
json = br.readLine();
}
JsonObject jsonobject = new JsonParser().parse(json).getAsJsonObject();
boolean checked = jsonobject.get("checked").getAsBoolean();
String id = jsonobject.get("id").getAsString();
if(id.equals("A")){
showModel.setASelected(checked);
response.getWriter().write("{isSuccess: true}");
return "index";
}
else if (id.equals("B")){
showModel.setBSelected(checked);
response.getWriter().write("{isSuccess: true}");
return "index";
}
else if (id.equals("C")){
showModel.setCSelected(checked);
response.getWriter().write("{isSuccess: true}");
return "index";
}
response.getWriter().write("{isSuccess: false}");
return "index";
}
}

How to response a proper server execution to Ajax Post method?

I have a very simple task in my application, wich retrieves data from a combobox in after its selection event.
When select event is raised, data is passed to a jQuery function which requests opperation from server side.
All these stuffs are ok, but on processing server's response, Ajax receives a fail condition to the request. As I'm a rookie in jQuery and Ajax, I didn't know what I should be missing or doing wrong.
Jsp piece:
<input id="updStatus"
class="easyui-combobox"
name="updStatus"
data-options="
valueField:'id',
textField:'Name',
url:'StatusListCombo',
onSelect:function updStatusTask(row) {
$.fn.updStatusTask(row.id, ${task.id});
}">
jQuery function:
$.fn.updStatusTask = function (idStatus, idTask) {
var result = $.post("TaskUpdateStatus", {
idTask : idTask,
idStatus : idStatus
});
result.done(function( data ) {
alert("data: " + data); //<--NOT REACHED!!
});
result.fail(function(jqXHR, textStatus) {
alert("fail data: " + textStatus); //FIRED
});
result.always(alert("always"));//FIRED
};
Serverside method:
#RequestMapping(value = "/TaskUpdateStatus")
public String TaskUpdateStatus(Long idTask, Long idStatus, HttpSession httpSession, HttpServletResponse resp) throws IOException {
String result = new String();
try {
//... do DAO opperations to persist, everything Ok...
resp.setStatus(200);
result = "Task successfully updated.";
}
catch(Exception e) {
result = "Error: " + e.getMessage();
}
return result;
}
I would start by annotating
public String TaskUpdateStatus(#RequestParam("idTask") Long idTask, #RequestParam("idStatus") Long idStatus, ...
otherwise Spring doesn't know where to get those fields and inject them.
The only problem was the String parameter returned by Controller. It was enough change controller type TaskUpdateStatus from String to void, and, obviously, supress the return statement.
Rather, the right way to return a text to the jQuery event handler, to be processed and showed, is using response.getWritter().write("something").
#RequestMapping(value = "/TaskUpdateStatus")
public void TaskUpdateStatus(Long idTask, Long idStatus, HttpSession httpSession, HttpServletResponse resp) throws IOException {
String result = new String();
try {
//... do DAO opperations to persist, everything Ok...
resp.getWriter().write("Status changed from "
+ oldStatusName + " to " + newStatusName);
resp.setStatus(200);
}
catch(Exception e) {
//... take care of exceptions
}
}
No errors is thrown and datum is correctly exhibited in Ajax post .done event handler.

How to submit a serialised object from an Applet, via a servlet, to a backing bean then open a results JSF page

I am an applications programmer doing my first JSF 2.0 web site and confess that I don't know as much as I should about JSF. I have been pooring over documents for months and, thanks to these forums in particular, have not gotten stuck up to this point. The bulk of the web site is finished and working and the backing bean used here is used elsewhere without problems.
I have a serialised search criteria object that needs to be submitted from an applet to a backing bean, via a servlet. A backing bean method then processes the search criteria data and applies it to a list of products, held in a database, and then displays the list of ranked products in a new JSF page.
All attempts to open a results page with the correct data have failed. The navigation-case "return "process_MainSearchResult";" is not doing anything in the backing bean (see backing bean code down further and faces-config entry). I have tried opening a results page from the applet using appletContext.showDocument (see below) but the new page does not have the backing bean that the search criteria object was passed to and therefore none of the correct data.
Note that setting POST on the setRequestMethod in the applet has no effect; it always uses a service. If setDoInput and setDoOutput are not set to true and a response sent from the servlet back to the applet then the submitted object is not sent and nothing happens. I suspect that this is at the core of my problems but altering it in any way stops the serialised object from being submitted successfully.
As it stands, the object is successfully sent to the backing bean but the new page will not load with the correct data (using the showDocument in the applet rather than the redirect in the servlet). Is there a way of setting the original backing bean on the new web page or am I doing this all wrong?
Web server is Glassfish 3.x, IDE is Netbeans 7.0.1, System is WinXP.
Backing bean class is 'ProductSelection'; servlet class is 'CriteriaServlet'.
Applet "Submit Search Criteria" button code:
private void jButton8ActionPerformed(java.awt.event.ActionEvent evt)
{
criteriaModel.loadCodeBase();
int choice = JOptionPane.showConfirmDialog(this,
"Are you sure you want to submit your search criteria and exit the \"Customise Search Criteria\" web page?",
"Confirm Submit",
JOptionPane.YES_NO_OPTION,
JOptionPane.QUESTION_MESSAGE);
if (choice == 0)
{
try
{
URL url;
url = new URL(criteriaModel.getCodeBase(), "CriteriaServlet");
System.out.println("Servlet address is: " + url);
// Get the search criteria object.
Object searchSubmitObject = criteriaModel.getObjectSlideData();
HttpURLConnection connection = (HttpURLConnection)url.openConnection();
connection.setDoInput(true);
connection.setDoOutput(true);
connection.setUseCaches(false);
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Type","application/x-java-serialized-object");
ObjectOutputStream out = new ObjectOutputStream(connection.getOutputStream());
out.writeObject(searchSubmitObject);
out.flush();
out.close();
out.close();
System.out.println("Object Written");
// If this and the corresponding servlet response code is removed
// then the searchSubmitObject fails to be sent.
ObjectInputStream in = new ObjectInputStream(connection.getInputStream());
String response = (String)in.readObject();
System.out.println(response);
in.close();
}
catch (MalformedURLException ex)
{
JOptionPane.showMessageDialog(jPanel8, "Submit criteria file Malformed URL."
+ ex.toString());
System.out.println("MalformedURLException occurred");
Logger.getLogger(CriteriaInterfaceView.class.getName()).log(Level.SEVERE, null, ex);
}
catch (Exception e)
{
System.out.println("Submit criteria file ERROR exception: " + e.toString());
JOptionPane.showMessageDialog(jPanel8, "Submit criteria file ERROR exception:"
+ e.toString());
}
}
// This opens a new page but with a new backing bean with the wrong data.
try
{
appletContext.showDocument(new URL(criteriaModel.getCodeBase()+"MainSearchResult.xhtml"),"_SELF");
}
catch (MalformedURLException ex)
{
Logger.getLogger(CriteriaInterfaceView.class.getName()).log(Level.SEVERE, null, ex);
}
}
I have tried redirecting in the servlet using redirect(url) with no success:
#Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException
{
System.out.println("service(ServletRequest req, ServletResponse res)");
res.setContentType("application/x-java-serialized-object");
try
{
ObjectInputStream in = new ObjectInputStream(req.getInputStream());
slideData = (MultipleSlideDataObject2)in.readObject();
in.close();
if(slideData != null)
{
System.out.println("Serial number of submitted slide series is: " + slideData.getSerialNumber());
}
String temp = "Criteria file Recieved";
ObjectOutputStream outputToApplet = new ObjectOutputStream(res.getOutputStream());
outputToApplet.writeObject(temp);
outputToApplet.flush();
outputToApplet.close();
}
catch (ClassNotFoundException ex)
{
Logger.getLogger(CriteriaServlet.class.getName()).log(Level.SEVERE, null, ex);
}
FacesContext facesContext = FacesUtil.getFacesContext(req, res);
// Get the backing bean.
ProductSelection productSelection = (ProductSelection) facesContext.getApplication().evaluateExpressionGet(facesContext, "#{productSelection}", ProductSelection.class);
productSelection.submitSearchCriteriaFile(slideData);
// This throws an java.lang.IllegalStateException error.
try
{
FacesContext context = FacesContext.getCurrentInstance();
ExternalContext extContext = context.getExternalContext();
String url = extContext.encodeActionURL(context.getApplication().getViewHandler().getActionURL(context, "/MainSearchResult.xhtml"));
extContext.redirect(url);
}
catch (IOException e)
{
throw new FacesException(e);
}
Gives the following error because I suspect the current response has already been committed :
WARNING: StandardWrapperValve[CriteriaServlet]: PWC1406: Servlet.service() for servlet CriteriaServlet threw exception
java.lang.IllegalStateException
at org.apache.catalina.connector.ResponseFacade.sendRedirect(ResponseFacade.java:522)
at com.sun.faces.context.ExternalContextImpl.redirect(ExternalContextImpl.java:572)
at searchselection.CriteriaServlet.service(CriteriaServlet.java:217)
at org.apache.catalina.core.StandardWrapper.service(StandardWrapper.java:1523)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:279)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:188)
at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:641)
at com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:97)
at com.sun.enterprise.web.PESessionLockingStandardPipeline.invoke(PESessionLockingStandardPipeline.java:85)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:185)
at org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:325)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:226)
at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:165)
at com.sun.grizzly.http.ProcessorTask.invokeAdapter(ProcessorTask.java:791)
at com.sun.grizzly.http.ProcessorTask.doProcess(ProcessorTask.java:693)
at com.sun.grizzly.http.ProcessorTask.process(ProcessorTask.java:954)
at com.sun.grizzly.http.DefaultProtocolFilter.execute(DefaultProtocolFilter.java:170)
at com.sun.grizzly.DefaultProtocolChain.executeProtocolFilter(DefaultProtocolChain.java:135)
at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:102)
at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:88)
at com.sun.grizzly.http.HttpProtocolChain.execute(HttpProtocolChain.java:76)
at com.sun.grizzly.ProtocolChainContextTask.doCall(ProtocolChainContextTask.java:53)
at com.sun.grizzly.SelectionKeyContextTask.call(SelectionKeyContextTask.java:57)
at com.sun.grizzly.ContextTask.run(ContextTask.java:69)
at com.sun.grizzly.util.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:330)
at com.sun.grizzly.util.AbstractThreadPool$Worker.run(AbstractThreadPool.java:309)
at java.lang.Thread.run(Thread.java:619)
The return "process_MainSearchResult"; in the backing bean does not work :
public String submitSearchCriteriaFile(MultipleSlideDataObject2 slideData) throws IOException
{
System.out.println("Recieved slide series with serial number: " + slideData.getSerialNumber());
// If there is no slide data then...
if (slideData == null)
{
return "process_MainSearchResultFailed";
}
else
{
rankProducts(slideData);
}
rowStart = 0;
currentStartPage = 0;
currentPageIndex = 0;
calculateNumberPages();
SetupPaginationValues();
// Ignores this...
return "process_MainSearchResult";
}
Faces-config.xml entry:
<navigation-rule>
<navigation-case>
<from-outcome>process_MainSearchResult</from-outcome>
<to-view-id>/MainSearchResult.xhtml</to-view-id>
</navigation-case>
</navigation-rule>
I have also tried this in the backing bean to force a redirect:
FacesContext context = FacesContext.getCurrentInstance();
HttpServletResponse response = (HttpServletResponse)context.getExternalContext().getResponse();
try
{
response.sendRedirect("MainSearchResult.xhtml");
//response.redirect("http://localhost:8080/SearchEngineServer/faces/MainSearchResult.xhtml");
}
catch (IOException e)
{
e.printStackTrace();
}
return null;
and also this in the backing bean:
redirectToPage("/MainSearchResult.xhtml");
Which calls this method:
private void redirectToPage(String toUrl)
{
try
{
FacesContext ctx = FacesContext.getCurrentInstance();
ExternalContext extContext = ctx.getExternalContext();
String url = extContext.encodeActionURL(ctx.getApplication().getViewHandler().getActionURL(ctx, toUrl));
extContext.redirect(url);
}
catch (IOException e)
{
throw new FacesException(e);
}
}
All give the same java.lang.IllegalStateException error as for the servlet example given above. The documentation for redirect states this:
IllegalStateException - if, in a portlet environment, the current response object is a RenderResponse instead of an ActionResponse
IllegalStateException - if, in a servlet environment, the current response has already been committed
The redirect has failed because you already wrote and committed the response before performing the redirect in the servlet. You seem to think that you can send multiple responses on a single request. This is actually not true. You can send only one HTTP response back per HTTP request, not more. Remove that whole block starting with the FacesUtil#getFacesContext() line. It doesn't belong there.
I'm not sure what that servlet is doing, it doesn't seem to do anything useful, but you should instead let the applet itself perform the "redirect" by AppletContext#showDocument() after having called the servlet. You can pass the search criteria (the properties of the Java object instance you're trying to serialize) to the JSF page/bean as GET request parameters the usual way and have JSF to collect it by #ManagedProperty or <f:viewParam> and process it by #PostConstruct or <f:event>.
E.g.
String query = "?param1=" + URLEncoder.encode(param1, "UTF-8")
+ "&param2=" + URLEncoder.encode(param2, "UTF-8")
+ "&param3=" + URLEncoder.encode(param3, "UTF-8");
getAppletContext().showDocument(new URL(getCodeBase(), "MainSearchResult.xhtml" + query), "_SELF");
with either
#ManagedBean
#RequestScoped
public class ProductSelection {
#ManagedProperty("#{param.param1}")
private String param1;
#ManagedProperty("#{param.param2}")
private String param2;
#ManagedProperty("#{param.param3}")
private String param3;
#PostConstruct
public void init() {
// Do your business job based on the submitted request parameters.
}
// ...
}
or
<f:metadata>
<f:viewParam name="param1" value="#{productSelection.param1}" />
<f:viewParam name="param2" value="#{productSelection.param2}" />
<f:viewParam name="param3" value="#{productSelection.param3}" />
<f:event type="preRenderView" listener="#{productSelection.init}" />
</f:metadata>
When implementing it this way, then I think that the clumsy servlet step with all that Java serialization is entirely superfluous. You could just remove it. Also, this way you end up with a nicely bookmarkable, reuseable and searchbot-indexable page which can be opened independently from the applet/servlet.
See also:
ViewParam vs #ManagedProperty(value = "#{param.id}")
Communication in JSF 2.0 - Processing GET request parameters
BalusC's answer is the appropriate answer for small amounts of object data, however, the object I am submitting to the backing bean is 2.2 megabytes and not suitable for encoding in the URL. Further more I don't want people bookmarking this particular page as more attributes may be added to the search criteria file in the future and this would make the book mark properties invalid.
The solution I am using is very low tech but it works. The applet submits the serialised object to the servlet, which in turn passes it to the backing bean, and then returns a fail or succeed message to the applet. If the submission succeeds then the applet calls a javascript function on the web page to load the results page. This ensures that the correct backing bean is retained.
The final code is as follows:
Applet "Submit Search Criteria" button code:
private void jButton8ActionPerformed(java.awt.event.ActionEvent evt)
{
criteriaModel.loadCodeBase();
int choice = JOptionPane.showConfirmDialog(this,
"Are you sure you want to submit your search criteria and exit the \"Customise Search Criteria\" web page?",
"Confirm Submit",
JOptionPane.YES_NO_OPTION,
JOptionPane.QUESTION_MESSAGE);
if (choice == 0)
{
try
{
URL url = new URL(criteriaModel.getCodeBase(), "CriteriaServlet");
System.out.println("Servlet address is: " + url);
Object searchSubmitObject = criteriaModel.getObjectSlideData();
HttpURLConnection connection = (HttpURLConnection)url.openConnection();
connection.setDoInput(true);
connection.setDoOutput(true);
connection.setUseCaches(false);
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Type","application/x-java-serialized-object");
ObjectOutputStream out = new ObjectOutputStream(connection.getOutputStream());
out.writeObject(searchSubmitObject);
out.flush();
out.close();
out.close();
System.out.println("Object Written");
ObjectInputStream in = new ObjectInputStream(connection.getInputStream());
String response = (String)in.readObject();
System.out.println(response);
in.close();
if(response.equals("Failed"))
{
JOptionPane.showMessageDialog(jPanel8, "Submit Search criteria file to server failed.\n Try Again later.");
}
else
{
getAppletContext().showDocument(new URL("javascript:openResultsPage()"));
}
}
catch (MalformedURLException ex)
{
JOptionPane.showMessageDialog(jPanel8, "Submit criteria file Malformed URL."
+ ex.toString());
System.out.println("MalformedURLException occurred");
Logger.getLogger(CriteriaInterfaceView.class.getName()).log(Level.SEVERE, null, ex);
}
catch (Exception e)
{
System.out.println("Submit criteria file ERROR exception: " + e.toString());
JOptionPane.showMessageDialog(jPanel8, "Submit criteria file ERROR exception:"
+ e.toString());
}
}
}
In the servlet:
#Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException
{
System.out.println("service(ServletRequest req, ServletResponse res)");
res.setContentType("text/plain");
try
{
ObjectInputStream in = new ObjectInputStream(req.getInputStream());
slideData = (MultipleSlideDataObject2)in.readObject();
in.close();
String reply = "Failed";
if(slideData != null)
{
System.out.println("Serial number of submitted slide series is: " + slideData.getSerialNumber());
FacesContext facesContext = FacesUtil.getFacesContext(req, res);
ProductSelection productSelection = (ProductSelection) facesContext.getApplication().evaluateExpressionGet(facesContext, "#{productSelection}", ProductSelection.class);
productSelection.submitSearchCriteriaFile(slideData);
reply = "Success";
}
ObjectOutputStream outputToApplet = new ObjectOutputStream(res.getOutputStream());
outputToApplet.writeObject(reply);
outputToApplet.flush();
outputToApplet.close();
}
catch (ClassNotFoundException ex)
{
Logger.getLogger(CriteriaServlet.class.getName()).log(Level.SEVERE, null, ex);
}
}
In the backing bean:
public String submitSearchCriteriaFile(MultipleSlideDataObject2 slideData) throws IOException
{
System.out.println("Recieved slide series with serial number: " + slideData.getSerialNumber());
// If there is no slide data then...
if (slideData == null)
{
return "process_MainSearchResultFailed";
}
else
{
rankProducts(slideData);
}
return "process_MainSearchResult";
}
In the header of the JSF page, which contains the applet:
<SCRIPT language="javascript">
function openResultsPage()
{
window.location = "MainSearchResult.xhtml";
}
</SCRIPT>
FacesUtil based on BalusC's FacesUtil class (couple of minor changes to the request and response types) Used to get the backing bean in the servlet:
package searchselection;
import javax.faces.FactoryFinder;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
import javax.faces.context.FacesContextFactory;
import javax.faces.lifecycle.Lifecycle;
import javax.faces.lifecycle.LifecycleFactory;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
public class FacesUtil
{
// Getters -----------------------------------------------------------------------------------
public static FacesContext getFacesContext(
ServletRequest request, ServletResponse response)
{
// Get current FacesContext.
FacesContext facesContext = FacesContext.getCurrentInstance();
// Check current FacesContext.
if (facesContext == null) {
// Create new Lifecycle.
LifecycleFactory lifecycleFactory = (LifecycleFactory)
FactoryFinder.getFactory(FactoryFinder.LIFECYCLE_FACTORY);
Lifecycle lifecycle = lifecycleFactory.getLifecycle(LifecycleFactory.DEFAULT_LIFECYCLE);
// Create new FacesContext.
FacesContextFactory contextFactory = (FacesContextFactory)
FactoryFinder.getFactory(FactoryFinder.FACES_CONTEXT_FACTORY);
facesContext = contextFactory.getFacesContext(
request.getServletContext(), request, response, lifecycle);
// Create new View.
UIViewRoot view = facesContext.getApplication().getViewHandler().createView(
facesContext, "");
facesContext.setViewRoot(view);
// Set current FacesContext.
FacesContextWrapper.setCurrentInstance(facesContext);
}
return facesContext;
}
// Helpers -----------------------------------------------------------------------------------
// Wrap the protected FacesContext.setCurrentInstance() in a inner class.
private static abstract class FacesContextWrapper extends FacesContext {
protected static void setCurrentInstance(FacesContext facesContext) {
FacesContext.setCurrentInstance(facesContext);
}
}
}

Categories

Resources