2

I have the following in which I have to write a servlet that takes an age, marital status, house income and number of kids, goes out to a DB, and then returns the updated averages to the user. However, I am encountering this stacktrace:

java.lang.NullPointerException
    at HouseSurvey$SurveyResults.access$200(HouseSurvey.java:86)
    at HouseSurvey.doPost(HouseSurvey.java:43)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:709)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:802)
    at org.apache.catalina.servlets.InvokerServlet.serveRequest(InvokerServlet.java:419)
    at org.apache.catalina.servlets.InvokerServlet.doPost(InvokerServlet.java:169)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:709)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:802)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:252)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:173)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:213)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:178)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:126)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:105)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:107)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:148)
    at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:868)
    at org.apache.coyote.http11.Http11BaseProtocol$Http11ConnectionHandler.processConnection(Http11BaseProtocol.java:663)
    at org.apache.tomcat.util.net.PoolTcpEndpoint.processSocket(PoolTcpEndpoint.java:527)
    at org.apache.tomcat.util.net.LeaderFollowerWorkerThread.runIt(LeaderFollowerWorkerThread.java:80)
    at org.apache.tomcat.util.threads.ThreadPool$ControlRunnable.run(ThreadPool.java:684)
    at java.lang.Thread.run(Unknown Source)

What the heck does this all mean? I figure I am getting a NullPointerException, but I don't see where. Here is my prog.:

import java.text.DecimalFormat;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class HouseSurvey extends HttpServlet
{
    private final static String SURVEY_FILE = "HouseSurvey.dat";
    SurveyResults results;
    Household h;
    DecimalFormat form = new DecimalFormat("#,###,#00.00");

    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
    {
        PrintWriter out = response.getWriter();  //make a printwriter to print the page
        File survey = new File(SURVEY_FILE);
        if(!survey.exists())    //check to see if the file exists
            results = new SurveyResults();
        else   
        {     //If the file exists, read it the latest survey tallies
            try
            {
                ObjectInputStream ips = new ObjectInputStream(new FileInputStream(survey));
                results = (SurveyResults)ips.readObject(); //read the file into 'results'
                ips.close();   //close the input stream
            }
            catch(ClassNotFoundException e) {e.printStackTrace();}
            catch(FileNotFoundException f)  {f.printStackTrace();}
            catch(IOException ioe)          {ioe.printStackTrace();}
        }
        int       ageValue = Integer.parseInt(request.getParameter("age")),
        childValue = Integer.parseInt(request.getParameter("children"));
        double incomeValue = Double.parseDouble(request.getParameter("income"));
        Boolean marriedValue = true;
        if (request.getParameter("status").equals("married"))
            marriedValue = true;
        else 
           if(request.getParameter("status").equals("single"))
           marriedValue = false;
        Household h = new Household(ageValue,childValue,incomeValue,marriedValue);
        results.totalResults(h);
        //Write results to disk.
        try
        {
            ObjectOutputStream ops = new ObjectOutputStream(new FileOutputStream(survey));
            ops.writeObject(results); ops.flush(); ops.close();
        }
        catch(IOException ioe)          {ioe.printStackTrace();}
        response.setContentType("text/html");       //contnent type for the responding webpage
            out.println("<html>\n"+
                "<head><title>Thanks!</title></head>"+
                "<body style='background:cyan;'>"+
                "   <h1 style='text-align:center'>RRC BIT Department - Household Survey</h1>"+
                "   <hr><br/>"+
                "   <h2 style='text-align:center'>Up to Date Survey Results</h2>"+
                "   <h4 style='margin-left:200px'>Average Age: "+form.format(results.getAvgAge())+"</h4>"+
                "   <h4 style='margin-left:200px'>Average Number of Children: "+form.format(results.getAvgKids())+"</h4>"+
                "   <h4 style='margin-left:200px'>Average Number of Married Respondents: "+form.format(results.getTotalMarried())+"</h4>"+
                "   <h4 style='margin-left:200px'>Average Number of Single Respondents: "+form.format(results.getTotalSingle())+"</h4>"+
                "   <h4 style='margin-left:200px'>Average Income: "+form.format(results.getAvgIncome())+"</h4></body>");
    }                           
    private class Household
    {
        private int age, children;
        private double income;
        private boolean married=false;
        /**Method: Household
         * Constructor
         * @ param age - age of person surveyed:int
         * @ param children - number of children person surveyed has:int
         * @ param married - true or false, used to determine married or single:boolean
         * @ param income - the family income of the person surveyed:double
         */
        private Household(int age, int children, double income, boolean married)
        {
            this.age=age; this.children=children; this.income=income; this.married=married;       
        } 
        //Getters
        private int getAge()         {return age;}
        private int getChildren()    {return children;}
        private double getIncome()   {return income;}
        private boolean getMarried() {return married;}
    }
    private class SurveyResults implements Serializable
    {
        private double totalAge, totalChildren, totalMarried, totalSingle, totalIncome;
        /**Method: SurveyResults
         * Used for totals
         * @ param h - Household object created above:Household
         */      
        private void totalResults(Household h)
        {
            totalAge += h.getAge(); totalChildren += h.getChildren(); totalIncome += h.getIncome();            
            if(h.getMarried()) totalMarried += 1;
               else
                  totalSingle += 1;
        }
        private double getTotalHouseholds() {return totalSingle + totalMarried;}
        private double getAvgAge()          {return totalAge/getTotalHouseholds();}
        private double getAvgKids()         {return totalChildren/getTotalHouseholds();}
        private double getTotalMarried()    {return totalMarried;}
        private double getTotalSingle()     {return totalSingle;}
        private double getAvgIncome()       {return totalIncome/getTotalHouseholds();}
    }
}

Originally in my HTML output, I accidentally had the line

"Average Number of Married Respondents: "+form.format(results.getTotalMarried())+"</h4>" 

as

"Average Number of Married Respondents: "+form.format(results.getAvgKids())+"</h4>"

and that worked fine. Then I switched it to the former to get the totalMarried, and now it is giving me the exception. Where is the issue?

Thanks in advance.

unit
  • 415
  • 1
  • 8
  • 15
  • Oh, sorry, line 89 is 'private class SurveyResults implements Serializable' and line 46 is ' results.totalResults(h);' – unit Apr 19 '11 at 00:11
  • Is that the entire stack trace, or just the top of it? – Brian Roach Apr 19 '11 at 00:59
  • Sorry, I think there was a bit more. Edited. There was also a trace in the stdout log. Not sure if that would help, it's obviously well over my head at this point. – unit Apr 19 '11 at 01:05
  • 1
    I'll bet the trace in stdout log is the IOException I was mentioning in my answers about the class not being Serializable. Changing everything to non-inner classes with public method access levels worked. I was running as a console app, and don't really have an environment to compile a full Servlets-based code sample at the moment though. – Sean Kleinjung Apr 19 '11 at 01:17
  • @Sean -- I didn't even have to change the access levels. Just changing the classes from being inner classes to static nested classes made the exceptions go away for me. – QuantumMechanic Apr 19 '11 at 01:30

3 Answers3

3

I am adding this as a separate answer because it is a completely different train of thought. If it would be better to edit let me know, and I will delete the duplicate.

It looks like you will be having an IOException thrown when reading the results object. The reason is, the class you are attempting to serialize is not serializable. Since SurveyResults is an inner class, to be serialized it will have to serialize the containing class as well (in this case, your Servlet). I would recommend changing your classes to static nested classes, or perhaps default scoped top-level classes. (With changes to method access levels as needed.)

You also may need to delete your survey results data file and start over, as well. In my testing, it had references to the non-serializable classes and I was not able to get the code to run until it was deleted.

Sean Kleinjung
  • 3,115
  • 2
  • 21
  • 15
  • Wow. Thanks. Sorry to be a dummy (pretty new), but what is the easiest way to delete the data file? – unit Apr 19 '11 at 01:16
  • No problem. I don't mean delete it in the code, but just open it in Windows Explorer or Finder or whatever your OS uses and physically delete it. For a webapp I'm not sure offhand where it would be -- in the root of your webapp, or maybe in WEB-INF? You named it HouseSurvey.dat, so just search for that. And no worries about being new, this was a tricky problem actually! – Sean Kleinjung Apr 19 '11 at 01:20
  • Interestingly, I took half of what you said and tried that first. I left the serializable in (for kicks, because I know my prof did it that way), and erased the file (you said it had references in it). I ran old code with the "getAvgKids()" on two lines. I wondered if it mattered that I was now trying to write getTotalMarried to one of those lines. Now it works fine. So why would it work with serializable? Your answer made a lot of sense to me. – unit Apr 19 '11 at 01:27
  • 1
    It would work if the SurveyResults class is Serializable (in fact, this needs to be the case to save them the way you are.) The problem was, when you had them as non-static inner classes SurveyResults was trying to serialize your Servlet also. If you changed SurveyResults to be a static inner class like QuantumMechanic suggested, this would no longer be the case. – Sean Kleinjung Apr 19 '11 at 01:28
1

HouseSurvey$SurveyResults.access$200() is a synthetic accessor method that the compiler generates and inserts into the inner class to allow the enclosing class (here, HouseSurvey) to call a private method of the inner class. I admit I'm not yet seeing where the NullPointerException is coming from, though.

Also, have you decided whether or not Household and SurveyResults should be inner classes (as they currently are) rather than static nested classes and does making them static nested classes change what happens? This SO question on inner vs. static nested classes might help you make that decision.

Update: I tried making this into a standalone class (only showing changes I made):

public class HouseSurvey
{
    public static void main(String[] args) {
        HouseSurvey h = new HouseSurvey();
        h.doPost(args);
    }

 public void doPost(String[] data) throws Exception {
    PrintWriter out = new PrintWriter(System.out);
    File survey = new File(SURVEY_FILE);
    if (!survey.exists()) {
        results = new SurveyResults();
    } else {
        ObjectInputStream ips = new ObjectInputStream(new FileInputStream(survey));
        results = (SurveyResults) ips.readObject();
        ips.close();
    }

    int ageValue = Integer.parseInt(data[0]);
    int childValue = Integer.parseInt(data[1]);
    double incomeValue = Double.parseDouble(data[2]);
    boolean marriedValue = Boolean.parseBoolean(data[3]);
    Household h = new Household(ageValue, childValue, incomeValue, marriedValue);
    results.totalResults(h);

    ObjectOutputStream ops = new ObjectOutputStream(new FileOutputStream(survey));
    ops.writeObject(results);
    ops.flush();
    ops.close();

    out.println("Average Age: " + form.format(results.getAvgAge()) +
            "\nAverage # of Children: " + form.format(results.getAvgKids()) +
            "\nAverage # of Married Respondents: " + form.format(results.getTotalMarried()) +
            "\nAverage # of Single Respondents: " + form.format(results.getTotalSingle()) +
            "\nAverage Income: " + form.format(results.getAvgIncome()));
    out.flush();
    out.close();
}

[snip]

}

When I ran this I got the following exception trace:

Exception in thread "main" java.io.NotSerializableException: HouseSurvey
    at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1164)
    at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1518)
    at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1483)
    at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1400)
    at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1158)
    at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:330)
    at HouseSurvey.doPost(HouseSurvey.java:36)
    at HouseSurvey.main(HouseSurvey.java:9)

which while it isn't the NullPointerException you got, it does demonstrate that there's a serialization issue that Sean alludes to is his answers. Once I change Household and SurveyResults to be static nested classes instead of inner classes, the exception went away and the program appears to work correctly.

Community
  • 1
  • 1
QuantumMechanic
  • 13,795
  • 4
  • 45
  • 66
  • I'll check it out in a bit (after I put the kids to bed). Thanks. – unit Apr 19 '11 at 00:23
  • Ah, I didn't even wait til I put the kids to bed. Didn't work, though. Same damned stackTrace, but thanks for the suggestion. – unit Apr 19 '11 at 00:25
  • 1
    Oh well. I still recommend reading the link in my answer and deciding what they should be even if it doesn't help with this problem. Have you tried extracting the body of the `doPost()` into a method (with appropriate changes) in separate, standalone class so you can run it standalone (and preferably in a debugger)? – QuantumMechanic Apr 19 '11 at 00:31
1

I have not personally used the ObjectInputStream API, but I am only seeing a couple possibilities for the NullPointerException you are listing. Is it possible for the following line to return null:

results = (SurveyResults)ips.readObject();

I am not sure if readObject can ever return null, or if it would just throw an exception if the object could not be read.

Another possibility is that an exception is being thrown before the results object is loaded. You have 3 empty catch blocks that just print exceptions -- are they being executed? (In other words, do you see other exceptions being thrown?)

Other than that, it would be possible to get a null pointer unboxing your a Boolean into a boolean (as you do with marriedValue), although in the code listed I don't see that being possible.

Sean Kleinjung
  • 3,115
  • 2
  • 21
  • 15
  • The weird thing is, I can use any of the other getters (getAvgKids,getAvgIncome, etc), and then everything works. Only when I call the getTotalMarried is there a problem. So I am wondering if there is a problem with the boolean. – unit Apr 19 '11 at 00:56
  • I put them both to lower case boolean. No change. – unit Apr 19 '11 at 00:58