Saturday, February 18, 2012

MySQL

One of the most critical things a web application does is persist data to a database.

Every session of every user generates data - shopping carts, credit cards, form fill-ups, messages, sales, etc.

All this data is typically saved into a relational database - typically with JDBC (Java Database Connectivity API) or Hibernate (an ORM solution).

We will learn how to do database persistence.

We will use one of the most popular open source database - MySQL.

Please install MySQL and get the service started. MySQL provides an installer which does everything - just follow the default settings on the wizard.

Make sure to remember/write-down the admin password that you will specify in the installation wizard - if you forget, it is a hell of a lot of trouble to uninstall/reinstall!

Note that MySQL comes in cluster mode as well. I had a hard time getting the cluster installer to work correctly, so I recommend installing the regular MySQL.

After you are done installing, you should be able to start the MySQL command line client.

Type the "show databases;" command at the mysql prompt.
If you see the default databases, you are good to go.

 Enter password: *********  
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 4
Server version: 5.5.2-m2-community MySQL Community Server (GPL)

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| test |
+--------------------+
4 rows in set (0.00 sec)

mysql>

Spring Beans - Scope

We saw that Spring creates the instances of cart, shop and checkout for us (we didn't have to do - new Cart(); new Shop(); new Checkout())

But what is the life of these bean instances? Are they created new each time a bean is requested? Or do we get the same instance even if we ask for it a hundred times?

This is called 'scope' of a bean.

Beans can have 5 scopes:
1. Singleton - this means there is exactly one instance of the bean, this bean never dies, everyone get this same bean instance no matter how many times they ask for it.

2. Prototype - this means that a new instance of the bean is created each time someone asks for it. If a hundred requests are made for it, a hundred independent instances will get created. Each instance will get garbage collected when it is no longer in use - just like any other object.

3. Request - this means a new instance is created for each HTTP request. The instance dies when the HTTP request completes.

4. Session - this means a new instance for each new HTTP session. When the session ends, the instance dies.

5. Global session - I am not completely clear about this scope.

Let us see what it takes to convert our beans to Request scope (by default, they are session scope).

First, we need to update out Spring context to include the Request scope.

To do this, open your SpringUtils.java, and update it to look like this:
(notice that we have changed the type of springContext to ClassPathXmlApplicationContext)

 package org.confucius;   

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.web.context.request.RequestScope;
import org.springframework.web.context.request.SessionScope;

public class SpringUtils {
private static ClassPathXmlApplicationContext springContext = null;

public static ApplicationContext getSpringContext() {
if (springContext == null){
springContext = new ClassPathXmlApplicationContext("spring-beans.xml");
springContext.getBeanFactory().registerScope("session", new SessionScope());
springContext.getBeanFactory().registerScope("request", new RequestScope());
}

return springContext;
}

}


Next, we need to add a Listener to our web.xml - so Spring knows when new requests are made.

Update your web.xml to look like this:

 <web-app>  
<listener>
<listener-class>
org.springframework.web.context.request.RequestContextListener
</listener-class>
</listener>

<servlet>
<servlet-name>shop</servlet-name>
<servlet-class>org.confucius.ShopServlet</servlet-class>
</servlet>

<servlet>
<servlet-name>checkout</servlet-name>
<servlet-class>org.confucius.CheckoutServlet</servlet-class>
</servlet>

<servlet-mapping>
<servlet-name>shop</servlet-name>
<url-pattern>/shop</url-pattern>
</servlet-mapping>

<servlet-mapping>
<servlet-name>checkout</servlet-name>
<url-pattern>/checkout</url-pattern>
</servlet-mapping>

</web-app>




Finally, update your bean declarations to specify the request scope.

Your bean-cart.xml will look like this:

 <beans xmlns="http://www.springframework.org/schema/beans"  
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">

<bean id="cart" class="org.confucius.Cart" scope="request"/>

</beans>


bean-shop.xml looks like this:

 <beans xmlns="http://www.springframework.org/schema/beans"  
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">

<bean id="shop" class="org.confucius.Shop" scope="request">
<property name="cart" ref="cart"/>
</bean>

</beans>


bean-checkout.xml looks like this:

 <beans xmlns="http://www.springframework.org/schema/beans"  
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">

<bean id="checkout" class="org.confucius.Checkout" scope="request">
<property name="cart" ref="cart"/>
</bean>

</beans>


If you build and deploy HelloWorld.war, then browse to:

http://localhost:8080/HelloWorld/shop

no matter how many times you refresh the browser, there will be only one item in the cart.

And if you browse to:

http://localhost:8080/HelloWorld/checkout

there will always be zero items in the cart.

This is because a new instance of the beans are getting created each time you refresh.

Friday, February 17, 2012

Spring Beans and Dependency Injection - Part 6

Running the application..

Build this application and deploy it to Tomcat.

Then point your browser to:
http://localhost:8080/HelloWorld/shop

You will see the number of items in the Shopping cart.

Each time you refresh your browser, the count will increase by 1.

Now point your browser to:
http://localhost:8080/HelloWorld/checkout

You will see the exact same items count in the Checkout cart.

Thanks to Spring dependency injection, both the Shop and Checkout are seeing the same Cart.

Imagine if we had more modules in our application - Shipping.java, Audit.java, NotificationEmail.java, FeedbackRequest.java etc which all needed to use the cart. We could ask Spring to inject the same cart in each of these modules. This will ensure that they are all looking at the exact same cart.

Spring Beans and Dependency Injection - Part 5

Using the Spring Beans.

We will create ShopServlet and CheckoutServlet so we can play with the Spring Beans we just created from a browser.

First, let us create a utility class to load the Spring Beans.

In your /src/org/confucius folder, create a class SpringUtils.java, like this:

 package org.confucius;  

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringUtils {
private static ApplicationContext springContext = null;

public static ApplicationContext getSpringContext() {
if (springContext == null){
springContext = new ClassPathXmlApplicationContext("spring-beans.xml");
}

return springContext;
}

}



This method loads the Spring Context exactly once, from the spring-beans.xml file.

Now, in your /src/org/confucius, create a Class ShopServlet.java, like this:

 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 ShopServlet extends HttpServlet {

public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Shop shop = (Shop) SpringUtils.getSpringContext().getBean("shop");
Cart cart = shop.getCart();

int numItems = cart.getNumItems();
cart.setNumItems(numItems+1);

response.getWriter().write("Number of items in shopping cart = " + cart.getNumItems());

}

}



This Servlet gets the shop bean and increments the items in its cart.

Now in your /src/org/confucius, create a class CheckoutServlet.java, like this:

 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 CheckoutServlet extends HttpServlet {

public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Checkout checkout = (Checkout) SpringUtils.getSpringContext().getBean("checkout");
Cart cart = checkout.getCart();

response.getWriter().write("Number of items in checkout cart = " + cart.getNumItems());

}

}



This Servlet get the checkout bean and prints the number of items in its cart.


Update your web.xml to use these Servlets, like this:

 <web-app>   
<servlet>
<servlet-name>shop</servlet-name>
<servlet-class>org.confucius.ShopServlet</servlet-class>
</servlet>

<servlet>
<servlet-name>checkout</servlet-name>
<servlet-class>org.confucius.CheckoutServlet</servlet-class>
</servlet>

<servlet-mapping>
<servlet-name>shop</servlet-name>
<url-pattern>/shop</url-pattern>
</servlet-mapping>

<servlet-mapping>
<servlet-name>checkout</servlet-name>
<url-pattern>/checkout</url-pattern>
</servlet-mapping>

</web-app>

Spring Beans and Dependency Injection - Part 4

Declaring Spring beans.

In your /classes directory (which is at the same level as /src), create a folder called "spring-beans".

In this folder, create a file bean-cart.xml, like this:

 <beans xmlns="http://www.springframework.org/schema/beans"  
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">

<bean id="cart" class="org.confucius.Cart" />

</beans>


Here we have declared a Spring Bean with the id "cart" as an instance of class org.confucius.Cart.

In /classes/spring-beans, create a file bean-shop.xml, like this:

 <beans xmlns="http://www.springframework.org/schema/beans"  
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">

<bean id="shop" class="org.confucius.Shop">
<property name="cart" ref="cart"/>
</bean>

</beans>


Here we have declared a Spring Bean with id 'shop' as an instance of class org.confucius.Shop.

Further, we have asked Spring to inject the 'cart' bean into the 'cart' property of this (shop) bean.

Next, in your /classes/spring-beans, create a file bean-checkout.xml, like this:

 <beans xmlns="http://www.springframework.org/schema/beans"  
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">

<bean id="checkout" class="org.confucius.Checkout">
<property name="cart" ref="cart"/>
</bean>

</beans>


Here we have declared a Spring Bean with id 'checkout' as an instance of class org.confucius.Checkout. Then we have injected the 'cart' bean into the 'cart' property of this (checkout) bean.

This completes the Spring Bean declaration.

For convenience of loading (you will see later), let us pack these bean declarations in a single parent xml.

In your /classes folder, create a file spring-beans.xml, like this:

(Note that we are creating this in the /classes folder and not in /classes/spring-beans folder - because we need this parent xml in the Classpath)

 <beans xmlns="http://www.springframework.org/schema/beans"  
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">

<import resource="spring-beans/bean-cart.xml" />
<import resource="spring-beans/bean-shop.xml" />
<import resource="spring-beans/bean-checkout.xml" />

</beans>


By loading this single XML, our program will essentially be loading all the individual bean declarations.

Spring Beans and Dependency Injection - Part 3

Writing Classes.

We will write the three Classes - Cart, Shop and Checkout.

In your /src/org/confucius, create a class Cart.java, like this:
 package org.confucius;  

public class Cart {
private int numItems = 0;

public int getNumItems() {
return numItems;
}

public void setNumItems(int numItems) {
this.numItems = numItems;
}
}



In your /src/org/confucius, create a Class Shop.java, like this:

 package org.confucius;  

public class Shop {
private Cart cart = null;

public Cart getCart() {
return cart;
}

public void setCart(Cart cart) {
this.cart = cart;
}
}



In your /src/org/confucius, create a Class Checkout.java, like this:

 package org.confucius;  

public class Checkout {
private Cart cart = null;

public Cart getCart() {
return cart;
}

public void setCart(Cart cart) {
this.cart = cart;
}
}

Spring Beans and Dependency Injection - Part 2

Getting spring.jar.

In your ivy.xml, add a dependency on spring.jar, so you ivy.xml now looks like this:

 <ivy-module version="2.0">  
<info organisation="org.confucius" module="helloworld"/>
<dependencies>
<dependency org="org.springframework" name="spring" rev="2.5.6"/>
<dependency org="struts" name="struts" rev="1.2.9"/>
<dependency org="org.apache.struts" name="struts-taglib" rev="1.3.10"/>
<dependency org="commons-collections" name="commons-collections" rev="20040616"/>
<dependency org="commons-digester" name="commons-digester" rev="2.1"/>
<dependency org="commons-beanutils" name="commons-beanutils" rev="20030211.134440"/>
<dependency org="commons-logging" name="commons-logging" rev="1.1.1"/>
<dependency org="org.apache.httpcomponents" name="httpcore" rev="4.2-alpha2"/>
<dependency org="org.apache.httpcomponents" name="httpclient" rev="4.2-alpha1"/>
<dependency org="org.apache.commons" name="commons-exec" rev="1.1"/>
<dependency org="com.google.guava" name="guava" rev="r09"/>
<dependency org="org.seleniumhq.selenium" name="selenium-api" rev="2.17.0"/>
<dependency org="org.seleniumhq.selenium" name="selenium-remote-driver" rev="2.17.0"/>
<dependency org="org.seleniumhq.selenium" name="selenium-firefox-driver" rev="2.17.0"/>
<dependency org="org.seleniumhq.selenium" name="selenium-java" rev="2.16.1"/>
<dependency org="junit" name="junit" rev="4.10"/>
<dependency org="org.json" name="json" rev="20090211"/>
<dependency org="javax.servlet" name="servlet-api" rev="2.5"/>
<dependency org="javax.servlet" name="jsp-api" rev="2.0"/>
<dependency org="jstl" name="jstl" rev="1.2"/>
<dependency org="log4j" name="log4j" rev="1.2.16"/>
</dependencies>
</ivy-module>


Run the Ant 'resolve'task to download spring.jar to your /lib directory.

In Eclipse, go to Project-->Properties-->Java Build Path-->Libraries-->Add Jars

and add the spring.jar to your Eclipse Classpath.

We also need to update build.xml to add spring.jar to Ant's classpath.

So your build.xml will now look like this:
 <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>

<path id="build.classpath">
<pathelement location="lib/spring-2.5.6.jar"/>
<pathelement location="lib/commons-collections-20040616.jar"/>
<pathelement location="lib/commons-digester-2.1.jar"/>
<pathelement location="lib/commons-beanutils-20030211.134440.jar"/>
<pathelement location="lib/struts-taglib-1.3.10.jar"/>
<pathelement location="lib/struts-1.2.9.jar"/>
<pathelement location="lib/commons-logging-1.1.1.jar"/>
<pathelement location="lib/httpcore-4.2-alpha2.jar"/>
<pathelement location="lib/httpclient-4.2-alpha1.jar"/>
<pathelement location="lib/commons-exec-1.1.jar"/>
<pathelement location="lib/guava-r09.jar"/>
<pathelement location="lib/selenium-api-2.17.0.jar"/>
<pathelement location="lib/selenium-firefox-driver-2.17.0.jar"/>
<pathelement location="lib/selenium-java-2.16.1.jar"/>
<pathelement location="lib/selenium-remote-driver-2.17.0.jar"/>
<pathelement location="lib/junit-4.10.jar"/>
<pathelement location="lib/json-20090211.jar"/>
<pathelement location="lib/servlet-api-2.5.jar"/>
<pathelement location="lib/jsp-api-2.0.jar"/>
<pathelement location="lib/log4j-1.2.16.jar"/>
</path>

<target name="compile" depends="init">
<javac srcdir="." destdir="classes">
<classpath refid="build.classpath"/>
</javac>
</target>

<path id="test.classpath">
<pathelement location="classes"/>
<pathelement location="lib/junit-4.10.jar"/>
<pathelement location="lib/selenium-api-2.17.0.jar"/>
<pathelement location="lib/selenium-firefox-driver-2.17.0.jar"/>
<pathelement location="lib/selenium-java-2.16.1.jar"/>
<pathelement location="lib/selenium-remote-driver-2.17.0.jar"/>
<pathelement location="lib/guava-r09.jar"/>
<pathelement location="lib/commons-exec-1.1.jar"/>
<pathelement location="lib/httpclient-4.2-alpha1.jar"/>
<pathelement location="lib/httpcore-4.2-alpha2.jar"/>
<pathelement location="lib/commons-logging-1.1.1.jar"/>
<pathelement location="lib/struts-1.2.9.jar"/>
<pathelement location="lib/struts-taglib-1.3.10.jar"/>
<pathelement location="lib/commons-beanutils-20030211.134440.jar"/>
<pathelement location="lib/commons-digester-2.1.jar"/>
<pathelement location="lib/commons-collections-20040616.jar"/>
<pathelement location="lib/spring-2.5.6.jar"/>
</path>

<target name="test" depends="compile" >
<junit failureproperty="junit.failure">
<test name="org.confucius.TestCalculator"/>
<classpath refid="test.classpath"/>
<formatter type="plain" usefile="false" />
</junit>
<fail if="junit.failure" message="Unit test(s) failed. See reports!"/>
</target>

<target name="dist" depends="test">
<war destfile="target/HelloWorld.war" webxml="web.xml">
<classes dir="classes"/>
<lib dir="lib">
<exclude name="jsp-api*.jar"/>
<exclude name="servlet-api*.jar"/>
</lib>
<fileset dir="web-content"/>
<webinf dir="WEB-INF"/>
</war>
<echo>Build executed at ${TIME_NOW}</echo>
</target>

<tstamp>
<format property="TIME_NOW" pattern="hh:mm:ss aa MM/dd/yyyy"/>
</tstamp>

</project>