CS636  Class 22 Servlets, Multithreading, Debugging

Last time: how the pizza3 servlets work.

We looked at StudentBean and how it is created for a user in StudentWelcomeController and made into a session variable, and how the DispatherServlet made sure that an incoming request is forwarded to the StudentWelcomeController if the session variable is not there.

Concurrency and Performance

Consider request-response cycles for various users:

We are assuming that each user causes a sequence of request cycles usually well separated in time. Concurrent requests come about because of different user’s requests happening to occur at the same time, as follows:


Timeline (one line segment here is mistaken) showing requests for one user over time-

 

Session object                          continuing existence of session object, until 20 minutes of idleness by user-->

Another user, another such sequence of requests, and another session object...

I added little ovals in each request lifetime to represent the domain objects created and used just for the request cycle. The session object and anything hanging off of it lasts from the first request by a user until about 20 minutes after the last request by that user.

Rough Performance Analysis




Suppose 10000 session objects, for 10000 users in last 20 minutes

1000 active sessions, each lasting 2 minutes (so 10000 in 20 min)

Each user session does 10 requests over the 2 minutes, so 10000 requests in 2 min = 120 sec

So about 100 requests per sec.

Suppose each request takes 50 ms and uses 10% of a CPU during this time

That’s 5ms of CPU time per request, so 100 req/sec * 5ms = 500 ms CPU per sec, means 50% of one CPU to handle this load, OK.

We said 50ms elapsed time per request.  There are 20 such periods in each second, in which 100 requests are positioned, so about 5 requests in each 50ms period.

So we see about 5 concurrent requests occurring at each point in time with this load. Most requests have transactions, so also about 5 concurrent transactions, each with its own Connection.

That means we are involved in multithreaded execution. But all the changeable shared domain data is held in the database, so we are using the database to do the hard work of concurrency control to shared data.  That’s one of the main secrets of good web application design.

Multi-threading: Let the DB handle concurrent access to domain data                                                  

Five concurrent transactions, each with its own thread, means we’re definitely doing a multi-threaded application.  Need to worry about concurrent access to shared data.

Web app initialization

How does a web app start up and create the needed service and DAO singletons in the web app case?

We know the servlets have an init() method that tomcat calls once when the servlet starts. Good place to call configureServices to get the system up and running.  But there are two important servlets--do they both call this? Better to ensure that only one does.

We could have the various servlets check if the service refs are available and if not, call configureServices to set them up.

Well, that would work (with a possible race condition causing duplicate « singletons »), but actually AdminServlet is config’d in web.xml to load after DispatcherServlet : See web.xml:

<servlet>

              <description>Admin Servlet</description>

              <display-name>AdminServlet</display-name>

              <servlet-name>AdminServlet</servlet-name>

              <servlet-class>cs636.pizza.presentation.web.AdminServlet</servlet-class>

              <!-- make this servlet load after the dispatcher servlet -->

              <load-on-startup>2</load-on-startup> 

</servlet>

Similarly DispatcherServlet has   <load-on-startup>1</load-on-startup>, so tomcat carefully calls init of DispatcherServlet first, and after that returns, calls init() of AdminServlet.

This sequence builds the service and DAO singleton objects with inter-references like this, as seen before in pizza1 and pizza2:



 

 

 

 

 

 

 

 

Service and DAO singletons in pizza1 through pizza3. For pizza3, they are inside the tomcat JVM.

This object graph lives on between user sessions and requests.  The singletons are themselves protected from garbage collection because of the static fields for the service objects in PizzaSystemConfig.

We added the servlet objects and their Controllers to this picture. The servlet objects are not garbage collected because they are managed by tomcat itself, so it has references to them.

More on DataSource handling in request, and domain objects

As covered last time, each request comes in and gets its own JDBC Connection from the connection pool in the DataSource residing in tomcat or instantiated from library code outside tomcat.

Recall we called the domain objects “scratch copies” of database data. They are created in the DAO for the currrent request and filled with fresh DB data. Or created in the service layer for the request's DB inserts, etc.

Either way, they are thread-private, not shared with executions of other requests in other threads.

Each request gets fresh copies of the DB data, so if an admin deletes a topping, that will be evident in following requests.

These private domain objects are important to the argument that we don’t have to worry about multithreading issues in our code. That’s a big feature!

Multithreading issues: we claim to be free of them!

We need the assumption that each session (i.e. requests from one user) involves one request at a time.

Multithreading problems come up because two threads act on the same object, i.e., the data is shared. So we have to look for objects shared between threads, i.e., requests.

--no domain objects obtained from DB, because each thread has its own domain objects

--no fields of service objects by statelessness, and immutability of DAO references

--no session variables, by one-at-a-time request assumption for each user

--we are not using “application” scope variables (this is possible, but leads to race conditions. We use the DB for shared data)

--we do share the Datasource object, but it is thread-safe, that is, has internal mutex to guard actions against race conditions.

--we do share the service singletons, but they are immutable once set up. So are the Controller singletons.

--we do share the DAO singletons, but they only have thread-safe fields.

--the servlet object is private to the thread, created for the doGet/doPost calls. So are the request and response objects

So we have an argument for all objects used in our programs. Right?

So we are doing multithreaded programming without a single mutex that we set up ourselves!

Summary: With our architecture, the database system takes care of concurrent access to shared data.  The only memory objects that receive concurrent access in our app are the API objects and Controller objects, which don’t change, and the DataSource, which is thread-safe (it has to be!). We are assuming that a single user does not have concurrent requests, so we don’t have to worry about synchronizing access to the HTTPSession object.

That’s a big win. Having multiple mutexes in code leads to the dreaded deadlock situation, when the software system freezes up, and needs true multithreaded debugging (Java has some support for this).

Debugging Servlets

Don’t forget to start your tunnels before starting tomcat, other work

Tomcat tries to connect to DBs on its way up, can fail if can’t connect

Bring down tomcat if your tunnels have stopped working, redo startup.

Also, edit context.xml only while tomcat is down.

Debugging using System.out.println
Example in servlet1: System.out.println("in doGet"); Can execute with “ant test1”

It is perfectly possible to debug music3 just using System.out.println output added in areas of problems. Also e.printStackTrace() as seen in the main error handler of SystemTest.java.

Our layers help localize behavior in expected places. If you add a println to each method outputting the method name, you can localize errors to a particular method.

System.out.println output goes to the “server log”, but this is located in different places depending on circumstances of the run.

Make sure you can find this System.out output!

Although not required, you can work faster if you use the eclipse debugger, described below. If you don't want to use it, you need not set up tomcat within eclipse. You can use eclipse as an editor, then use "ant deploy-hsqldb" (or other db) to deploy your new code to tomcat, itself started with startup.bat (Windows) or startup.sh (Mac/Linux).  Tomcat checks webapps every minute for changed webapps, and reinitializes the webapp, as you can see in the server log.

From Eclipse help, slightly edited to clarify:

Debugging a servlet running on the local system using eclipse (checked on Windows)

The debugger enables you to detect and diagnose errors in your application. It allows you to control the execution of your program by setting breakpoints, suspending threads, stepping through the code, and examining the contents of the variables. You can debug a servlet on a server without losing the state of your application.

To debug a servlet on a server:

Web attack by SQL Injection: What it is and how to avoid it

SQL Injection means a break-in to a SQL-using application (usually a webapp) by entering just the right user input, causing the SQL in the application to provide hidden data, or allow a user to log in without knowing the credentials.

There is a possible flaw the admin app of music3 that should be considered

Login UI takes in username and password for an admin user, for example user andrea, password sesame (this relates to a row in userpass).

Suppose DAO does  select count(*) from userpass where username=’andrea‘and password=’sesame

 (where the underlined parts were input by the user) and allows the user to log in if the count is positive.

Sounds OK, but is prone to “SQL injection” ploy--

                Adding on to app’s SQL by putting the right text in a user input field

My break in :

                Username   ‘ or ‘a’ = ‘a

                Password    ‘ or ‘a’ = ‘a

                Success login!

This input made the query into

  select count(*) from userpass

     where username=’‘ or ‘a’ = ‘a‘and password=’‘ or ‘a’ = ‘a

(user input underlined) which counts every line in the table, resulting in a successful login.

Fixes:

or, another more common approach:

See the Wikipedia article on SQL injection.

In general be wary of using strings from users in SQL!