Hi there,

I wanted to get input on a matter of thread safety for a servlet
that processes online tests. The basic question is, "Should I implement
the SingleThreadModel on the servlet since many students maybe
trying to submit their tests symultaneously for processing?" The servlet
does contain three instance variables which perhaps need to remain as such
so that an inner class and statements in the doPost(...) method
have access to them. Please advise if changes are necessary.

Servlet Structure and Business Logic:

The servlet contains two inner classes named Answer and XMLAnswerHandler.
A backend database contains the answers
to the tests in the form of xml markup. To process an online test,
in the doPost(...) method, I read in the xml data from the database
using the XMLAnswerHandler class which implements
org.xml.sax.helpers.DefaultHandler. XMLAnswerHandler
is used to create an instance variable ArrayList named "answers" to
contain multiple instances of class Answer. Class Answer contains
an item identifier and the actual answers to a particular test
question (item). Also, in the doPost(...) method I load up another
instance variable ArrayList named "studentAnswers" again with
multiple instances of class Answer. I then, compare the answers
given by the student with the answers in the answers ArrayList to
determine a final score.

Regards,

Alan


The servlet is as follows:

Code:
package mvcs;

import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import java.util.*;
import org.xml.sax.*;
import org.xml.sax.helpers.*;


public class TestProcessor extends HttpServlet
{
    

    private static ResourceBundle sql_bundle = ResourceBundle.getBundle("mvcs.testprocessor.URLs");

    public final static String rootElementName = "test";
    public final static String testIDElementName = "testid";
    public final static String TestTitleElementName = "title";
    public final static String QuestionCountElementName = "qcount";
    public final static String AnswersElementName = "answers";
    public final static String AnswerElementName = "answer";
    public final static String ItemElementName = "item";
    public final static String ValueElementName = "value";
    public final static String DTDURL = sql_bundle.getString("dtd"); 
    public final static String StyleURL = sql_bundle.getString("style");

    private ArrayList<Answer> answers = null;
    private ArrayList<Answer> studentAnswers = null;
    private int QuestionCount = 0;
      
    

    public void doGet(HttpServletRequest req, HttpServletResponse res)throws ServletException, IOException
    {
       res.setContentType("text/html");
       PrintWriter out = res.getWriter();
       out.println(ServletUtilities.headWithTitle("Access Denied") +
                    "<body>\n" +
                    "<h1>ACCESS DENIED</h1>\n" +
                    "</body>\n</html>");	
    }

    public void doPost(HttpServletRequest req, HttpServletResponse res)throws ServletException, IOException
    {
       String role = "";
       String instructorID = "";
       String studentID = "";
       String testID = "";
       String title = "";
       String companyID = "";

       role = req.getParameter("role");
       
       if(role == null || role.equals(""))
       {
         res.setContentType("text/html");
         PrintWriter out = res.getWriter();
         out.println(ServletUtilities.headWithTitle("Access Denied") +
                    "<body>\n" +
                    "<h1>ACCESS DENIED</h1>\n" +
                    "</body>\n</html>");
         return;
       }

       if(role.equals("instructor"))
       {
         ...
         ...
       }
       else if(role.equals("student"))
       {
         companyID = req.getParameter("companyID");
         studentID = req.getParameter("studentID");
         testID = req.getParameter("testID");
         int passMark = TestsModel.getPassMark(testID);
         ...
         ...
         try
         {
           //Build an ArrayList with Answer objects
           String xmlString = TestsModel.getAnswers(testID);
           //System.out.println(xmlString);
           XMLReader parser = XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser");
           org.xml.sax.ContentHandler handler = new XMLAnswerHandler();
           parser.setContentHandler(handler);
           InputSource source = new InputSource(new StringReader(xmlString));
           parser.parse(source);

           Answer temp = null;
           
           //Load up the students' answers into studentAnswers ArrayList
           studentAnswers = new ArrayList<Answer>();
           Enumeration paramNames = req.getParameterNames();
           String paramName = "";
           String[] paramValues = null;
           while(paramNames.hasMoreElements())
           {
             paramName = (String)paramNames.nextElement();
             if(paramName.charAt(0) == 'Q')
             {
               paramValues = req.getParameterValues(paramName);
               //NOTE that we DO NOT filter any illegal
               //characters from the students' answers.
               //The instructors' answers have been reverse_filtered
               //during the xml parsing process, therefore,
               //we'll be comparing the raw text from both sources.
               for(int i = 0; i < paramValues.length; i++)
               {
                 paramValues[i] = paramValues[i].trim();
               }
               studentAnswers.add(new Answer(paramName, paramValues));             
             }
           }
           Collections.sort(studentAnswers);

           //Once the parsing is completed, we should have a Question Count
           System.out.println("QCount is: " + QuestionCount);

           //At this point, we have both the instructors' answers
           //and the students' answers.  We need to compare the students'
           //answers with the instructors' answers and allocate one point
           //for every correct answer.

           Answer instructorsAnswer = null;
           Answer studentsAnswer = null;
           String item = "";
           String[] values1 = null;
           String[] values2 = null;
           for(int i = 0; i < answers.size(); i++)
           {
             instructorsAnswer = answers.get(i);             
             item = instructorsAnswer.getItem();
             values1 = instructorsAnswer.getAnswers();
             //Iterate over the ArrayList of the students' answers
             //to get a handle on each item.
             for(int j = 0; j < studentAnswers.size(); j++)
             {
               studentsAnswer = studentAnswers.get(j);
               if(studentsAnswer.getItem().equals(item))
                  break;
             }
             //If after iterating over the studentAnswers ArrayList,
             //there isn't a match for the particular item in question,
             //then we'll just continue onto the next question item.
             if(!studentsAnswer.getItem().equals(item))
                continue;

             //Get the students answers
             values2 = studentsAnswer.getAnswers();

              int answersCorrect = 0;
              int answersWrong = 0;
              int score = 0; //as a percentage out of 100%

              if(values1.length == 1 && values2.length == 1)
              {
                if(values1[0].equals(values2[0]))
                   answersCorrect++;
              }
              else if(values1.length > 1 && values1.length == values2.length)
              {
                String val1 = "";
                String val2 = "";
                boolean correct = true;
                for(int k = 0; k < values1.length; k++)
                {
                  val1 = values1[k];
                  for(int l = 0; l < values2.length; l++)
                  {
                    val2 = values2[l];
                    if(val1.equals(val2))
                       break;
                  }
                  if(!val1.equals(val2))
                  {
                     correct = false;
                     break;
                  }
                }
                if(correct)
                   answersCorrect++;
              }             
              
           }//end of for loop

           //At this point, we can determine the score
           answersWrong = QuestionCount - answersCorrect;
           score = Math.round((answersCorrect*100)/QuestionCount);
           System.out.println("Number of Questions: " + QuestionCount + "\nAnswers Correct: " + answersCorrect + "\nAnswers Incorrect: " + answersWrong + "\nScore: " + score + "%");

           //reset variables
           QuestionCount = 0;
           answersCorrect = 0;
           answersWrong = 0;
           score = 0;
          
                      
         }
         catch(SAXException saxe){saxe.printStackTrace();}
         
       }       
             
   }

   class Answer implements Comparable<Answer>
   {
      ...
   }

   class XMLAnswerHandler extends DefaultHandler
   {
      ...

      public void startDocument()throws SAXException
      {
        answers = new ArrayList<Answer>();
      }

      public void startElement(String namespaceURI, String localName, String qualifiedName, Attributes atts)throws SAXException
      {
        ...
      }

      public void endElement(String namespaceURI, String localName, String qualifiedName)throws SAXException
      {
        ...     
      }

      public void characters(char[] text, int start, int length)throws SAXException
      {
        ...
      }

      public void endDocument()
      {
        ...
      }    
   }    
}