From the time you fire the first request from a browser to a server, to the time you shut down the browser - is a single HTTP Session to this server.
All the requests the server received from this browser during this period belong to the same HTTP Session.
If you restart the browser, and send a new request to the server - that would start a new HTTP Session on the server.
If 10 (or n) people are sending requests to the server, then there are 10 (or n) simultaneous HTTP Sessions.
If you have multiple tabs open in your browser, each pointing to a different server, then you have that many HTTP Sessions open on your client side. Each server sees a single HTTP Session from you.
If you have a single tab open in your browser and send requests to multiple servers, you have that many HTTP Sessions open. Each server you sent a request to sees a single HTTP Session from you.
Note that a Login Session is different from a HTTP Session. Suppose I open a browser, login to my bank website, then logout, again login and then logout - and then shut down the browser. I had 2 Login Sessions within a single HTTP Session.
HTTP Sessions are timed out by the server if the administrator has configured an idle period timeout. Login Sessions are timed out by the application. Some internet email and news services allow you to maintain your Login Session over several days, even if you reboot your computer. HTTP Sessions will always end when you close your browser, regardless of the idle timeout set by the administrator.
Friday, December 2, 2011
Stateful HelloWorld
Let us make HellowWorld 'stateful' - i.e. make it maintain some data ("state") between requests.
We will store the exact time a request is received. If the next request is received later than a certain threshold, we can potentially timeout the session. Many websites do this for security.
In this example, we will not actually timeout - we just want to demonstrate how to maintain 'state'.
We will maintain a simple Hashtable which maps the user ID (that we get from the cookie) to the time the request is received.
Here is what HelloWorld looks like now:
Build HelloWorld.war, deploy it, then point your browser to:
http://localhost:8080/HelloWorld/home
Each time you refresh the browser, you will see the time that the last request was received.
We will store the exact time a request is received. If the next request is received later than a certain threshold, we can potentially timeout the session. Many websites do this for security.
In this example, we will not actually timeout - we just want to demonstrate how to maintain 'state'.
We will maintain a simple Hashtable which maps the user ID (that we get from the cookie) to the time the request is received.
Here is what HelloWorld looks like now:
package org.confucius;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
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;
private static HashMap<String, Date> requestsTracker = new HashMap<String, Date>();
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String cookieValue = getCookieValue(request.getCookies(), "userId");
if (cookieValue == null) {
cookieValue = String.valueOf(nextUserId);
Cookie userCookie = new Cookie("userId", cookieValue);
response.addCookie(userCookie);
nextUserId++;
}
Date now = new Date();
response.getWriter().write(
"Your last request was received on: "
+ requestsTracker.get(cookieValue) + ", updating to: "
+ now);
requestsTracker.put(cookieValue, now);
}
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;
}
}
Build HelloWorld.war, deploy it, then point your browser to:
http://localhost:8080/HelloWorld/home
Each time you refresh the browser, you will see the time that the last request was received.
Thursday, December 1, 2011
ServletContext
Using a static class variable like we did (nextUserId) in a Servlet is not such a good idea - it is known to have some issues in multi-threading, and also because the exact lifecycle of a Servlet is controlled by the Servlet container.
So it is recommended to use ServletContext.
ServletContext is a global object which is created when the Application is started and exists until the Application is shut down (this is called 'Application-Scope').
Any Servlet can access the ServletContext object by calling getServletContext()
Here is our HelloWorld again, this time using ServletContext:
So it is recommended to use ServletContext.
ServletContext is a global object which is created when the Application is started and exists until the Application is shut down (this is called 'Application-Scope').
Any Servlet can access the ServletContext object by calling getServletContext()
Here is our HelloWorld again, this time using ServletContext:
package org.confucius;
import java.io.IOException;
import javax.servlet.ServletContext;
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{
public void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
String cookieValue = getCookieValue(request.getCookies(), "userId");
if (cookieValue == null){
ServletContext context = getServletContext();
Integer nextUserId = (Integer) context.getAttribute("nextUserId");
if (nextUserId == null){
nextUserId = new Integer(0);
context.setAttribute("nextUserId", nextUserId);
}
Cookie userCookie = new Cookie("userId", nextUserId.toString());
response.addCookie(userCookie);
nextUserId++;
context.setAttribute("nextUserId", 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;
}
}
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:
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:
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.
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:
Remember to specify a log4j dependency in your ivy.xml:
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:
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.
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):
Update web.xml to redirect age-check requests to HelloWorld Servlet:
Now update HelloWorld.jsp to use AJAX:
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)
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:
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.)
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:
Call this from your HelloWorld.jsp:
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:
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.
(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:
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:
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)
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:
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:
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.
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.
Deploying the war
Copy the HelloWorld.war to your Tomcat installation in the /webapps directory.
Tomcat will automatically detect it, explode it, then read the web.xml and will be ready to run it.
Now point your browser to:
http://localhost:8080/HelloWorld/home
And you should see 'Hello World!'
The /HelloWorld in the URL told Tomcat to go to the HelloWorld.war and the /home was resolved to the HelloWorld servlet because of the way we configured the web.xml
Congratulations - you just wrote your first Java web application ;)
Tomcat will automatically detect it, explode it, then read the web.xml and will be ready to run it.
Now point your browser to:
http://localhost:8080/HelloWorld/home
And you should see 'Hello World!'
The /HelloWorld in the URL told Tomcat to go to the HelloWorld.war and the /home was resolved to the HelloWorld servlet because of the way we configured the web.xml
Congratulations - you just wrote your first Java web application ;)
'Exploding' the war
Often, you will come across references to 'exploded war'. All this means is that the war file has been unzipped (or unwared or unjared - its the same thing).
If you explode the war, you will see that it contains the WEB-INF directory.
Look inside it to see the web.xml, /classes and /lib
You can deploy your application as a war, or copy the exploded war.
On Tomcat, when you deploy it as a war, it will automatically be exploded when Tomcat loads it.
On some other servers, like JBoss or Weblogic, a war file will not be exploded.
If you explode the war, you will see that it contains the WEB-INF directory.
Look inside it to see the web.xml, /classes and /lib
You can deploy your application as a war, or copy the exploded war.
On Tomcat, when you deploy it as a war, it will automatically be exploded when Tomcat loads it.
On some other servers, like JBoss or Weblogic, a war file will not be exploded.
Packing our web application into a war
As mentioned before, to deploy our web application, we need to pack it into a war file.
We do this with the Ant "war" task.
Update your Ant build.xml 'dist' target as shown below:
In the war task, we tell Ant to build a HeloWorld.war, and we tell it where to get the web.xml, the classes and the lib folders.
Ant will automatically create a WEB-INF directory from this information.
So now if you run the Ant:dist target, you will see a HelloWorld.war in your /target
We do this with the Ant "war" task.
Update your Ant build.xml 'dist' target as shown below:
<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"/>
</war>
</target>
</project>
In the war task, we tell Ant to build a HeloWorld.war, and we tell it where to get the web.xml, the classes and the lib folders.
Ant will automatically create a WEB-INF directory from this information.
So now if you run the Ant:dist target, you will see a HelloWorld.war in your /target
web.xml
Create a file web.xml in your HelloWorld project root directory (i.e. in the same place as .classpath).
Write (or copy/paste) the following:
As mentioned before, web.xml is the master specification which tells the Web Server how to run our Application.
Let us understand what it says.
First it tells the Web Server to map the HelloWorld class (servlet) to the friendly name 'hello'
Then it tell the Web Server to redirect any request for the URL /home to the 'hello' servlet.
Write (or copy/paste) the following:
<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>
As mentioned before, web.xml is the master specification which tells the Web Server how to run our Application.
Let us understand what it says.
First it tells the Web Server to map the HelloWorld class (servlet) to the friendly name 'hello'
Then it tell the Web Server to redirect any request for the URL /home to the 'hello' servlet.
Servlets API
As mentioned earlier, it is the Java Servlet API which is the key to writing Java Web Applications.
Any class which extends the HTTPServlet abstract class can handle web requests.
Any class which extends HTTPServlet becomes a 'Servlet'.
So to make our HelloWorld class a Servlet, extend the HTTPServlet abstract class, as shown below:
Implementing the doGet method makes it possible for HelloWorld to receive HTTP requests.
Writing to the response will make the text appear on the browser.
In order for this class to compile, we need the servlet-api.jar
Update your ivy.xml file to get the servlet-api.jar, as shown below:
If you now run the ant:resolve task, it will download the servlet-api.jar to your /lib
For Eclipse to detect it, right click on HelloWorld project, the go to:
Properties->Java Build Path->Libraries->Add Jars
then add the /lib/servlet-api.jar
Now run the ant:compile task to compile HelloWorld.java
Any class which extends the HTTPServlet abstract class can handle web requests.
Any class which extends HTTPServlet becomes a 'Servlet'.
So to make our HelloWorld class a Servlet, extend the HTTPServlet abstract class, as shown 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
{
response.getWriter().println("Hello World!");
}
}
Implementing the doGet method makes it possible for HelloWorld to receive HTTP requests.
Writing to the response will make the text appear on the browser.
In order for this class to compile, we need the servlet-api.jar
Update your ivy.xml file to get the servlet-api.jar, as shown below:
<ivy-module version="2.0">
<info organisation="org.confucius" module="helloworld"/>
<dependencies>
<dependency org="javax.servlet" name="servlet-api" rev="2.5"/>
</dependencies>
</ivy-modu
If you now run the ant:resolve task, it will download the servlet-api.jar to your /lib
For Eclipse to detect it, right click on HelloWorld project, the go to:
Properties->Java Build Path->Libraries->Add Jars
then add the /lib/servlet-api.jar
Now run the ant:compile task to compile HelloWorld.java
WEB-INF and CLASSPATH
Back again to Classpath.
When the web server runs your web application, it looks at the WEB-INF/classes and WEB-INF/lib to find classes. If you put your class or jar somewhere else, it will not find it.
One result of this is that you will often find *.properties files in WEB-INF/classes.
Just like .class files, Java classloaders also look for .properties files in the Classpath. So if your application needs a .properties file, it needs to be in the Classpath - which for a web application is the WEB-INF/classes or WEB-INF/lib
Maybe there should have been a WEB-INF/properties - but somebody didn't think of that, so we have to do this workaround of putting the properties files in /classes.
When the web server runs your web application, it looks at the WEB-INF/classes and WEB-INF/lib to find classes. If you put your class or jar somewhere else, it will not find it.
One result of this is that you will often find *.properties files in WEB-INF/classes.
Just like .class files, Java classloaders also look for .properties files in the Classpath. So if your application needs a .properties file, it needs to be in the Classpath - which for a web application is the WEB-INF/classes or WEB-INF/lib
Maybe there should have been a WEB-INF/properties - but somebody didn't think of that, so we have to do this workaround of putting the properties files in /classes.
WEB-INF
A Java Web Application has certain specifications.
First, it should be packaged as a 'war' file. A 'war' file is a special case of a 'jar' file. It tells the web server that this file contains a web application.
Second, it should contain a folder called 'WEB-INF'.
WEB-INF itself should contain two folders: /classes and /lib, which contain all your Java classes and external libraries (jars)
WEB-INF should also contain a web.xml - this is the master file which tells the web server how to run your application.
Besides the WEB-INF, the web application can contain any number of other directories, for example /images, /jsp, /html, /js, etc
The difference between WEB-INF and all the other directories is that the web server will NEVER allow direct access to the contents of WEB-INF.
For example, if a user pointed their browser to:
http://www.foo.com/yourapp/WEB-INF/../..
they will get an error.
But if they point to:
http://www.foo.com/yourapp/some-other-folder/../..
they will get the file if it exists.
Contents of WEB-INF can only be accessed by classes and libraries inside WEB-INF.
In other words, WEB-INF is the private and mandatory directory in your web application. All other directories are public and optional.
First, it should be packaged as a 'war' file. A 'war' file is a special case of a 'jar' file. It tells the web server that this file contains a web application.
Second, it should contain a folder called 'WEB-INF'.
WEB-INF itself should contain two folders: /classes and /lib, which contain all your Java classes and external libraries (jars)
WEB-INF should also contain a web.xml - this is the master file which tells the web server how to run your application.
Besides the WEB-INF, the web application can contain any number of other directories, for example /images, /jsp, /html, /js, etc
The difference between WEB-INF and all the other directories is that the web server will NEVER allow direct access to the contents of WEB-INF.
For example, if a user pointed their browser to:
http://www.foo.com/yourapp/WEB-INF/../..
they will get an error.
But if they point to:
http://www.foo.com/yourapp/some-other-folder/../..
they will get the file if it exists.
Contents of WEB-INF can only be accessed by classes and libraries inside WEB-INF.
In other words, WEB-INF is the private and mandatory directory in your web application. All other directories are public and optional.
Friday, November 18, 2011
Tomcat is a Java app
Note that Tomcat itself is written in Java.
If you look inside the /bin and /lib directories of your Tomcat installation, you will see jar files.
As is true of all Java programs, it is the JRE that is running Tomcat.
Just like how we saw earlier how Eclipse is a Java app running inside the JRE.
If you look inside the /bin and /lib directories of your Tomcat installation, you will see jar files.
As is true of all Java programs, it is the JRE that is running Tomcat.
Just like how we saw earlier how Eclipse is a Java app running inside the JRE.
Tomcat - creating an admin account
Creating a admin account allows you to manage Tomcat from your browser.
The management options are usually available from the Welcome screen when you point your browser to:
http://localhost:8080/
Different versions of Tomcat can have different ways to create admin and user accounts
(they keep enhancing this)
For Tomcat 7.0, open the tomcat-users.xml file that you will find in your Tomcat deployment in the /conf directory.
Add to it an admin user with the 'manager-gui' role, as shown below:
(I have removed all the comments from the file, for clarity)
manager-gui is one of the Tomcat roles, which allows you to manage Tomcat from the browser. There are also other roles which you can read up in the Tomcat documentation.
You can create as many users as you want, and assign them any combination of roles.
The management options are usually available from the Welcome screen when you point your browser to:
http://localhost:8080/
Different versions of Tomcat can have different ways to create admin and user accounts
(they keep enhancing this)
For Tomcat 7.0, open the tomcat-users.xml file that you will find in your Tomcat deployment in the /conf directory.
Add to it an admin user with the 'manager-gui' role, as shown below:
(I have removed all the comments from the file, for clarity)
<?xml version='1.0' encoding='cp1252'?>
<tomcat-users>
<user username="admin" password="s3cret" roles="manager-gui"/>
</tomcat-users>
manager-gui is one of the Tomcat roles, which allows you to manage Tomcat from the browser. There are also other roles which you can read up in the Tomcat documentation.
You can create as many users as you want, and assign them any combination of roles.
Tomcat - Download / Install
Download the installer from Apache Tomcat's website and run it.
You can use all the defaults in the wizard.
It will create an entry in the Start->Programs for 'Monitor Tomcat'.
If you run this utility, a small icon will appear in your Start task bar tray (near the clock in the bottom right corner)
Right clicking on this monitor utility allows you to start/stop Tomcat.
Once Tomcat is started, point your browser to:
http://localhost:8080
You should see the Tomcat welcome screen.
You can use all the defaults in the wizard.
It will create an entry in the Start->Programs for 'Monitor Tomcat'.
If you run this utility, a small icon will appear in your Start task bar tray (near the clock in the bottom right corner)
Right clicking on this monitor utility allows you to start/stop Tomcat.
Once Tomcat is started, point your browser to:
http://localhost:8080
You should see the Tomcat welcome screen.
Thursday, November 17, 2011
Understanding URLs - protocols, domains and ports
URLs are at the heart of the internet. So let us make sure we understand them clearly.
URLs are of the form:
protocol://domain:port/something/something/something/something/..
In browsers, protocol is almost always http.
But it can also be ftp, sftp, https, or your_custom_protocol.
Protocol tells the browser how to communicate with the server.
If you are communicating with a http server, you need to specify the protocol as http. Obviously, if you specify a different protocol, say ftp, you will get an error - because the browser will be talking greek to a french server.
Secondly, note that while the URL format does not stop you from specifying any protocol, even your_custom_protocol, it will work only if the browser knows how to communicate in that protocol. Most modern browsers can do http, ftp, sftp, https.
Domain is the DNS name of the server - like www.yahoo.com, www.google.com, www.apache.org
The browser first contacts the DNS server of your network service provider, to find out the IP address of the server that is registered for that domain. Once it knows the IP address, it can contact the server.
Port is very often not specified, and defaults to 80 for http (21 for ftp, 22 for sftp, 443 for https).
What exactly is port?
Port comes from TCP. Suppose you run two different network services on the same computer - say service1 and service2. When a network packet is received, how can TCP know which service to give the packet to? It uses a number called port. Port is just an integer and, in principle, can be anything >= 1. That said, several port numbers have been reserved and are called well-known ports - 80 for HTTP, 21 for FTP, etc.
When you start a TCP/IP service on a computer, you have to specify a port number.
When clients try to access this service, they need to specify the port number.
If you create your_custom_service, and you start it on the server at port 998, then your URL will have to specify 998.
If you start a HTTP server, but use a non-default port, say 212 - then you will need to specify 212 in your URL. Otherwise, by default the browser will use 80 - and it will give you an error that no HTTP service is running on port 80.
The last part of the URL /something/something/something - is of no importance to the browser. It doesn't care to understand it. It just passes it as a string to the web server. It is for the server to interpret it.
How does the server interpret it?
Depends on the server.
A simple HTTP server can interpret it as dir1/dir2/dir3/file
A Java web server can interpret it as context/servlet/args
We will learn more about this (something/something/something) part of the URL later.
For now, know that the browser does not interpret it at all.
URLs are of the form:
protocol://domain:port/something/something/something/something/..
In browsers, protocol is almost always http.
But it can also be ftp, sftp, https, or your_custom_protocol.
Protocol tells the browser how to communicate with the server.
If you are communicating with a http server, you need to specify the protocol as http. Obviously, if you specify a different protocol, say ftp, you will get an error - because the browser will be talking greek to a french server.
Secondly, note that while the URL format does not stop you from specifying any protocol, even your_custom_protocol, it will work only if the browser knows how to communicate in that protocol. Most modern browsers can do http, ftp, sftp, https.
Domain is the DNS name of the server - like www.yahoo.com, www.google.com, www.apache.org
The browser first contacts the DNS server of your network service provider, to find out the IP address of the server that is registered for that domain. Once it knows the IP address, it can contact the server.
Port is very often not specified, and defaults to 80 for http (21 for ftp, 22 for sftp, 443 for https).
What exactly is port?
Port comes from TCP. Suppose you run two different network services on the same computer - say service1 and service2. When a network packet is received, how can TCP know which service to give the packet to? It uses a number called port. Port is just an integer and, in principle, can be anything >= 1. That said, several port numbers have been reserved and are called well-known ports - 80 for HTTP, 21 for FTP, etc.
When you start a TCP/IP service on a computer, you have to specify a port number.
When clients try to access this service, they need to specify the port number.
If you create your_custom_service, and you start it on the server at port 998, then your URL will have to specify 998.
If you start a HTTP server, but use a non-default port, say 212 - then you will need to specify 212 in your URL. Otherwise, by default the browser will use 80 - and it will give you an error that no HTTP service is running on port 80.
The last part of the URL /something/something/something - is of no importance to the browser. It doesn't care to understand it. It just passes it as a string to the web server. It is for the server to interpret it.
How does the server interpret it?
Depends on the server.
A simple HTTP server can interpret it as dir1/dir2/dir3/file
A Java web server can interpret it as context/servlet/args
We will learn more about this (something/something/something) part of the URL later.
For now, know that the browser does not interpret it at all.
Wednesday, November 16, 2011
Java-enabled Web Servers
For our application to be accessible from a browser, it has to be deployed on a Web Server.
Because we are writing our application in Java, it has to be deployed on a Java-enabled Web Server.
A Java-enabled Web Server is one that implements the so called 'Servlet API'.
Servlet API allows us to write Java Applications which can respond to HTTP requests from a browser.
The most popular Java-enabled Web Server is Apache Tomcat. Others are JBoss, BEA WebLogic, IBM WebSphere, Sun GlassFish, Jetty, Apache Geronimo, etc
Each has its pros and cons - we will try and play with as many of them as possible (some are commercial, so we can't play with them).
Because we are writing our application in Java, it has to be deployed on a Java-enabled Web Server.
A Java-enabled Web Server is one that implements the so called 'Servlet API'.
Servlet API allows us to write Java Applications which can respond to HTTP requests from a browser.
The most popular Java-enabled Web Server is Apache Tomcat. Others are JBoss, BEA WebLogic, IBM WebSphere, Sun GlassFish, Jetty, Apache Geronimo, etc
Each has its pros and cons - we will try and play with as many of them as possible (some are commercial, so we can't play with them).
Creating a Web Application
Our HelloWorld application needs to be accessible from a browser - it has to be a web application.
For this we need to understand the difference between Desktop and Web Applications.
The main difference is that Desktop applications are 'multi-deploy' and 'single-user'.
Web Applications are 'single-deploy' and 'multi-user'.
Because a Desktop Application is tied to the computer on which it is installed, it enjoys implicit security. Only the user who owns the computer can use the application (assuming the computer is physically secured). Secondly, any data that needs to be persisted can be written to files on that computer. Thirdly, if the installation runs into issues, or there is a bug, only the single user is affected.
Web Applications need to be secured via logins and firewalls. Secondly, data needs to be persisted in a database, because with potentially millions of users using it simultaneously, reading and writing to files will not scale. Thirdly, any downtime or bug affects ALL users - for a business critical application, this is a catastrophe!
Inspite of this, web applications are very popular - how else can google and amazon reach out to millions of users?!
Web applications are lot more challenging to design. The good news is that a lot of solutions have already been found. We will learn about these solutions step by step.
For this we need to understand the difference between Desktop and Web Applications.
The main difference is that Desktop applications are 'multi-deploy' and 'single-user'.
Web Applications are 'single-deploy' and 'multi-user'.
Because a Desktop Application is tied to the computer on which it is installed, it enjoys implicit security. Only the user who owns the computer can use the application (assuming the computer is physically secured). Secondly, any data that needs to be persisted can be written to files on that computer. Thirdly, if the installation runs into issues, or there is a bug, only the single user is affected.
Web Applications need to be secured via logins and firewalls. Secondly, data needs to be persisted in a database, because with potentially millions of users using it simultaneously, reading and writing to files will not scale. Thirdly, any downtime or bug affects ALL users - for a business critical application, this is a catastrophe!
Inspite of this, web applications are very popular - how else can google and amazon reach out to millions of users?!
Web applications are lot more challenging to design. The good news is that a lot of solutions have already been found. We will learn about these solutions step by step.
Log4j - Configuration
So far we have been using log4j in its most basic configuration, which sent message to the console.
Let us now upgrade it to write to a log file, in addition to console.
To do this, create a log4j.properties in your HelloWorld project directory (i.e. in the same pace as .classpath).
Type or copy-paste the following:
Let us understand this configuration.
We are configuring the root logger with DEBUG level - this means that all log messages will get logged.
We are attaching it to two appenders - one which writes to the console, and the other to a file. We want the log file to be named HelloWorld.log.
RollingFileAppender, by default, will limit the log file size to 10 MB. When that limit is reached, it will start a new log file. This is very important for web applications which run for months without restarts. It prevents log files from getting humongous and un-openable in editors. Note that the RollingFileAppender can be configured in various ways - the most popular one being to roll log files on a daily basis (instead of by size).
we are associating a pattern to the appenders to tell it how to format the messages.
%p --> debug level of the message
%d --> time stamp
%C --> class which generated the message
%L --> line of code which generated the message
%n --> newline
%m --> message to be logged
Update your HelloWorld.java code to load this configuration, as shown below:
Now if you rebuild and run HelloWorld.jar, you will see a HelloWorld.log file created.
Let us now upgrade it to write to a log file, in addition to console.
To do this, create a log4j.properties in your HelloWorld project directory (i.e. in the same pace as .classpath).
Type or copy-paste the following:
# Set root logger level to DEBUG and its only appender to Appender1 & Appender2.
log4j.rootLogger=DEBUG, Appender1,Appender2
# Appender1 is set to be a ConsoleAppender.
log4j.appender.Appender1=org.apache.log4j.ConsoleAppender
log4j.appender.Appender2=org.apache.log4j.RollingFileAppender
log4j.appender.Appender2.File=HelloWorld.log
# Appender1 uses PatternLayout.
log4j.appender.Appender1.layout=org.apache.log4j.PatternLayout
log4j.appender.Appender1.layout.ConversionPattern=%p %d %C %L %n %m %n
log4j.appender.Appender2.layout=org.apache.log4j.PatternLayout
log4j.appender.Appender2.layout.ConversionPattern=%p %d %C %L %n %m %n
Let us understand this configuration.
We are configuring the root logger with DEBUG level - this means that all log messages will get logged.
We are attaching it to two appenders - one which writes to the console, and the other to a file. We want the log file to be named HelloWorld.log.
RollingFileAppender, by default, will limit the log file size to 10 MB. When that limit is reached, it will start a new log file. This is very important for web applications which run for months without restarts. It prevents log files from getting humongous and un-openable in editors. Note that the RollingFileAppender can be configured in various ways - the most popular one being to roll log files on a daily basis (instead of by size).
we are associating a pattern to the appenders to tell it how to format the messages.
%p --> debug level of the message
%d --> time stamp
%C --> class which generated the message
%L --> line of code which generated the message
%n --> newline
%m --> message to be logged
Update your HelloWorld.java code to load this configuration, as shown below:
package org.confucius;
import org.apache.log4j.PropertyConfigurator;
import org.apache.log4j.Logger;
public class HelloWorld {
public static void main(String[] args) {
PropertyConfigurator.configure("log4j.properties");
Logger logger = Logger.getLogger(HelloWorld.class);
logger.debug("Hello World!");
}
}
Now if you rebuild and run HelloWorld.jar, you will see a HelloWorld.log file created.
Tuesday, November 15, 2011
Thinking about Classpaths..
Let us take a moment to recollect how many different Classpaths we have specified so far:
- A Classpath as a environment variable when we ran javac from a command line
- A Classpath in build.xml which Ant used to build HelloWorld.jar
- A Classpath in MANIFEST.MF which java used to execute the HelloWorld.jar
- A Classpath in Eclipse so it knew how to compile HelloWorld.java
- A Classpath for Ant in Eclipse so it could load Ivy
- A Classpath for Eclipse Run Configuration so it could find HelloWorld.jar
As you can see, Classpath is a ghost that will always follow you in Java(!)
Because every Java program comes with its own ClassLoader which needs a Classpath.
- A Classpath as a environment variable when we ran javac from a command line
- A Classpath in build.xml which Ant used to build HelloWorld.jar
- A Classpath in MANIFEST.MF which java used to execute the HelloWorld.jar
- A Classpath in Eclipse so it knew how to compile HelloWorld.java
- A Classpath for Ant in Eclipse so it could load Ivy
- A Classpath for Eclipse Run Configuration so it could find HelloWorld.jar
As you can see, Classpath is a ghost that will always follow you in Java(!)
Because every Java program comes with its own ClassLoader which needs a Classpath.
Eclipse - Run Configuration
To run HelloWorld.jar in Eclipse, we will create a 'Run Configuration'
Go to Run->Run COnfigurations->Java Application->HelloWorld
Eclipse created this Run Configuration when we earlier ran the /bin/HelloWorld.class
Now we want to run /target/HelloWorld.jar
Go to the Classpath tab -> User Entries:
Remove -> 'HelloWorld(default classpath)'
Add jar -> /target/HelloWorld.jar
If you run this configuration, it will execute the HelloWorld.jar
Go to Run->Run COnfigurations->Java Application->HelloWorld
Eclipse created this Run Configuration when we earlier ran the /bin/HelloWorld.class
Now we want to run /target/HelloWorld.jar
Go to the Classpath tab -> User Entries:
Remove -> 'HelloWorld(default classpath)'
Add jar -> /target/HelloWorld.jar
If you run this configuration, it will execute the HelloWorld.jar
Ant in Eclipse - Which JDK?
When we ran Ant from command line, it picked the JDK from the JAVA_HOME environment variable.
But when we run it in Eclipse, it picks the JDK provided by Eclipse (which is the JDK specified in the eclipse.ini file, with the -vm argument).
But when we run it in Eclipse, it picks the JDK provided by Eclipse (which is the JDK specified in the eclipse.ini file, with the -vm argument).
Ant in Eclipse - Which CLASSPATH? -
Remember that Eclipse uses the .classpath file to decide the CLASSPATH
Ant uses the CLASSPATH specified in the build.xml
If there is a mismatch, then it is possible that one of them will show an error while the other is happy.
Note also that in our current configuration, Eclipse is compiling to the /bin while Ant is compiling to the /classes
Ant uses the CLASSPATH specified in the build.xml
If there is a mismatch, then it is possible that one of them will show an error while the other is happy.
Note also that in our current configuration, Eclipse is compiling to the /bin while Ant is compiling to the /classes
Eclipse - Ant & Ivy
Eclipse is Ant ready out of the box.
To make it Ivy ready, go to Window->Preferences->Ant->Runtime->Classpath->Global Entries
Click on 'Add External Jars' and add the Ivy jar that we downloaded earlier.
Now it is Ivy ready.
Copy the build.xml, ivy.xml, ivysettings.xml and ivysettings-public.xml to the HelloWorld project folder (in the root directory, same place as .classpath)
Go to Window->Show View->Ant - this will open the Ant viewer
Drag drop the build.xml to this viewer - you will now be able to see all the Ant targets in the build.xml
Double-click on the 'dist' target to build the HelloWorld.jar
In the Navigator view, select the HelloWorld project and hit F5 to refresh.
You will now see the /target and /classes directories that were added by the Ant build.
If you delete the /lib directory, then rerun Ant 'dist' and refresh - you will see the /lib reappear with the log4j.jar - proof that Ivy is working.
To make it Ivy ready, go to Window->Preferences->Ant->Runtime->Classpath->Global Entries
Click on 'Add External Jars' and add the Ivy jar that we downloaded earlier.
Now it is Ivy ready.
Copy the build.xml, ivy.xml, ivysettings.xml and ivysettings-public.xml to the HelloWorld project folder (in the root directory, same place as .classpath)
Go to Window->Show View->Ant - this will open the Ant viewer
Drag drop the build.xml to this viewer - you will now be able to see all the Ant targets in the build.xml
Double-click on the 'dist' target to build the HelloWorld.jar
In the Navigator view, select the HelloWorld project and hit F5 to refresh.
You will now see the /target and /classes directories that were added by the Ant build.
If you delete the /lib directory, then rerun Ant 'dist' and refresh - you will see the /lib reappear with the log4j.jar - proof that Ivy is working.
Eclipse constantly compiles code
Remember that Eclipse compiles code constantly as you type!
It uses the .classpath to locate classes and jars.
That is how it is able to red mark problems and also show you helpful popups (like a list of public methods in a class).
It uses the .classpath to locate classes and jars.
That is how it is able to red mark problems and also show you helpful popups (like a list of public methods in a class).
Eclipse - HelloWorld
Right click on your HelloWorld project and select New Class
In the wizard, enter the package name 'org.confucius' and the class name 'HelloWorld'
A new file HelloWorld.java will be created in the /src/org/confucius folder
Enter the following code - it is the same code which we wrote earlier.
(Notice that this time we have a package org.confucius for the class)
Eclipse will red mark all references to the log4j classes because we do not have log4j.jar in the CLASSPATH
Let us add log4j.jar
Right Click on the HelloWorld project directory, and select New->Folder
Name the folder 'lib'
Copy the log4j.jar to this folder
Right Click on the HelloWorld project directory, and select Properties->Java Build Path->Libraries
Click on 'Add Jars' and select the /lib/log4j.jar
The red marks should all disappear and you will see the HelloWorld.class created in /bin/org/confucius
Right Click on the HelloWorld project directory, and select Run As->Java Application-->HelloWorld
You will see the output in the Eclipse Console.
Note: Open the .classpath and notice the entry for log4j.jar
In the wizard, enter the package name 'org.confucius' and the class name 'HelloWorld'
A new file HelloWorld.java will be created in the /src/org/confucius folder
Enter the following code - it is the same code which we wrote earlier.
(Notice that this time we have a package org.confucius for the class)
package org.confucius;
import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.Logger;
public class HelloWorld {
public static void main(String[] args) {
// Set up a simple configuration that logs on the console.
BasicConfigurator.configure();
Logger logger = Logger.getLogger(HelloWorld.class);
logger.debug("Hello World!");
}
}
Eclipse will red mark all references to the log4j classes because we do not have log4j.jar in the CLASSPATH
Let us add log4j.jar
Right Click on the HelloWorld project directory, and select New->Folder
Name the folder 'lib'
Copy the log4j.jar to this folder
Right Click on the HelloWorld project directory, and select Properties->Java Build Path->Libraries
Click on 'Add Jars' and select the /lib/log4j.jar
The red marks should all disappear and you will see the HelloWorld.class created in /bin/org/confucius
Right Click on the HelloWorld project directory, and select Run As->Java Application-->HelloWorld
You will see the output in the Eclipse Console.
Note: Open the .classpath and notice the entry for log4j.jar
Monday, November 14, 2011
Eclipse - .project and .classpath files
Two very important files in your Eclipse project are the .project and .classpath
Both of these are XML files.
.project contains all the information about your project and Eclipse reads this when it opens your project
.classpath contains the CLASSPATH of the project and Eclipse reads this when it compiles your project
Tip: If you give your Eclipse project to your colleagues, you will need to give them both these files. If your .classpath contains absolute paths that are specific to your machine, it can cause problems to other members of your team. This is a common problem when people checkin an Eclipse project into a source control system for team sharing.
Both of these are XML files.
.project contains all the information about your project and Eclipse reads this when it opens your project
.classpath contains the CLASSPATH of the project and Eclipse reads this when it compiles your project
Tip: If you give your Eclipse project to your colleagues, you will need to give them both these files. If your .classpath contains absolute paths that are specific to your machine, it can cause problems to other members of your team. This is a common problem when people checkin an Eclipse project into a source control system for team sharing.
Eclipse - Package Explorer and Navigator
Eclipse lets you see your project files in different 'views'.
You can open any View from Windows-->Show View
Two useful Vies are 'Package explorer' and "Navigator'
Package Explorer shows you all the java classes and libraries (jars) in your project - organized by the package each class belongs to.
Navigator is exactly like Windows Explorer - it shows you all the files in your project as appear in your file system.
We recommend you open both these views.
You can open any View from Windows-->Show View
Two useful Vies are 'Package explorer' and "Navigator'
Package Explorer shows you all the java classes and libraries (jars) in your project - organized by the package each class belongs to.
Navigator is exactly like Windows Explorer - it shows you all the files in your project as appear in your file system.
We recommend you open both these views.
Eclipse - HelloWorld project
If you haven't already done so, open the eclipse.ini file in your eclipse installation, and specify the -vm argument:
Create a workspace in your home directory called workspace-confucius.
For example, I have:
C:\Documents and Settings\LavanniM\workspace-confucius
Start Eclipse by double clicking on the eclipse.exe in your eclipse installation.
When it asks you for the workspace, specify the workspace-confucius folder.
When Eclipse comes up, it will show you the Welcome screen - you can close it, and then you will see the regular Eclipse editors.
Take a moment to look into the /workspace-confucius directory.
You will see that Eclipse has created the .metadata directory for this workspace.
In Eclipse, go to File->New->Project.. and select the Java-->Java Project
Name the project HelloWorld and use all the defaults in the Wizard
When the process completes, you will see the HelloWorld project in your Package Explorer window.
-vm
C:\Program Files\Java\jdk1.6.0_21\bin\javaw.exe
Create a workspace in your home directory called workspace-confucius.
For example, I have:
C:\Documents and Settings\LavanniM\workspace-confucius
Start Eclipse by double clicking on the eclipse.exe in your eclipse installation.
When it asks you for the workspace, specify the workspace-confucius folder.
When Eclipse comes up, it will show you the Welcome screen - you can close it, and then you will see the regular Eclipse editors.
Take a moment to look into the /workspace-confucius directory.
You will see that Eclipse has created the .metadata directory for this workspace.
In Eclipse, go to File->New->Project.. and select the Java-->Java Project
Name the project HelloWorld and use all the defaults in the Wizard
When the process completes, you will see the HelloWorld project in your Package Explorer window.
Eclipse - Workspace
Eclipse has this fundamental notion of 'workspace'.
A workspace is just a folder on your file system - something as simple as:
C:\users\joe\workspace
Eclipse uses this folder to keep all its projects. So for each Eclipse project, you will see a corresponding directory in the /workspace
In addition, you will see a directory called /.metadata - this is the place where Eclipse will keep all its workspace-wide settings.
For example, it will remember how you like your various editors configured, how you want to setup your run time configuration, how you want to connect to CVS or Subversion, which projects you have opened, etc
You can have multiple workspaces, for example:
C:\users\joe\workspace-my-projects
C:\users\joe\workspace-company-projects
C:\users\joe\workspace-experimental-projects
If you frequently switch between different versions of Eclipse, you might even have something like:
C:\users\joe\workspace\europa
C:\users\joe\workspace\helios
C:\users\joe\workspace\indigo
Everytime you open Eclipse, it will ask you which workspace you want to use, then load the settings and projects from that workspace.
Tip - Sometimes, due to strange reasons, your workspace can get corrupted and Eclipse will start throwing weird errors. Deleting the .metadata folder usually resolves these issues (but you have to redo all your workspace settings)
A workspace is just a folder on your file system - something as simple as:
C:\users\joe\workspace
Eclipse uses this folder to keep all its projects. So for each Eclipse project, you will see a corresponding directory in the /workspace
In addition, you will see a directory called /.metadata - this is the place where Eclipse will keep all its workspace-wide settings.
For example, it will remember how you like your various editors configured, how you want to setup your run time configuration, how you want to connect to CVS or Subversion, which projects you have opened, etc
You can have multiple workspaces, for example:
C:\users\joe\workspace-my-projects
C:\users\joe\workspace-company-projects
C:\users\joe\workspace-experimental-projects
If you frequently switch between different versions of Eclipse, you might even have something like:
C:\users\joe\workspace\europa
C:\users\joe\workspace\helios
C:\users\joe\workspace\indigo
Everytime you open Eclipse, it will ask you which workspace you want to use, then load the settings and projects from that workspace.
Tip - Sometimes, due to strange reasons, your workspace can get corrupted and Eclipse will start throwing weird errors. Deleting the .metadata folder usually resolves these issues (but you have to redo all your workspace settings)
java.exe vs javaw.exe
Inside your JRE /bin directory, you will find java.exe and javaw.exe - whats the difference?
java.exe and javaw.exe are the exact same thing, with one tiny difference. When you run java.exe, it opens in a console. On the other hand, javaw.exe does not pop up any console.
This is good when an external program, like eclipse.exe or your browser, runs JRE - so you do not see annoying console windows popping up.
(javaw >>> java "windowless")
java.exe and javaw.exe are the exact same thing, with one tiny difference. When you run java.exe, it opens in a console. On the other hand, javaw.exe does not pop up any console.
This is good when an external program, like eclipse.exe or your browser, runs JRE - so you do not see annoying console windows popping up.
(javaw >>> java "windowless")
Eclipse - the eclipse.ini file
If you understand this post, you will save yourself a lot of trouble later.
Eclipse is a java program - just a jar. So it needs to run inside a JRE.
The eclipse.exe is a misnomer - it is not the eclipse program, but a program which starts the eclipse program. It does a few initialization things, then calls the eclipse jar with appropriate arguments (I am only showing the most important -jar argument here):
We are familiar with the above command. We are calling the JRE and giving it a -jar argument, pointing to the actual jar which runs Eclipse.
You could have as well called this command from the command line yourself, instead of running eclipse.exe (the misnomer).
Why is all this so important?
Because the immediate question to ask yourself is which JRE is Eclipse running under?
If you just think of the above command:
we can be sure that the OS will look for the 'java' program in its PATH, and execute the first JRE it finds.
This is problematic.
Why?
Because what if we have multiple installs of JDK on our machine (this is quite common) - JDK5, JDK6 and JDK7?
If we are not absolutely sure what is in our PATH, we cannot be sure what Eclipse is running in. Consequently, when we compile our java source in Eclipse, we do not know for sure if we compiled in JDK5, JDK6 or JDK7!
This can lead to serious compilation issues. Sometimes it will give compilation errors because it can't find the necessary libraries, other times it will compile against an older version of the libraries, etc, etc.
To avoid going down this labyrinth of problems, we have the eclipse.ini file.
Instead of relying on the OS to figure out what JRE to use, we will specify it upfront - so we can be absolutely sure what version of JDK we are using.
Open the eclipse.ini file in a text editor - you will find this file in the top directory of your eclipse installation.
At the very top, add the following two lines:
This tell eclipse which Virtual Machine to use.
Note that we are specifying the javaw.exe, and not java.exe - what is the difference?
java.exe and javaw.exe are the exact same thing, with one tiny difference. When you run java.exe, it opens in a console. On the other hand, javaw.exe does not pop up any console.
This is good when an external program, like eclipse.exe or your browser, is runs JRE - so you do not see annoying console windows popping up.
Eclipse is a java program - just a jar. So it needs to run inside a JRE.
The eclipse.exe is a misnomer - it is not the eclipse program, but a program which starts the eclipse program. It does a few initialization things, then calls the eclipse jar with appropriate arguments (I am only showing the most important -jar argument here):
> java -jar eclipse/plugins/org.eclipse.equinox.launcher_1.0.0.v20070606.jar ..
We are familiar with the above command. We are calling the JRE and giving it a -jar argument, pointing to the actual jar which runs Eclipse.
You could have as well called this command from the command line yourself, instead of running eclipse.exe (the misnomer).
Why is all this so important?
Because the immediate question to ask yourself is which JRE is Eclipse running under?
If you just think of the above command:
> java -jar eclipse/plugins/org.eclipse.equinox.launcher_1.0.0.v20070606.jar ..
we can be sure that the OS will look for the 'java' program in its PATH, and execute the first JRE it finds.
This is problematic.
Why?
Because what if we have multiple installs of JDK on our machine (this is quite common) - JDK5, JDK6 and JDK7?
If we are not absolutely sure what is in our PATH, we cannot be sure what Eclipse is running in. Consequently, when we compile our java source in Eclipse, we do not know for sure if we compiled in JDK5, JDK6 or JDK7!
This can lead to serious compilation issues. Sometimes it will give compilation errors because it can't find the necessary libraries, other times it will compile against an older version of the libraries, etc, etc.
To avoid going down this labyrinth of problems, we have the eclipse.ini file.
Instead of relying on the OS to figure out what JRE to use, we will specify it upfront - so we can be absolutely sure what version of JDK we are using.
Open the eclipse.ini file in a text editor - you will find this file in the top directory of your eclipse installation.
At the very top, add the following two lines:
-vm
C:\Program Files\Java\jdk1.6.0_21\bin\javaw.exe
This tell eclipse which Virtual Machine to use.
Note that we are specifying the javaw.exe, and not java.exe - what is the difference?
java.exe and javaw.exe are the exact same thing, with one tiny difference. When you run java.exe, it opens in a console. On the other hand, javaw.exe does not pop up any console.
This is good when an external program, like eclipse.exe or your browser, is runs JRE - so you do not see annoying console windows popping up.
Eclipse - Download
Download eclipse from their website - it downloads as a zip file.
Unzip it to an appropriate directory (say, C:\Program Files\Eclipse)
Note that Eclipse versions have alphabetical names - europa, galileo, helios, indigo.
If you want to have multiple versions of Eclipse, you might want to install them in different folders:
C:\Program Files\Eclipse\europa
C:\Program Files\Eclipse\galileo
C:\Program Files\Eclipse\helios
C:\Program Files\Eclipse\indigo
That way you can use any version of Eclipse as necessary.
You will see an eclipse.exe in the top folder. You can double click on this to start Eclipse, or copy it to your Desktop or Start Bar as a shortcut.
To start Eclipse from command line, add the Eclipse directory to your PATH environment attribute.
Unzip it to an appropriate directory (say, C:\Program Files\Eclipse)
Note that Eclipse versions have alphabetical names - europa, galileo, helios, indigo.
If you want to have multiple versions of Eclipse, you might want to install them in different folders:
C:\Program Files\Eclipse\europa
C:\Program Files\Eclipse\galileo
C:\Program Files\Eclipse\helios
C:\Program Files\Eclipse\indigo
That way you can use any version of Eclipse as necessary.
You will see an eclipse.exe in the top folder. You can double click on this to start Eclipse, or copy it to your Desktop or Start Bar as a shortcut.
To start Eclipse from command line, add the Eclipse directory to your PATH environment attribute.
Friday, November 11, 2011
Eclipse
For real Java projects, you need more that a text editor to work efficiently.
You need an IDE (Integrated Development Environment) which can manage your source files, help you code faster, check your code as you type, manage your deployment, testing, etc,
Enter Eclipse - the free IDE.
Eclipse can do a lot of things out of the box. Additionally, people have written all kinds of plugins which make it do even more.
FYI - Eclipse releases are usually named in alphabetical order - Callista, Europa, Ganymede, Galileo, Helios, Indigo.
You need an IDE (Integrated Development Environment) which can manage your source files, help you code faster, check your code as you type, manage your deployment, testing, etc,
Enter Eclipse - the free IDE.
Eclipse can do a lot of things out of the box. Additionally, people have written all kinds of plugins which make it do even more.
FYI - Eclipse releases are usually named in alphabetical order - Callista, Europa, Ganymede, Galileo, Helios, Indigo.
Ivy - Custom Configuration
In addition to the Maven Central Repository, let us say we want Ivy to also look up the JBoss public repository at https://repository.jboss.org/nexus/content/repositories/
The best way to configure Ivy is to start with the default configuration and incrementally introduce our changes.
Create a ivysettings.xml in your /confucius directory (i.e. the same directory where you have HelloWorld.java) and make it match the default ivysettings.xml.
Like this:
Next, we will override the default ivysettings-public.xml
We will create our own ivysettings-public.xml in the /confucius directory and point the ivysettings.xml to it.
So your updated ivysettings.xml will look like this:
Now create a ivysettings-public.xml in your /confucius directory and type (or copy-paste) the following in it:
This tells Ivy to use both the Maven central repository and the JBoss public repository when looking for jars.
Note that we have used the 'Url' Resolver - this Resolver looks for jars in any repository accessible with urls.
The best way to configure Ivy is to start with the default configuration and incrementally introduce our changes.
Create a ivysettings.xml in your /confucius directory (i.e. the same directory where you have HelloWorld.java) and make it match the default ivysettings.xml.
Like this:
<ivysettings>
<settings defaultResolver="default"/>
<include url="${ivy.default.settings.dir}/ivysettings-public.xml"/>
<include url="${ivy.default.settings.dir}/ivysettings-shared.xml"/>
<include url="${ivy.default.settings.dir}/ivysettings-local.xml"/>
<include url="${ivy.default.settings.dir}/ivysettings-main-chain.xml"/>
<include url="${ivy.default.settings.dir}/ivysettings-default-chain.xml"/>
</ivysettings>
Next, we will override the default ivysettings-public.xml
We will create our own ivysettings-public.xml in the /confucius directory and point the ivysettings.xml to it.
So your updated ivysettings.xml will look like this:
<ivysettings>
<settings defaultResolver="default"/>
<include url="./ivysettings-public.xml"/>
<include url="${ivy.default.settings.dir}/ivysettings-shared.xml"/>
<include url="${ivy.default.settings.dir}/ivysettings-local.xml"/>
<include url="${ivy.default.settings.dir}/ivysettings-main-chain.xml"/>
<include url="${ivy.default.settings.dir}/ivysettings-default-chain.xml"/>
</ivysettings>
Now create a ivysettings-public.xml in your /confucius directory and type (or copy-paste) the following in it:
<ivysettings>
<resolvers>
<url name="public">
<artifact pattern="http://repo1.maven.org/maven2/[organization]/[module]/[revision]/[artifact]-[revision].[ext]" />
<artifact pattern="https://repository.jboss.org/nexus/content/repositories/[organization]/[module]/[revision]/[artifact]-[revision].[ext]" />
</url>
</resolvers>
</ivysettings>
This tells Ivy to use both the Maven central repository and the JBoss public repository when looking for jars.
Note that we have used the 'Url' Resolver - this Resolver looks for jars in any repository accessible with urls.
Ivy - Default configuration
Ivy is configured in a ivysettings.xml file.
Now the interesting news - this ivysettings.xml can point to other settings.xml files, which in turn can point to other settings.xml and so on.
So, you can restrict ivy configuration to a single long ivysettings.xml file or distribute it to several short (specific) settings.xml files.
By default, Ivy ships with a ivysettings.xml file which points to 5 other settings.xml file.
It looks like this:
The ${ivy.default.settings.dir} by default points to the ./ivy directory in your home directory
The ivysettings-public.xml, ivysettings-shared.xml, ivysettings-local.xml files point to resolvers for your public, shared and local repositories.
The ivysettings-main-chain.xml and ivysettings-default-chain.xml specify the order in which Ivy will look at repositories.
Now the interesting news - this ivysettings.xml can point to other settings.xml files, which in turn can point to other settings.xml and so on.
So, you can restrict ivy configuration to a single long ivysettings.xml file or distribute it to several short (specific) settings.xml files.
By default, Ivy ships with a ivysettings.xml file which points to 5 other settings.xml file.
It looks like this:
<ivysettings>
<settings defaultResolver="default"/>
<include url="${ivy.default.settings.dir}/ivysettings-public.xml"/>
<include url="${ivy.default.settings.dir}/ivysettings-shared.xml"/>
<include url="${ivy.default.settings.dir}/ivysettings-local.xml"/>
<include url="${ivy.default.settings.dir}/ivysettings-main-chain.xml"/>
<include url="${ivy.default.settings.dir}/ivysettings-default-chain.xml"/>
</ivysettings>
The ${ivy.default.settings.dir} by default points to the ./ivy directory in your home directory
The ivysettings-public.xml, ivysettings-shared.xml, ivysettings-local.xml files point to resolvers for your public, shared and local repositories.
The ivysettings-main-chain.xml and ivysettings-default-chain.xml specify the order in which Ivy will look at repositories.
Ivy - More about Repositories
Ivy looks for jars in not one, but three repositories - local, shared and public.
Local is your private repository - if you want Ivy to pick a certain specific jar no matter what else happens in the world outside, this is the place to put it.
Shared is a repository shared with your team. If there are certain jars that all your team members must use, this is the place to keep them.
Public is a repository that you do not expect to have control over - like the central Maven Repository.
By default, Ivy will looks for a jar first in the local, then in the shared and finally in the public repository.
Local is your private repository - if you want Ivy to pick a certain specific jar no matter what else happens in the world outside, this is the place to put it.
Shared is a repository shared with your team. If there are certain jars that all your team members must use, this is the place to keep them.
Public is a repository that you do not expect to have control over - like the central Maven Repository.
By default, Ivy will looks for a jar first in the local, then in the shared and finally in the public repository.
Thursday, November 10, 2011
Ivy - Resolvers
Just like JRE uses ClassLoaders to find and load classes, Ivy uses Resolvers to find and download jars.
Ivy comes with standard Resolvers. But anyone can write custom Resolvers if they want to.
Each Resolver has its won specific way of configuration, which tells it where to download the jars from.
By default, Ivy will use its standard Resolvers configured in a standard way.
If necessary, you can override the standard settings to tell Ivy to use a Resolver of your choice, configured your way.
In an upcoming post, we will use a Resolver to direct Ivy to use a non-default repository.
Ivy comes with standard Resolvers. But anyone can write custom Resolvers if they want to.
Each Resolver has its won specific way of configuration, which tells it where to download the jars from.
By default, Ivy will use its standard Resolvers configured in a standard way.
If necessary, you can override the standard settings to tell Ivy to use a Resolver of your choice, configured your way.
In an upcoming post, we will use a Resolver to direct Ivy to use a non-default repository.
Ivy in Ant
You have to do 3 things.
One, create an xmlns:ivy name space in your build.xml
Two, create a ivy:retrieve target
Three, create a file ivy.xml and define your dependencies
See this HelloWorld build.xml updated to use Ivy - note that we have made the 'init' target dependent on the 'resolve' target:
Here is the ivy.xml to download the log4j.jar - notice how the log4j co-ordinates are specified using the org, name and rev attributes.
Now if you run Ant, it will call Ivy to do the log4j download (see below)
One, create an xmlns:ivy name space in your build.xml
Two, create a ivy:retrieve target
Three, create a file ivy.xml and define your dependencies
See this HelloWorld build.xml updated to use Ivy - note that we have made the 'init' target dependent on the 'resolve' target:
<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/log4j-1.2.16.jar"/>
</classpath>
</javac>
</target>
<target name="dist" depends="compile">
<jar jarfile="target/HelloWorld.jar" basedir="classes">
<manifest>
<attribute name="Main-Class" value="HelloWorld"/>
<attribute name="Class-Path" value="HelloWorld.jar ../lib/log4j-1.2.16.jar"/>
</manifest>
</jar>
</target>
</project>
Here is the ivy.xml to download the log4j.jar - notice how the log4j co-ordinates are specified using the org, name and rev attributes.
<ivy-module version="2.0">
<info organisation="org.confucius" module="helloworld"/>
<dependencies>
<dependency org="log4j" name="log4j" rev="1.2.16"/>
</dependencies>
</ivy-module>
Now if you run Ant, it will call Ivy to do the log4j download (see below)
C:\users\LavanniM\confucius>ant dist
Buildfile: C:\users\LavanniM\confucius\build.xml
resolve:
[ivy:retrieve] :: Ivy 2.2.0 - 20100923230623 :: http://ant.apache.org/ivy/ ::
[ivy:retrieve] :: loading settings :: file = C:\users\LavanniM\confucius\ivysettings.xml
[ivy:retrieve] :: resolving dependencies :: org.confucius#helloworld;working@LavanniM-01
[ivy:retrieve] confs: [default]
[ivy:retrieve] found log4j#log4j;1.2.16 in default
[ivy:retrieve] :: resolution report :: resolve 93ms :: artifacts dl 0ms
---------------------------------------------------------------------
| | modules || artifacts |
| conf | number| search|dwnlded|evicted|| number|dwnlded|
---------------------------------------------------------------------
| default | 1 | 0 | 0 | 0 || 1 | 0 |
---------------------------------------------------------------------
[ivy:retrieve] :: retrieving :: org.confucius#helloworld
[ivy:retrieve] confs: [default]
[ivy:retrieve] 1 artifacts copied, 0 already retrieved (470kB/15ms)
init:
compile:
[javac] C:\users\LavanniM\confucius\build.xml:13: warning: 'includeantruntime' was not set, defaulting to build.sysclasspath=last; set to false for repeatable builds
dist:
BUILD SUCCESSFUL
Total time: 0 seconds
C:\users\LavanniM\confucius>
Ivy - Download / Install
Download Ivy from the Apache Ivy website, unpack the downloaded zip file wherever you want, and copy the ivy jar file into your Ant /lib directory.
Ivy - Repository 'co-ordinates'
If Ivy just told the repository server give me john.jar, the repository server will ask, "The one from John Lennon or John McEnroe? And you want version 1.0 or 2.6.2?"
This is called co-ordinates.
Each company/organization/individual that uploads jars to the repository is assigned a unique 'address space'. For example, log4j has 'log4j' (what a surprise), apache has 'org.apache', hibernate has 'org.hibernate', etc
Each company can then create its own address space inside for its different jars.
For example, Spring has the address space org.springframework, under which it has spring-aop, spring-core, spring-jmx, spring-jca, etc
So if Ivy wants the version 2.3 of spring-jmx.jar, it will tell the repository server:
organization = org.springframework
jar name = spring-jmx
revision = 2.3
These 3 specifications are called the co-ordinates of the jar.
This is called co-ordinates.
Each company/organization/individual that uploads jars to the repository is assigned a unique 'address space'. For example, log4j has 'log4j' (what a surprise), apache has 'org.apache', hibernate has 'org.hibernate', etc
Each company can then create its own address space inside for its different jars.
For example, Spring has the address space org.springframework, under which it has spring-aop, spring-core, spring-jmx, spring-jca, etc
So if Ivy wants the version 2.3 of spring-jmx.jar, it will tell the repository server:
organization = org.springframework
jar name = spring-jmx
revision = 2.3
These 3 specifications are called the co-ordinates of the jar.
Ivy
We have used the log4j.jar in our project.We downloaded this jar manually.
A typical Java project can use hundreds of external jars. To expect to download these manually, tracking version numbers and all that can quickly become a harrowing experience.
Enter Ivy.
Ivy is a Ant extension - tell Ivy what jars you want, and it will do the downloading for you.
That sounds simple enough. But how does Ivy know where to download the jar from?
From a place called 'repositories'.
A repository is an online directory (location is http://repo1.maven.org/maven2) which contains ALL jars from all over the world.
Whenever Mr. John Whoever writes a new library john.jar, he uploads it to the repository. When you tell Ivy you need john.jar, Ivy downloads it from the repository.
If this sounds too easy, and you are not yet confused - let us learn some more things about repository.
After Ivy downloads the john.jar, it keeps a local copy of it in a local directory - by default in your home directory/.ivy/
This way, Ivy does not have to download from the online (central) repository each time - this makes builds go faster.
Second, http://repo1.maven.org/maven2 is a central repository really maintained by the 'Maven Project' - Maven is the competitor of Ant :)
Third, not every John likes to save his jar to Maven central repository - so there are other online repositories (jBoss has its own, SpringSource as well). So every once in a while you have to point Ivy to a different repository.
A typical Java project can use hundreds of external jars. To expect to download these manually, tracking version numbers and all that can quickly become a harrowing experience.
Enter Ivy.
Ivy is a Ant extension - tell Ivy what jars you want, and it will do the downloading for you.
That sounds simple enough. But how does Ivy know where to download the jar from?
From a place called 'repositories'.
A repository is an online directory (location is http://repo1.maven.org/maven2) which contains ALL jars from all over the world.
Whenever Mr. John Whoever writes a new library john.jar, he uploads it to the repository. When you tell Ivy you need john.jar, Ivy downloads it from the repository.
If this sounds too easy, and you are not yet confused - let us learn some more things about repository.
After Ivy downloads the john.jar, it keeps a local copy of it in a local directory - by default in your home directory/.ivy/
This way, Ivy does not have to download from the online (central) repository each time - this makes builds go faster.
Second, http://repo1.maven.org/maven2 is a central repository really maintained by the 'Maven Project' - Maven is the competitor of Ant :)
Third, not every John likes to save his jar to Maven central repository - so there are other online repositories (jBoss has its own, SpringSource as well). So every once in a while you have to point Ivy to a different repository.
Wednesday, November 9, 2011
Ant - Run
To run Ant and build the HelloWorld.jar, give the following command:
> ant dist
This tells Ant to run the 'dist' target.
Because 'dist' depends on 'compile' which depends on 'init', Ant will automatically run all three in the order init, compile, dist.
You can then run HelloWorld from the target directory (see below):
Note - you can ignore the includeantruntime warning if you get it - its a known and harmless bug in Ant 1.8
> ant dist
This tells Ant to run the 'dist' target.
Because 'dist' depends on 'compile' which depends on 'init', Ant will automatically run all three in the order init, compile, dist.
You can then run HelloWorld from the target directory (see below):
C:\users\LavanniM\confucius>ant dist
Buildfile: C:\users\LavanniM\confucius\build.xml
init:
compile:
[javac] C:\users\LavanniM\confucius\build.xml:8: warning: 'includeantruntime' was not set, defaulting to build.sysclasspath=last; set to false for repeatable builds
dist:
[jar] Building jar: C:\users\LavanniM\confucius\target\HelloWorld.jar
BUILD SUCCESSFUL
Total time: 0 seconds
C:\users\LavanniM\confucius>cd target
C:\users\LavanniM\confucius\target>java -jar HelloWorld.jar
0 [main] DEBUG HelloWorld - Hello World!
C:\users\LavanniM\confucius\target>
Note - you can ignore the includeantruntime warning if you get it - its a known and harmless bug in Ant 1.8
Ant - build.xml
When you run Ant, it looks for a file called build.xml, then follows the instructions in it.
Create a new file called build.xml in /confucius - i.e., in the same directory where you have the HelloWorld.java
Type in (or copy-paste) the following in build.xml - these are the instructions for building HelloWorld.jar
Let us try and understand what we have written.
We created an Ant project called "HelloWorld".
We created three Ant 'targets' - init, compile and dist (short for distribution).
'dist' depends on 'compile' which depends on 'init' - this means that if you run 'dist', it will first run 'compile' which itself will first run 'init'.
In the 'init' we created two directories.
Note that if these directories already exist, Ant will not create them.
In the 'compile', we told Ant to compile everything in the current direcrtory and place the .class files in the 'classes' directory.
We also specified a classpath so it can find the log4j.jar
In the 'dist' we told Ant to jar all the .class files in /classes directory and place the jar in /target.
We also told it to add some things to the MANIFEST.MF
Create a new file called build.xml in /confucius - i.e., in the same directory where you have the HelloWorld.java
Type in (or copy-paste) the following in build.xml - these are the instructions for building HelloWorld.jar
<project name="HelloWorld">
<target name="init">
<mkdir dir="classes"/>
<mkdir dir="target"/>
</target>
<target name="compile" depends="init">
<javac srcdir="." destdir="classes">
<classpath>
<pathelement location="lib/log4j-1.2.16.jar"/>
</classpath>
</javac>
</target>
<target name="dist" depends="compile">
<jar jarfile="target/HelloWorld.jar" basedir="classes">
<manifest>
<attribute name="Main-Class" value="HelloWorld"/>
<attribute name="Class-Path" value="HelloWorld.jar ../lib/log4j-1.2.16.jar"/>
</manifest>
</jar>
</target>
</project>
Let us try and understand what we have written.
We created an Ant project called "HelloWorld".
We created three Ant 'targets' - init, compile and dist (short for distribution).
'dist' depends on 'compile' which depends on 'init' - this means that if you run 'dist', it will first run 'compile' which itself will first run 'init'.
In the 'init' we created two directories.
Note that if these directories already exist, Ant will not create them.
In the 'compile', we told Ant to compile everything in the current direcrtory and place the .class files in the 'classes' directory.
We also specified a classpath so it can find the log4j.jar
In the 'dist' we told Ant to jar all the .class files in /classes directory and place the jar in /target.
We also told it to add some things to the MANIFEST.MF
Ant - Configuration
Add the /bin directory of Ant to your PATH and you should be good to go.
For example, my bin is here: C:\Program Files\ant\apache-ant-1.8.1\bin
Next, create a JAVA_HOME environment variable and set it to the location of your JDK.
For example, my JAVA_HOME looks like this:
JAVA_HOME=C:\Program Files\Java\jdk1.6.0_21
This tells Ant where to find Java compiler.
For example, my bin is here: C:\Program Files\ant\apache-ant-1.8.1\bin
Next, create a JAVA_HOME environment variable and set it to the location of your JDK.
For example, my JAVA_HOME looks like this:
JAVA_HOME=C:\Program Files\Java\jdk1.6.0_21
This tells Ant where to find Java compiler.
Ant - Download
Download the latest Ant from the Apache Ant website. It is distributed as a zip - just unzip it to an appropriate folder (say C:\Program Files\Ant)
At this time, you just need Ant, not Ivy - which is the dependency management plugin. We will come to that later.
At this time, you just need Ant, not Ivy - which is the dependency management plugin. We will come to that later.
Quick note about Java versions
When you download JRE or JDK, you see versions like jdk1.5.0_22, jdk1.6.0_21, etc
When you read doc, you see things like Java5, Java6, etc
What gives?
The idea behind Java versioning was x.y.z - where x is the major release number. The idea was that any change in x implied a very significant change - something that will not be backward compatible, a paradigm change, a point of serious transition.
A change in x would cause so many problems to so many people that at that point they will have to rename Java to Mocha!
So in real life, x always is 1 - so people have resorted to referring to Java version by y - hence Java5, Java6, etc
When you read doc, you see things like Java5, Java6, etc
What gives?
The idea behind Java versioning was x.y.z - where x is the major release number. The idea was that any change in x implied a very significant change - something that will not be backward compatible, a paradigm change, a point of serious transition.
A change in x would cause so many problems to so many people that at that point they will have to rename Java to Mocha!
So in real life, x always is 1 - so people have resorted to referring to Java version by y - hence Java5, Java6, etc
Tuesday, November 8, 2011
Ant
So far we have been giving commands like javac, jar from command line to build our program. Clearly this won't scale for a typical project that has hundreds of files, external libraries, etc.
We need a build tool - Enter Ant.
With Ant, we can do a declarative build. In a file called build.xml, we can specify what needs to be built and how - then Ant can do that. It makes things easier to manage.
We need a build tool - Enter Ant.
With Ant, we can do a declarative build. In a file called build.xml, we can specify what needs to be built and how - then Ant can do that. It makes things easier to manage.
ClassLoader (and CL Hell)
When we ran HelloWorld.class, we saw that the JRE looked at the CLASSPATH environment variable to know where to load the classes.
Then, when we ran HelloWorld.jar, we saw that the JRE looked at the Class-Path property in MANIFEST.MF
So what is going on? Why does the scenery change one way and another?!
Truth is that this scenery can keep changing in every environment!!
The culprit is called ClassLoader.
A ClassLoader is a special class which implements the ClassLoader abstract class.
You can write any number of ClassLoaders - MyFooClassLoader, MyYellowClassLoader, MyCrazyClassLoader, ...
JRE will call the ClassLoader to get a class it needs. Needless to say different ClassLoaders look in different places!!!
This leads to hair-tearing situations where Java programs deployed in one place work perfectly, and then stop working in another. Worse things can happen. If you happen to have two different versions of a class on your computer, one ClassLoader might pick one and another ClassLoader will pick the other. It will happen silently and your program can behave differently!!
This is ClassLoader HELL - it is an unsolved problem.
Then, when we ran HelloWorld.jar, we saw that the JRE looked at the Class-Path property in MANIFEST.MF
So what is going on? Why does the scenery change one way and another?!
Truth is that this scenery can keep changing in every environment!!
The culprit is called ClassLoader.
A ClassLoader is a special class which implements the ClassLoader abstract class.
You can write any number of ClassLoaders - MyFooClassLoader, MyYellowClassLoader, MyCrazyClassLoader, ...
JRE will call the ClassLoader to get a class it needs. Needless to say different ClassLoaders look in different places!!!
This leads to hair-tearing situations where Java programs deployed in one place work perfectly, and then stop working in another. Worse things can happen. If you happen to have two different versions of a class on your computer, one ClassLoader might pick one and another ClassLoader will pick the other. It will happen silently and your program can behave differently!!
This is ClassLoader HELL - it is an unsolved problem.
Running a jar - how confusing this can be!
Let us try running the HelloWorld program as a jar - just to see how confusing this can ge.
Lets first pack our HelloWorld.class in a HelloWorld.jar
Go to the directory which contains the HelloWorld.class and give the following command
> jar cf HelloWorld.jar HelloWorld.class
The 'c' stands for create and 'f' stands for send the output to file, in this case, HelloWorld.jar
Next, try to run the jar using the following command - it will fail (see below):
> java -jar HelloWorld.jar
What happened?
When we initially ran the HelloWorld program from HelloWorld.class,the JRE 'knew' it had to look for the 'main' method in the HelloWorld.class
But when faced with a jar, which can potentially contain N classes, each of which might have a 'main', it cannot figure out which class's 'main' method to call.
Enter MANIFEST.MF
Inside each jar, is a directory called META-INF, and inside the META-INF is a file called MANIFEST.MF
This is a simple text file with name:value property pairs
Here is what it's contents look like:
JRE looks into this file for a property called Main-Class - if it does not find it, it throws the error we saw above.
So how do we put a MAIN-CLASS property in MANIFEST.MF? We can't simply edit this file because it will get recreated with the jar, and our changes will be lost!
Here is how to do this:
Create a new file called Manifest.txt in /confucius
Add a key-value property to it:
Very Important: The line has to end with a newline - so make sure you hit enter!
Now recreate the HelloWorld.jar with the following command:
> jar cfm HelloWorld.jar Manifest.txt HelloWorld.class
The 'm' stands for include name-value pairs in MANIFEST.MF
Now try to run the new HelloWorld.jar - it will fail again (See below)!
What now? Why can't it find the log4j class?
Turns out, when you run jar, JRE does not look at the CLASSPATH environment variable. It looks for a Class-Path property in the MANIFEST-MF.
So let us add another property, Class-Path, to our Manifest.txt - like this:
Again, do not forget to hit Enter (to add newline) after the last line.
Also, note that we use space to separate the two paths, and there are no semi-colons.
Try recreating the jar and running it - it will fail again (see below):
This time the reason is that we gave absolute path for the Class-Path property, but JRE expects relative paths.
Update the Manifest.txt as follows:
Now if you recreate the jar and run it, it will run (phew!)
Lets first pack our HelloWorld.class in a HelloWorld.jar
Go to the directory which contains the HelloWorld.class and give the following command
> jar cf HelloWorld.jar HelloWorld.class
The 'c' stands for create and 'f' stands for send the output to file, in this case, HelloWorld.jar
Next, try to run the jar using the following command - it will fail (see below):
> java -jar HelloWorld.jar
C:\users\LavanniM\confucius>jar cf HelloWorld.jar HelloWorld.class
C:\users\LavanniM\confucius>java -jar HelloWorld.jar
Failed to load Main-Class manifest attribute from
HelloWorld.jar
C:\users\LavanniM\confucius>
What happened?
When we initially ran the HelloWorld program from HelloWorld.class,the JRE 'knew' it had to look for the 'main' method in the HelloWorld.class
But when faced with a jar, which can potentially contain N classes, each of which might have a 'main', it cannot figure out which class's 'main' method to call.
Enter MANIFEST.MF
Inside each jar, is a directory called META-INF, and inside the META-INF is a file called MANIFEST.MF
This is a simple text file with name:value property pairs
Here is what it's contents look like:
Manifest-Version: 1.0
Created-By: 1.6.0_21 (Sun Microsystems Inc.)
JRE looks into this file for a property called Main-Class - if it does not find it, it throws the error we saw above.
So how do we put a MAIN-CLASS property in MANIFEST.MF? We can't simply edit this file because it will get recreated with the jar, and our changes will be lost!
Here is how to do this:
Create a new file called Manifest.txt in /confucius
Add a key-value property to it:
Main-Class: HelloWorld
Very Important: The line has to end with a newline - so make sure you hit enter!
Now recreate the HelloWorld.jar with the following command:
> jar cfm HelloWorld.jar Manifest.txt HelloWorld.class
The 'm' stands for include name-value pairs in MANIFEST.MF
Now try to run the new HelloWorld.jar - it will fail again (See below)!
C:\users\LavanniM\confucius>jar cfm HelloWorld.jar Manifest.txt HelloWorld.class
C:\users\LavanniM\confucius>java -jar HelloWorld.jar
Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/log4j/BasicConfigurator
at HelloWorld.main(HelloWorld.java:7)
Caused by: java.lang.ClassNotFoundException: org.apache.log4j.BasicConfigurator
at java.net.URLClassLoader$1.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
... 1 more
C:\users\LavanniM\confucius>
What now? Why can't it find the log4j class?
Turns out, when you run jar, JRE does not look at the CLASSPATH environment variable. It looks for a Class-Path property in the MANIFEST-MF.
So let us add another property, Class-Path, to our Manifest.txt - like this:
Main-Class: HelloWorld
Class-Path: C:\users\LavanniM\confucius\HelloWorld.jar C:\users\LavanniM\confucius\lib\log4j-1.2.16.jar
Again, do not forget to hit Enter (to add newline) after the last line.
Also, note that we use space to separate the two paths, and there are no semi-colons.
Try recreating the jar and running it - it will fail again (see below):
C:\users\LavanniM\confucius>jar cfm HelloWorld.jar Manifest.txt HelloWorld.class
C:\users\LavanniM\confucius>java -jar HelloWorld.jar
Exception in thread "main" java.lang.NoClassDefFoundError: HelloWorld
Caused by: java.lang.ClassNotFoundException: HelloWorld
at java.net.URLClassLoader$1.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
Could not find the main class: HelloWorld. Program will exit.
C:\users\LavanniM\confucius>
This time the reason is that we gave absolute path for the Class-Path property, but JRE expects relative paths.
Update the Manifest.txt as follows:
Main-Class: HelloWorld
Class-Path: HelloWorld.jar lib\log4j-1.2.16.jar
Now if you recreate the jar and run it, it will run (phew!)
C:\users\LavanniM\confucius>jar cfm HelloWorld.jar Manifest.txt HelloWorld.class
C:\users\LavanniM\confucius>java -jar HelloWorld.jar
0 [main] DEBUG HelloWorld - Hello World!
Subscribe to:
Posts (Atom)