Wednesday, November 30, 2011

Cookies

HTTP is a 'stateless' protocol - each new HTTP request is completely independent of the previous requests.

Most web applications need to be 'stateful',for example, web applications that need to identify clients through the course of a session (login->to->logout).

They use cookies.

Cookies are name/value pairs which applications can associate with a response. The browser returns these cookies in future requests (until the cookie 'expires' at a preset date/time).

Cookies are the backbone of 'stateful' web applications.

Java Servlet API provides a Cookie API for setting cookies.

Let us set a cookie in our application that gives a unique ID to each user.
Here is how HelloWorld.java looks like with cookie:


 package org.confucius;   

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class HelloWorld extends HttpServlet{
private static int nextUserId = 0;

public void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
String cookieValue = getCookieValue(request.getCookies(), "userId");

if (cookieValue == null){
Cookie userCookie = new Cookie("userId", String.valueOf(nextUserId));
response.addCookie(userCookie);
nextUserId++;
}

response.getWriter().write("User ID = " + cookieValue);
}

private String getCookieValue(Cookie[] cookies, String cookieName) {
if (cookies == null)
return null;

for (int i = 0; i < cookies.length; i++) {
Cookie cookie = cookies[i];
if (cookieName.equals(cookie.getName()))
return (cookie.getValue());
}
return null;
}
}


Let us understand what we did.

We maintain a static counter to track the next User ID (a simple integer)

We get the cookie from the request - if one is not found, we assign one.

Update your web.xml to direct the /home URL to HelloWorld Servlet:

 <web-app>   
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>org.confucius.HelloWorld</servlet-class>
</servlet>

<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/home</url-pattern>
</servlet-mapping>
</web-app>



If you build and deploy HelloWorld.war, then point your browser to:
http://localhost:8080/HelloWorld/home

You will first see that the User ID = null, becuase it starts off with no cookie.
If you refresh your browser, it will set the User ID to 1.
If you keep refreshing, it will continue to be 1.
If you close and restart your browser, the user ID will go to 2.

Note that since we did not explicitly set an expiry for the cookie, the cookie dies when the browser is closed.

Using Log4J in Web Application

Create a log4j.properties in your /classes folder - because only /classes and /lib folders will be in Tomcat ClassPath, the convention is to put all .properties files in /classes

Here is what my log4j.properties looks like:
  log4j.rootLogger=DEBUG, RollFileAppender   

log4j.appender.RollFileAppender=org.apache.log4j.RollingFileAppender
log4j.appender.RollFileAppender.File=${catalina.home}/logs/HelloWorld.log
log4j.appender.RollFileAppender.layout=org.apache.log4j.PatternLayout
log4j.appender.RollFileAppender.layout.ConversionPattern=%p %d %C %L %n %m %n


Remember to specify a log4j dependency in your ivy.xml:
 <ivy-module version="2.0">  
<info organisation="org.confucius" module="helloworld"/>
<dependencies>
<dependency org="javax.servlet" name="servlet-api" rev="2.5"/>
<dependency org="log4j" name="log4j" rev="1.2.16"/>
</dependencies>
</ivy-module>


Run the Ant:resolve target to download log4j.jar if necessary.

Remember to add log4j.jar to Eclipse->Project->Properties->JAVA Build Path->Libraries

Update your HelloWorld.java to use log4j:

  package org.confucius;   

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.log4j.PropertyConfigurator;
import org.apache.log4j.Logger;

public class HelloWorld extends HttpServlet{
public void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
PropertyConfigurator.configure("log4j.properties");
Logger logger = Logger.getLogger(HelloWorld.class);
logger.debug("Received age check request");

int year = Integer.parseInt(request.getParameter("year"));
if (year > 1995)
response.getWriter().println("You are underage!");
else
response.getWriter().println("You may enter!");
}
}


Now if you build and redeploy HelloWorld.war, and do an age check, you will see a HelloWorld.log file in your tomcat_home/logs folder.

AJAX

For this simple example, a client-side age check worked. For other scenarios, involving maybe a database look-up or running special analytics, we may need a server-side check.

We can do this with AJAX.

AJAX is a standardized Javascript API for sending HTTP requests to the server and handling the response. For the server, the HTTP request sent from AJAX looks no different from the one sent by a browser. Therefore any Servlet can handle a AJAX request.

Let us update out example to use AJAX to do server-side age check.

Update the HelloWorld.java Servlet to do an age check (see below):

  package org.confucius;   

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class HelloWorld extends HttpServlet{
public void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
int year = Integer.parseInt(request.getParameter("year"));
if (year > 1995)
response.getWriter().println("You are underage!");
else
response.getWriter().println("You may enter!");
}
}


Update web.xml to redirect age-check requests to HelloWorld Servlet:

 <web-app>   
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>org.confucius.HelloWorld</servlet-class>
</servlet>

<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/age-check</url-pattern>
</servlet-mapping>
</web-app>




Now update HelloWorld.jsp to use AJAX:

 <html>  
<head>
<script type="text/javascript">
function ageCheck()
{
year = document.getElementById("birthdate").value.substring(0,4);

// Create AJAX object
var xmlhttp;

if (window.XMLHttpRequest)
xmlhttp=new XMLHttpRequest(); // IE7+, Firefox, Chrome, Opera, Safari
else
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP"); // IE6, IE5

// Associate a method for AJAX response
xmlhttp.onreadystatechange=function()
{
if (xmlhttp.readyState==4 && xmlhttp.status==200) // Successful response
alert(xmlhttp.responseText);
}

// Send AJAX request
xmlhttp.open("GET","http://localhost:8080/HelloWorld/age-check?year="+year,true);
xmlhttp.send();
}
</script>
</head>
<body>
<form>
Enter your birthday (yyyy-mm-dd): <input type="text" id="birthdate"/> <button type="button" onclick="ageCheck()">Check</button>
</form>
</body>
</html>



Now if you rebuild and deploy HelloWorld.war - you will be able to age check with an AJAX call to the Servlet.

Note: Instead of copying the HelloWorld.war each time to the /webapps directory, you can use the Tomcat Manager to undeploy/redeploy.

To do this:
Go to http://localhost:8080, click on the Manager App - you can login using the admin you created earlier (see your tomcat_home/conf/tomcat-users.xml file)

Tuesday, November 22, 2011

JSP - Javascript

Using javascript in JSP is no different from using it in HTML.

Update your HelloWorld.jsp with the following:
 <html>  
      <head>  
           <script type="text/javascript">  
                function ageCheck()  
                     {  
                          year = parseInt(document.getElementById("birthdate").value.substring(0,4));  
                          if (year > 1995)  
                               alert("You are underage!");  
                          else  
                               alert ("You may enter!");  
                     }  
           </script>       
      </head>  
      <body>  
           Enter your birthday (yyyy-mm-dd): <input type="text" id="birthdate" onblur="ageCheck()"/>  
      </body>  
 </html>  


We have an edit field which takes a birthday, then calls a javascript function to check for underage.

Run Ant:dist to rebuild HelloWorld.war, then deploy it to Tomcat.
You may need to restart Tomcat after cleaning the previous HelloWorld.

Point your browser to http://localhost:8080/HelloWorld/jsp/HelloWorld.jsp

Enter your birthdate, then tab out of the edit field to trigger the javascript function.

(The javascript function is attached to the onblur event, so tabbing out of the edit field triggers it.)

Monday, November 21, 2011

JSP - Calling Java class

Reduce your HelloWorld.java class from a Servlet to a POJO.
(POJO = Plain Old Java Object - one which does not extend any other class nor implement any external interface)

Like this:
 package org.confucius;  
   
 public class HelloWorld{  
      public static String getGreeting ()  
      {  
           return "Hello World!";  
      }  
 }  
   


Call this from your HelloWorld.jsp:
 <html>  
      <head>  
           <%@ page import="org.confucius.HelloWorld" %>  
      </head>  
      <body>  
           <p><%= HelloWorld.getGreeting() %></p>  
      </body>  
 </html  


Note that <% .. %> tells JSP that this is Java code.
Between these enclosures, you can write any Java code, just like you would write inside a Foo.java source file.

Cleanup your web.xml - we no longer using HelloWorld as a servlet:
 <web-app>  
 </web-app>  
   


Run Ant:dist target, then deploy HelloWorld.war to Tomcat.

If you point your browser to:
http://localhost:8080/HelloWorld/jsp/HelloWorld.jsp

You will see "Hello World!" - but this time the greeting has come from the HelloWorld.java POJO.

JSP - HelloWorld

In your HelloWorld project, create a directory /web-content/jsp
In the /jsp directory, create a file HelloWorld.jsp

Write (or copy/paste) the following in this file:
 <html>  
      <head>  
      </head>  
      <body>  
           <p>Hello World!</p>  
      </body>  
 </html>  


As you can see, this JSP is no different from an HTML page.

Update your Ant:dist target in build.xml to include this JSP in the war:
 <project name="HelloWorld" xmlns:ivy="antlib:org.apache.ivy.ant" >  
     
   <target name="resolve" description="--> retrieve dependencies with ivy">  
     <ivy:retrieve />  
   </target>  
   
      <target name="init" depends="resolve">  
           <mkdir dir="classes"/>  
           <mkdir dir="target"/>  
      </target>  
   
      <target name="compile" depends="init">  
           <javac srcdir="." destdir="classes">  
                <classpath>  
                 <pathelement location="lib/servlet-api-2.5.jar"/>  
                </classpath>  
           </javac>  
      </target>  
   
      <target name="dist" depends="compile">  
           <war destfile="target/HelloWorld.war" webxml="web.xml">  
                 <classes dir="classes"/>  
                 <lib dir="lib"/>  
                 <fileset dir="web-content"/>  
           </war>  
      </target>  
 </project>  


Run the Ant:dist target to create a new HelloWorld.war

Deploy it to Tomcat (you may need to stop the server, delete the exploded HelloWorld.war then restart).

Point your broser to:
http://localhost:8080/HelloWorld/jsp/HelloWorld.jsp

You should see 'Hello World!' - this time rendered by the JSP.

Note that because the /jsp directory is outside the WEB-INF, it is a public directory - so we are able to directly access its contents (HelloWorld.jsp)

JSP

For our HelloWorld servlet, we could get away with a single call to the response writer:
 response.getWriter().println("Hello World!");  


However, this doesn't work so well if you wanted to output an entire HTML.
For example, to generate 'Hello, World!' message as an HTML page, you will have to do:
           response.getWriter().println("<HTML>");  
response.getWriter().println("<HEAD>");
response.getWriter().println("</HEAD>");
response.getWriter().println("<BODY>");
response.getWriter().println("Hello World!");
response.getWriter().println("</BODY>");
response.getWriter().println("</HTML>");



Clearly, as your HTML gets more complex, the Servlet will become unreadable.

JSP (Java Server Pages) was introduced to solve this problem.

JSP is a templating technology - JSP pages (*.jsp) look exactly like HTML pages, plus they can make calls to Java classes.