One of the oft-heard terms in Java is "Connection pool".
This is a very simple idea which is at times blown out of proportion.
Whenever you make a HTTP request to a web application, you need to open a TCP connection. After you are done, you need to close the connection.
Suppose you make several requests to the server, it is not very efficient to open and close connections each and every time.
Better to open a connection and leave it open for the duration of the requests. That way you can send all requests on the same connection, saving yourself significant overhead.
Now, pushing this idea further, why not open several connections to the server and keep them ready. Now you can send multiple requests to the server simultaneously.
This is a connection pool.
The idea is simple as that. But not without its quirks.
First, how do you know when to close the connections. Leaving a pool of connections open for eternity is not smart and consumes resources. How can you tell when the "last" request has been fulfilled?
Second, how many connections should be in the pool? 5 or 10 or 1000? This is called pool size.
Worse problems surface very soon. What if a connection has been idle for a long time and TCP has timed out and closed it? This leaves dead connections in the pool.
Even more challenging - what if you have 10 connections in the pool and a 11th request is received? Should you queue the request? How many requests should be queued??
It is all these questions which have no easy answer which make connection pools easy to understand, but hard to configure.
Eventually, all connection pool implementations come with several configuration parameters like pool size, idle timeout, queuing policy, connection validation, etc.
What makes things worse is that there are usually several queues involved in a typical web application infrastructure - connection pool maintained by hibernate, another pool maintained by web server, yet another from app server to databases, etc.
Mismatches between their configuration means you really have no idea what is going on under the hood!
Monday, April 16, 2012
Monday, April 9, 2012
Form Validation
Besides internationalization, the other top functionality needed by web applications is form validation (or user input validation).
Java does not provide any standard interface to do this, so it is mostly left to the application developer.
There are primarily two ways to do form validation - on the client side using javascript, or on the server side using java.
The first is faster, while the second is easier (it is usually easier to write code and debug in Java than in javascript).
In the coming posts we will see several front end frameworks like Tapestry, JSF and GWT, which provide facilities for input validation, amongst other things.
Java does not provide any standard interface to do this, so it is mostly left to the application developer.
There are primarily two ways to do form validation - on the client side using javascript, or on the server side using java.
The first is faster, while the second is easier (it is usually easier to write code and debug in Java than in javascript).
In the coming posts we will see several front end frameworks like Tapestry, JSF and GWT, which provide facilities for input validation, amongst other things.
ResourceBundle and i18n
i18n = internationalization (there are 18 characters between i and n!)
When you need your application to be internationalized, you want all messages displayed to the user to be in the language of their locale.
Java has a class called java.util.ResourceBundle which helps you do this.
You can create property files with extensions which correspond to various locales.
For example, I can have greetings_en_US.properties and greetings_fr_CA.properties.
The first contains greetings to be displayed to english users from US. The second is for french users from Canada.
Inside the first, I will have a key-value pair like:
greet="Hello World!"
In the second, I wil have:
greet="Bonjour"
In my application, I will have the code:
Java's ResourceBundle class automatically picks the correct property file based on the current locale. If it is a HTTP request, the browser will send the locale of the user, and that will determine which property file will be picked up.
Property files which are designed to be used by ResourceBundle class are themselves called "resource bundles".
Let us modify our project from the previous post to use resource bundles.
In the /src/main/resources folder, create two files:
greetings_en_US.properties and greetings_fr_CA.properties
In the first, type:
greet="Hello World!"
In the second, type:
greet="Bonjour"
Now update your index.jsp to:
R-click on the project in Eclipse navigator and select Run As-->Maven install.
I am assuming you are using the cargo plugin to deploy the war, as described in this post.
Then point your browser to:
http://localhost:8080/HelloWorldWARArch/
You will see "Hello World!" if you are in the US.
Now, let us force our code to change locale.
Change your index.jsp to:
Now if you build and deploy your application, you will see "Bonjour!"
You can have as many resource bundles as you want to reflect different locales.
If a resource bundle for a given locale is not found, the default resource bundle (i.e. the one which has no locale specification - greetings.properties) will be used.
When you need your application to be internationalized, you want all messages displayed to the user to be in the language of their locale.
Java has a class called java.util.ResourceBundle which helps you do this.
You can create property files with extensions which correspond to various locales.
For example, I can have greetings_en_US.properties and greetings_fr_CA.properties.
The first contains greetings to be displayed to english users from US. The second is for french users from Canada.
Inside the first, I will have a key-value pair like:
greet="Hello World!"
In the second, I wil have:
greet="Bonjour"
In my application, I will have the code:
ResourceBundle res = ResourceBundle.getBundle("greetings");
String greetStr = res.getString("greet");
Java's ResourceBundle class automatically picks the correct property file based on the current locale. If it is a HTTP request, the browser will send the locale of the user, and that will determine which property file will be picked up.
Property files which are designed to be used by ResourceBundle class are themselves called "resource bundles".
Let us modify our project from the previous post to use resource bundles.
In the /src/main/resources folder, create two files:
greetings_en_US.properties and greetings_fr_CA.properties
In the first, type:
greet="Hello World!"
In the second, type:
greet="Bonjour"
Now update your index.jsp to:
<html>
<head>
<%@ page import="java.util.ResourceBundle" %>
</head>
<body>
<% ResourceBundle res = ResourceBundle.getBundle("greetings"); %>
<h2><%= res.getString("greet")%></h2>
</body>
</html>
R-click on the project in Eclipse navigator and select Run As-->Maven install.
I am assuming you are using the cargo plugin to deploy the war, as described in this post.
Then point your browser to:
http://localhost:8080/HelloWorldWARArch/
You will see "Hello World!" if you are in the US.
Now, let us force our code to change locale.
Change your index.jsp to:
<html>
<head>
<%@ page import="java.util.ResourceBundle" %>
<%@ page import="java.util.Locale" %>
</head>
<body>
<% ResourceBundle res = ResourceBundle.getBundle("greetings",new Locale("fr","CA")); %>
<h2><%= res.getString("greet")%></h2>
</body>
</html>
Now if you build and deploy your application, you will see "Bonjour!"
You can have as many resource bundles as you want to reflect different locales.
If a resource bundle for a given locale is not found, the default resource bundle (i.e. the one which has no locale specification - greetings.properties) will be used.
Sunday, April 1, 2012
Maven - profiles
With a ordinary pom.xml, the exact exact same plugins and goals are executed no matter which environment the build is run in.
Ordinarily, this is exactly what you want. That the build steps are the same no matter who runs the build and where.
But there are times when different environments need slightly different builds.
For example, we may want to run the cargo plugin only in our local build.
In our production build, we would not want to automatically deploy to the server.
We do this by creating Profiles.
A profile, in effect, is nothing but a name for section of the build.
Different sections of the build can be enclosed in different profiles.
Any build section enclosed in a profile will be executed only when that profile is active.
For example, let us enclose our cargo plugin in a profile, like this:
We have defined a profile "local-profile" and enclosed the cargo plugin in it.
Our cargo plugin will be executed only when the local-profile is active.
By default, profiles are inactive.
Update your /src/main/java/webapp/index.jsp, like this:
R-click on HelloWorldWARArch project and select Run As-->Maven install.
If you look at the messages in the console, you will see that the cargo plugin was not executed.
If you point your browser to:
http://localhost:8080/HelloWorldWARArch/
you will see "Hello World v3!", further proof that the cargo plugin did not execute.
How to activate the profile?
There are several ways to activate profiles.
We will activate the profile from our settings.xml, like this:
Now if you run Maven install, you will see the cargo plugin execute.
Refresh your browser, and you will see:
"HelloWorld v4!"
You can create different profiles for different environments (development/testing/production) and execute the correct build for each environment.
Ordinarily, this is exactly what you want. That the build steps are the same no matter who runs the build and where.
But there are times when different environments need slightly different builds.
For example, we may want to run the cargo plugin only in our local build.
In our production build, we would not want to automatically deploy to the server.
We do this by creating Profiles.
A profile, in effect, is nothing but a name for section of the build.
Different sections of the build can be enclosed in different profiles.
Any build section enclosed in a profile will be executed only when that profile is active.
For example, let us enclose our cargo plugin in a profile, like this:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.confucius</groupId>
<artifactId>HelloWorldWARArch</artifactId>
<packaging>war</packaging>
<version>0.0.1-SNAPSHOT</version>
<name>Webapp demonstrating cargo plugin</name>
<url>http://www.confucius.org</url>
<pluginRepositories>
<pluginRepository>
<id>codehaus-releases</id>
<url>http://nexus.codehaus.org/releases/</url>
</pluginRepository>
</pluginRepositories>
<profiles>
<profile>
<id>local-profile</id>
<build>
<finalName>HelloWorldWARArch</finalName>
<plugins>
<plugin>
<groupId>org.codehaus.cargo</groupId>
<artifactId>cargo-maven2-plugin</artifactId>
<version>1.2.0</version>
<executions>
<execution>
<phase>install</phase>
<goals>
<goal>undeploy</goal>
<goal>deploy</goal>
</goals>
</execution>
</executions>
<configuration>
<container>
<containerId>tomcat7x</containerId>
<type>remote</type>
</container>
<deployer>
<type>remote</type>
<deployables>
<deployable>
<groupId>org.confucius</groupId>
<artifactId>HelloWorldWARArch</artifactId>
<type>war</type>
</deployable>
</deployables>
</deployer>
<configuration>
<type>runtime</type>
<properties>
<cargo.server.settings>localhost</cargo.server.settings>
</properties>
</configuration>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>
We have defined a profile "local-profile" and enclosed the cargo plugin in it.
Our cargo plugin will be executed only when the local-profile is active.
By default, profiles are inactive.
Update your /src/main/java/webapp/index.jsp, like this:
<html>
<body>
<h2>Hello World v4!</h2>
</body>
</html>
R-click on HelloWorldWARArch project and select Run As-->Maven install.
If you look at the messages in the console, you will see that the cargo plugin was not executed.
If you point your browser to:
http://localhost:8080/HelloWorldWARArch/
you will see "Hello World v3!", further proof that the cargo plugin did not execute.
How to activate the profile?
There are several ways to activate profiles.
We will activate the profile from our settings.xml, like this:
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
http://maven.apache.org/xsd/settings-1.0.0.xsd">
<localRepository/>
<interactiveMode/>
<usePluginRegistry/>
<offline/>
<pluginGroups/>
<servers>
<server>
<id>localhost</id>
<configuration>
<cargo.tomcat.manager.url>http://localhost:8080/manager</cargo.tomcat.manager.url>
<cargo.remote.username>admin</cargo.remote.username>
<cargo.remote.password>s3cret</cargo.remote.password>
</configuration>
</server>
</servers>
<mirrors/>
<proxies/>
<profiles/>
<activeProfiles>
<activeProfile>local-profile</activeProfile>
</activeProfiles>
</settings>
Now if you run Maven install, you will see the cargo plugin execute.
Refresh your browser, and you will see:
"HelloWorld v4!"
You can create different profiles for different environments (development/testing/production) and execute the correct build for each environment.
Maven - settings.xml
While pom.xml is the configuration for a specific Maven project, settings.xml is the configuration for Maven itself.
The default settings.xml is located in the <maven_home>/conf
It looks like this (after removing the comments):
Anything you specify in the settings.xml applies to all Maven projects.
As you can see, the default settings.xml does not specify anything.
But if you were to update it by specifying servers, proxies, mirrors, repositories, etc - it would be picked up by all maven projects.
If you update the settings.xml in <maven_home>/conf, then all users of maven will be affected by your changes.
A better way to do things is to create a settings.xml in your <home directory>/.m2. If you do this, Maven merges your settings.xml with the settings.xml in <maven_home>/conf. Your settings taking precedence.
I will not describe each and every setting in settings.xml, but just show one as an example.
In our previous post, we put the username and password to Tomcat in the pom.xml.
This is not a good idea because the pom.xml gets distributed with the project and it will expose your credentials.
So we will specify our server credentials in settings.xml, which stays in our home directory.
In your <home directory>/.m2, create a settings.xml file, like this:
Here we have specified a server called "localhost" and given it the url and credentials of our local tomcat server.
Now update your pom.xml to use the server in the settings.xml, like this:
(we have updated the properties in the bottom section of pom.xml)
Update your /src/main/webapp/index.jsp, like this:
In your Eclipse Navigatory, R-click on HelloWorldWARArch project and select Run As-->Maven install
Now if you point your browser to:
http://localhost:8080/HelloWorldWARArch/
you will see "Hello World v3!"
This shows that the pom.xml picked up the server settings from settings.xml
You can now safely distribute this project without exposing the server credentials.
Also, you do not need to specify the credentials in each and every project. All projects will pick up the configuration from settings.xml.
The default settings.xml is located in the <maven_home>/conf
It looks like this (after removing the comments):
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
http://maven.apache.org/xsd/settings-1.0.0.xsd">
<localRepository/>
<interactiveMode/>
<usePluginRegistry/>
<offline/>
<pluginGroups/>
<servers/>
<mirrors/>
<proxies/>
<profiles/>
<activeProfiles/>
</settings>
Anything you specify in the settings.xml applies to all Maven projects.
As you can see, the default settings.xml does not specify anything.
But if you were to update it by specifying servers, proxies, mirrors, repositories, etc - it would be picked up by all maven projects.
If you update the settings.xml in <maven_home>/conf, then all users of maven will be affected by your changes.
A better way to do things is to create a settings.xml in your <home directory>/.m2. If you do this, Maven merges your settings.xml with the settings.xml in <maven_home>/conf. Your settings taking precedence.
I will not describe each and every setting in settings.xml, but just show one as an example.
In our previous post, we put the username and password to Tomcat in the pom.xml.
This is not a good idea because the pom.xml gets distributed with the project and it will expose your credentials.
So we will specify our server credentials in settings.xml, which stays in our home directory.
In your <home directory>/.m2, create a settings.xml file, like this:
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
http://maven.apache.org/xsd/settings-1.0.0.xsd">
<localRepository/>
<interactiveMode/>
<usePluginRegistry/>
<offline/>
<pluginGroups/>
<servers>
<server>
<id>localhost</id>
<configuration>
<cargo.tomcat.manager.url>http://localhost:8080/manager</cargo.tomcat.manager.url>
<cargo.remote.username>admin</cargo.remote.username>
<cargo.remote.password>s3cret</cargo.remote.password>
</configuration>
</server>
</servers>
<mirrors/>
<proxies/>
<profiles/>
<activeProfiles/>
</settings>
Here we have specified a server called "localhost" and given it the url and credentials of our local tomcat server.
Now update your pom.xml to use the server in the settings.xml, like this:
(we have updated the properties in the bottom section of pom.xml)
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.confucius</groupId>
<artifactId>HelloWorldWARArch</artifactId>
<packaging>war</packaging>
<version>0.0.1-SNAPSHOT</version>
<name>Webapp demonstrating cargo plugin</name>
<url>http://www.confucius.org</url>
<pluginRepositories>
<pluginRepository>
<id>codehaus-releases</id>
<url>http://nexus.codehaus.org/releases/</url>
</pluginRepository>
</pluginRepositories>
<build>
<finalName>HelloWorldWARArch</finalName>
<plugins>
<plugin>
<groupId>org.codehaus.cargo</groupId>
<artifactId>cargo-maven2-plugin</artifactId>
<version>1.2.0</version>
<executions>
<execution>
<phase>install</phase>
<goals>
<goal>undeploy</goal>
<goal>deploy</goal>
</goals>
</execution>
</executions>
<configuration>
<container>
<containerId>tomcat7x</containerId>
<type>remote</type>
</container>
<deployer>
<type>remote</type>
<deployables>
<deployable>
<groupId>org.confucius</groupId>
<artifactId>HelloWorldWARArch</artifactId>
<type>war</type>
</deployable>
</deployables>
</deployer>
<configuration>
<type>runtime</type>
<properties>
<cargo.server.settings>localhost</cargo.server.settings>
</properties>
</configuration>
</configuration>
</plugin>
</plugins>
</build>
</project>
Update your /src/main/webapp/index.jsp, like this:
<html>
<body>
<h2>Hello World v3!</h2>
</body>
</html>
In your Eclipse Navigatory, R-click on HelloWorldWARArch project and select Run As-->Maven install
Now if you point your browser to:
http://localhost:8080/HelloWorldWARArch/
you will see "Hello World v3!"
This shows that the pom.xml picked up the server settings from settings.xml
You can now safely distribute this project without exposing the server credentials.
Also, you do not need to specify the credentials in each and every project. All projects will pick up the configuration from settings.xml.
Maven - cargo plugin
As an example of a Maven plugin, let us use cargo.
Cargo is one of the popular maven plugins. It enables you to deploy your war to the server.
We will update the HelloWorldWARArch project that we created in this post to use cargo.
Update the pom.xml to looks like this:
What are we doing here?
First, we are declaring the plugin repository from which Maven can get the cargo plugin.
Next, we include the cargo plugin in the build, specifying its groupId, artifactId and version.
After that, we tell Maven that this plugin must be executed in the 'install' phase of the lifecycle.
We also tell Maven to execute the undeploy and deploy goals of the cargo plugin.
'Undeploy' goal undeploys the HelloWorldWARArch.war that is currently on the server and 'deploy' goal deploys the latest HelloWorldWARArch.war to the server.
Next, we tell cargo that we are deploying to Tomcat 7.
Then we tell cargo to deploy the HelloWorldWARArch.war.
Finally, we tell cargo the credentials (username/password) to use when deploying.
Before we can execute the plugin, we need to update the admin roles on Tomcat so it will accept a deployment from cargo.
Open the tomcat-users.xml in your <tomcat-install-dir>/conf folder and update the admin user with a manager-script role, like this:
Restart Tomcat for the role to take effect.
Now, in your Eclipse HelloWorldWARArch project, update the file /src/main/webapp/index.jsp like this:
Now, in your Eclipse navigator view, R-click on the HelloWorldWARArch prohect and select Rus As --> Maven Install.
In the console, you will see messages from the cargo plugin.
If you point your browser to:
http://localhost:8080/HelloWorldWARArch/
you will see the message "Hello World v2!"
This tells you that cargo successfully deployed the new war to Tomcat.
Notes:
As you can see cargo is a very convenient plugin. It will automatically take care of deploying the new war to the server at the end of the build process.
But this has come at a configuration cost. You can see that the pom.xml has become a lot more complex. The more plugins you use, the more complex the pom.xml becomes.
Cargo is one of the popular maven plugins. It enables you to deploy your war to the server.
We will update the HelloWorldWARArch project that we created in this post to use cargo.
Update the pom.xml to looks like this:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.confucius</groupId>
<artifactId>HelloWorldWARArch</artifactId>
<packaging>war</packaging>
<version>0.0.1-SNAPSHOT</version>
<name>Webapp demonstrating cargo plugin</name>
<url>http://www.confucius.org</url>
<pluginRepositories>
<pluginRepository>
<id>codehaus-releases</id>
<url>http://nexus.codehaus.org/releases/</url>
</pluginRepository>
</pluginRepositories>
<build>
<finalName>HelloWorldWARArch</finalName>
<plugins>
<plugin>
<groupId>org.codehaus.cargo</groupId>
<artifactId>cargo-maven2-plugin</artifactId>
<version>1.2.0</version>
<executions>
<execution>
<phase>install</phase>
<goals>
<goal>undeploy</goal>
<goal>deploy</goal>
</goals>
</execution>
</executions>
<configuration>
<container>
<containerId>tomcat7x</containerId>
<type>remote</type>
</container>
<deployer>
<type>remote</type>
<deployables>
<deployable>
<groupId>org.confucius</groupId>
<artifactId>HelloWorldWARArch</artifactId>
<type>war</type>
</deployable>
</deployables>
</deployer>
<configuration>
<type>runtime</type>
<properties>
<cargo.tomcat.manager.url>http://localhost:8080/manager</cargo.tomcat.manager.url>
<cargo.remote.username>admin</cargo.remote.username>
<cargo.remote.password>s3cret</cargo.remote.password>
</properties>
</configuration>
</configuration>
</plugin>
</plugins>
</build>
</project>
What are we doing here?
First, we are declaring the plugin repository from which Maven can get the cargo plugin.
Next, we include the cargo plugin in the build, specifying its groupId, artifactId and version.
After that, we tell Maven that this plugin must be executed in the 'install' phase of the lifecycle.
We also tell Maven to execute the undeploy and deploy goals of the cargo plugin.
'Undeploy' goal undeploys the HelloWorldWARArch.war that is currently on the server and 'deploy' goal deploys the latest HelloWorldWARArch.war to the server.
Next, we tell cargo that we are deploying to Tomcat 7.
Then we tell cargo to deploy the HelloWorldWARArch.war.
Finally, we tell cargo the credentials (username/password) to use when deploying.
Before we can execute the plugin, we need to update the admin roles on Tomcat so it will accept a deployment from cargo.
Open the tomcat-users.xml in your <tomcat-install-dir>/conf folder and update the admin user with a manager-script role, like this:
<user name="admin" password="s3cret" roles="manager-gui,manager-script" />
Restart Tomcat for the role to take effect.
Now, in your Eclipse HelloWorldWARArch project, update the file /src/main/webapp/index.jsp like this:
<html>
<body>
<h2>Hello World v2!</h2>
</body>
</html>
Now, in your Eclipse navigator view, R-click on the HelloWorldWARArch prohect and select Rus As --> Maven Install.
In the console, you will see messages from the cargo plugin.
If you point your browser to:
http://localhost:8080/HelloWorldWARArch/
you will see the message "Hello World v2!"
This tells you that cargo successfully deployed the new war to Tomcat.
Notes:
As you can see cargo is a very convenient plugin. It will automatically take care of deploying the new war to the server at the end of the build process.
But this has come at a configuration cost. You can see that the pom.xml has become a lot more complex. The more plugins you use, the more complex the pom.xml becomes.
Subscribe to:
Posts (Atom)