Monday, 26 January 2015

POM is not a Derogatory Term

The Maven Project Object Model (POM) file is a powerful way to view a project.  It supports dependency management, build management and adds hierarchy to a project.  You can even specify how to auto-deploy a project.  There is far too much on the POM to cover here so we will just use what we need.

If you have not yet setup a simple project, please see The Wonderful World of JSF.  We will now add the dependencies we require.  The reason I chose to use Java JDK 7 or later is because Java 7 supports non-blocking IO.  The version of Jetty we are going to use requires this feature.  The reason we need to use a JDK rather than a JRE is that Maven and JSF require a full blown compiler, not just a runtime environment.

Dependencies of Dependencies

For a long time dependency management has been an important part of software engineering.  It allows us to track dependencies and dependencies of dependencies.  Without, an upgrade of an application would be nearly impossible.  For Java Maven is a dependency management tool (actually it is a lifecycle tool, but dependemcy management is part of this).  Maven keeps track of our required dependencies and fetches them if required.  It also keeps track of the versions we use.  It saves a great deal of time.

First we will look at the Jetty dependencies.  To view the central Maven repository for Jetty we can simply Google 'Maven Jetty' which should take you here.  Now we can pick and choose what we require.

Firstly, we will do some cleanup.  Open the pom.xml file in Eclipse.  Select the Dependencies tab.  Delete the dependencies (JUnit 3) by selecting them and clicking Remove.  Select the pom.xml tab.  I prefer to add dependencies in test mode as we can simply paste them in from the central repoistory.  It is only when I am doing some of the more complicated tasks that I will use the forms.  Format the document by pressing Crtl + Shift + F.  Between the <dependencies></dependencies> tags add the dependencies below.  I have included the entire POM for completeness.  Again format the document (Ctrl + Shift + F).


       
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>net.sf.pbu.ontrack</groupId>
    <artifactId>ontrack</artifactId>
    <version>0.0.1</version>
    <packaging>jar</packaging>

    <name>ontrack</name>
    <url>http://maven.apache.org</url>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.eclipse.jetty</groupId>
            <artifactId>jetty-server</artifactId>
            <version>9.3.0.M1</version>
        </dependency>
        <dependency>
            <groupId>org.eclipse.jetty</groupId>
            <artifactId>jetty-servlet</artifactId>
            <version>9.3.0.M1</version>
        </dependency>
        <dependency>
            <groupId>org.eclipse.jetty</groupId>
            <artifactId>jetty-webapp</artifactId>
            <version>9.3.0.M1</version>
        </dependency>
        <dependency>
            <groupId>org.eclipse.jetty</groupId>
            <artifactId>jetty-jsp</artifactId>
            <version>9.3.0.M1</version>
        </dependency>

        <dependency>
            <groupId>org.apache.myfaces.core</groupId>
            <artifactId>myfaces-api</artifactId>
            <version>2.2.7</version>
        </dependency>
        <dependency>
            <groupId>org.apache.myfaces.core</groupId>
            <artifactId>myfaces-impl</artifactId>
            <version>2.2.7</version>
        </dependency>

    </dependencies>
</project>

       

The dependencies above are the minimum set of dependencies.  To give a breakdown, jetty-server provides a lightweight HTTP server, jetty-servlet provides us with Java servlet support required by JSP and JSF, jetty-webapp provides us with a handler that will process our web.xml file, jetty-jsp provides us with the glassfish implementation of JSP and Expression Language (EL) that we will be making extensive use of, myfaces-api provides the JSF API glue and finally myfaces-impl provides the apache implementation of JSF.  With these dependencies we can compile an run our first JSF application.

There is still a considerable amount of clean-up left.  We will revise these dependencies later.  We will also introduce some features of the POM.


Creating a Page

Now we have the framework that will run our code.  Now let's create some stuff to run.

Delete the pre-created classes App.java and AppTest.java.  They are not required.  We will create a class net.sf.pbu.ontrack.main.Main.  To create a new class under the selected project press Ctrl + N and select Java > Class.  Change the Package to net.sf.pbu.ontrack.main and the Class Name to Main.  Click Finish.  What you have just witnessed is just part of the powerful Eclipse engine.  It allows many forms of refactoring as well as optimisation shortcuts.  We will see more of this soon.

Repeat the step above creating a new class called Welcome with a package net.sf.pbu.ontrack.ui.webapp.general.

The code we will put in the Main class will allow us to run our server.  The code below creates our Web Application and assigns it to our server.  The Context Path is the path URL path that will be used to access our application.  Keep in mind that multiple applications can run under one Web Server instance.  For example, if we wanted a 'foo' application and a 'bar' application we would specify '/foo/' and '/bar/' respectively.  This would allow us to access foo via www.example.com/foo/ and bar via www.example.com/bar/.  As our application will be the only one on the server, we shall use the root context '/'.  This will allow us to access our application with something like www.example.com.

The resource base is the location of our web application files relative to the working directory of our running web server.  This will change from development to production.  Finally we have our descriptor.  This is a special file that tell our web server how to setup the rest of our web server.

Please note that none of these configuration items should be hard coded, this is just an example.  Later we will use the spring framework to allow us to define these attributes in configuration.

The final four lines creates and runs our webserver.  We are using TCP port 8080.  This is an unprivileged port so we can avoid security issues.  When deploying this on a production environment you may want to use the standard HTTP port (TCP Port 80).

       
package net.sf.pbu.ontrack.main;

import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.webapp.WebAppContext;

public class Main {
    public static void main(String[] args) throws Exception {
        WebAppContext webAppContext = new WebAppContext();
        webAppContext.setContextPath("/");
        webAppContext.setResourceBase("src/main/webapp/");
        webAppContext.setDescriptor("src/main/webapp/WEB-INF/web.xml");
        
        Server server = new Server(8080);
        server.setHandler(webAppContext);
        server.start();
        server.join();
    }
}

Next, create the following file and directory structure.
  1. Expand the src folder.
  2. Right-click the main folder and select New > Folder
  3. Name the folder webapp
  4. Right-click the webapp folder and select New > Folder
  5. Name the folder WEB-INF
  6. Right-click the webapp folder and select New > File
  7. Name the file welcome.xhtml
  8. Right-click the WEB-INF folder and select New > File
  9. Name the file faces-config.xml
  10. Right-click the WEB-INF folder and select New > File
  11.  Name the file web.xml
After following the step above you should have:


web.xml

Let's focus on web.xml.  This tells our web application server how to run our application.  In the configuration below we set the refresh period to 10 seconds.  This allows us to modify dynamic resources without having to reload the server.  This should be turned off for production environments.  The listener class is the MyFaces entry point into our web application.  One servlet has been defined called faces.  This will process our JSF pages.  A servlet-mapping is used to specify how files are selected for processing by a servlet.  More than one mapping can be specified per servlet.  Finally, if we do not identify a resource or path when loading our page, the specified welcome file will be used.  This file can get large.  There are hundreds of possible MyFaces and JSF options to choose from.  I have only presented the basics here.

       
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://java.sun.com/xml/ns/javaee"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
    version="3.0">

    <context-param>
        <param-name>facelets.REFRESH_PERIOD</param-name>
        <param-value>10</param-value>
    </context-param>

    <listener>
        <listener-class>org.apache.myfaces.webapp.StartupServletContextListener</listener-class>
    </listener>

    <servlet>
        <servlet-name>faces</servlet-name>
        <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>faces</servlet-name>
        <url-pattern>*.jsf</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>faces</servlet-name>
        <url-pattern>welcome.xhtml</url-pattern>
    </servlet-mapping>

    <welcome-file-list>
        <welcome-file>welcome.xhtml</welcome-file>
    </welcome-file-list>
</web-app>


On a side note.  Anything stored in the WEB-INF folder is private.  The web server can access any files in this folder but the clients cannot.  It is great for storing templates and configuration.  Use it with some caution, no super secret stuff (like passwords) should be stored here.  Just in case.

faces-config.xml

This file specifies all the JSF specific configuration.  In this case we use it to map a Java POJO to a managed bean.  A managed bean is controlled by the JSF engine.  They are a powerful way to interface standard code or Plain Old Java Objects (POJOs) with the view logic of a web application.  Here we specify one managed bean.  You may also use annotations inside the POJO of the managed bean instead of the faces-config.xml file.  We will use annotations a little later.

Too annotate or not to annotate?  Annotations are a powerful construct but they may not be suitable for all applications.  I am more than happy to use them in tests and in other tools like hibernate.  Like many of the tools on offer, I have preferences (opinion only) as to where and when I use them.  This should not blow-up into a religious war.  Your opinion is allowed to be different to mine.  I would just say, if you have joined a project that has been using annotations or not, keep with the project standard!  Do not mix them and do not break the agreed project standard.

       
<?xml version='1.0' encoding='UTF-8'?>
<faces-config xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_2.xsd"
    version="2.2">

    <managed-bean>
        <managed-bean-name>welcome</managed-bean-name>
        <managed-bean-class>net.sf.pbu.ontrack.ui.webapp.general.Welcome</managed-bean-class>
        <managed-bean-scope>request</managed-bean-scope>
    </managed-bean>
</faces-config>



welcome.xhtml

This is the JSF file that will get processed.  By default the xhtml extension will get mapped to a jsf extension.  As of JSF 2.0, HTML tags and JSF tags may be used together.  What we are left with is a powerful mechanism of defining widgets coupled with the ability to customise the HTML style and tools however you like them.  In the files below we have specified and output label with some EL code.  This code will look-up the message property in our welcome managed bean and display the contents.  As you can see, it is a powerful mix of simple java and simple mark-up.

       
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:ui="http://java.sun.com/jsf/facelets"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:c="http://java.sun.com/jsp/jstl/core"
    xmlns:fn="http://java.sun.com/jsp/jstl/functions">

<h:body>
    <h:outputLabel value="#{welcome.message}" />
</h:body>
</html>


Run!

As you can see there is a fair amount of setup required.  This is one downside.  The upside is that once the setup is completed, you can use it over and over again.  To make things easier, there are several Maven archetypes dedicated towards JSF that come with sane presets.

  1. Select Main.java
  2. Select Run > Run As > Java Application (or Alt + Shift + X, then J)
  3. Open your favourite browser
  4. Browse to http://127.0.0.1:8080
  5. Happy Days (I Hope)
Just for clarity, 127.0.0.1 is the loopback address.  It is your machine.  You may also use http://localhost:8080 instead.  The 8080 is required as we are using a non-standard port.

What's next?

As always, the code can be found here.  In the mean time, I will clean up the project in the next post.  We need to sort out issues such as logging moving hard carded attributes to a configuration file.


1 comment: