unit
unit

Reputation: 415

Java problem with servlet and stacktrace thrown

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.

Upvotes: 2

Views: 586

Answers (3)

QuantumMechanic
QuantumMechanic

Reputation: 13946

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.

Upvotes: 1

Sean Kleinjung
Sean Kleinjung

Reputation: 3175

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.

Upvotes: 3

Sean Kleinjung
Sean Kleinjung

Reputation: 3175

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.

Upvotes: 1

Related Questions