Modern Java EE

Content:

  1. Introduction
  2. What is Java EE?
  3. Parts
  4. Versions
  5. More information
  6. Examples:
    1. Simple load and save data via web frontend
    2. Add remote access to EJB
    3. Add web services
    4. Add asynchroneous logic
    5. Add new presentaion layer technology
  7. Development environment

Introduction:

Java EE today is not like J2EE a decade ago. It has evolved a lot.

But for some reason unknown to me, then many peoples perception of Java EE has not changed in that decade.

The purpose of this article is to show how it has changed with the goal of changing peoples perception.

What is Java EE?

As you probably know then Java SE basically consists of 3 specifications:

Several implementations exist:

Java EE is a large series of specications defining the interface between application components and application servers.

Multiple application servers implement these specifcations. Including:

Multiple application servers implement a subset of these specifcations. Including:

An application writte to the specifications will then run on any compliant application server.

Parts:

Java EE consists of multiple parts each with their own specification.

Here is a list of some of the most important parts with a description of what functionality they provide and an attempt to map to .NET equivalent for those without any J2EE/Java EE knowledge but some ASP.NET knowledge.

Abbreviation Full name Description .NET equivalent
Java EE Java Enterprise Edition Overall specification (none)
Servlet (none) Basic code handling of web request Web handler (.ashx)
JSP Java Server Pages Web page with HTML and embedded code Web page (.aspx)
JSF Java Server Faces Web component framework ASP.NET Web Forms
EJB Enterprise Java Bean Business logic framework System.EnterpriseServices
SB Session Bean Request-response EJB System.EnterpriseServices
MDB Message Driven Bean Async processing EJB (none)
JCA Java Connector Architecture Custom in/out protocol adapters (none)
JMS Java Message Service Message Queue API System.Messaging (only support MSMQ)
JTA Java Transaction API Transaction Manager API System.Transactions
JPA Java Persistence API O/R-Mapper Entity Framework
JSTL Java Standard Tag Library Server side components System.Web.UI.WebControls
EL Expression Language Server side scripting language (none)
JAX-WS Java API for XML Web Services SOAP web services .asmx & WCF
JAX-RS Java API for RESTful Web Services RESTful web services WCF & Web API

Versions:

Java EE has an overall version number and then each part has its own version number.

Here is a quick overview including version numbers for a few of the implementing application servers:

J2EE 1.2 J2EE 1.3 J2EE 1.4 Java EE 5 Java EE 6 Java EE 7 Java EE 8 Jakarta EE 8 Jakarta EE 9/9.1 Jakarta EE 10
Year 1999 2001 2003 2006 2009 2013 2017 2019 2020/2021 2022
Java SE version 1.2 1.3 1.4 5 6 7 8 8 8/11 11/17
Servlet 2.2 2.3 2.4 2.5 3.0 3.1 4.0 4.0 5.0 6.0
EJB 1.1 2.0 2.1 3.0 3.1 3.2 3.2 3.2 4.0 4.0
JSP 1.1 1.2 2.0 2.1 2.2 2.3 2.3 2.3 3.0 3.1
JCA - - 1.0 1.5 1.6 1.7 1.7 1.7 1.7 1.7
JSF - - - 1.2 2.0 2.2 2.3 2.3 3.0 4.0
WAS (WebSphere AS) 4.0 5.x 6.x 7.x 8.x 9.x 19.x Liberty 19.x & 20.x Liberty 21.x Liberty 22.x
WL (WebLogic) 6.1 7.x & 8.x 9.x 10.x 12.0 12.2 14.1 14.1 - -
JBoss AS / WildFly - - (4.x) 5.x (6.x) & 7.x (EAP 6.x) 8.x 14.x (EAP 7.x) 14.x (EAP 7.x) 23.x 27.x

More information:

For new features in newer Java EE versions see:

For more advanced web app stuff see:

For information about JCA adapters see:

For more web services examples see Java examples in:

For more EJB examples see:

For more JMS examples see Java EE examples in:

Examples:

I will illustrate using a small example that I will gradually evolve test1->test2->test3->test4->test5.

I will only show new/changed code for each evolution.

The example is very simple and do not reflec the complexity in most real world applications, but it is only intended to illustrate certain techniques in Java EE.

The example is tested with JBoss AS 7.1, but the code should not have any JBoss dependencies.

Simple load and save data via web frontend:

We will start with a very simple setup with:

test.jsp:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" trimDirectiveWhitespaces="true" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Test page</title>
</head>
<body>
<h1>Test page</h1>
<h2>Data:</h2>
<table border="1">
<tr>
<th>F1</th>
<th>F2</th>
</tr>
<c:forEach var="o" items="${data}">
<tr>
<td>${o.f1}</td>
<td>${o.f2}</td>
</tr>
</c:forEach>
</table>
<h2>Add:</h2>
<form method="post">
F1: <input type="text" name="f1">
<br>
F2: <input type="text" name="f2">
<br>
<input type="submit" value="Save">
</form>
</body>
</html>

Explanation:

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

loads JSTL taglib including EL support.

<c:forEach var="o" items="${data}">
...
</c:forEach>

uses JSTL to iterate over an item labeled "data" from context(request/session/application) and puts every item in variable o.

<tr>
<td>${o.f1}</td>
<td>${o.f2}</td>
</tr>

uses EL to output properties f1 and f2 on variable o.

JSTL and EL was introduced in 2001 with J2EE 1.3 and since then the use of <% %> code blocks has been considered bad practice in JSP pages.

TestController.java:

package test1.web;

import java.io.IOException;

import javax.ejb.EJB;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import test1.ejb.TestManager;
import test1.ejb.T1;

@WebServlet(urlPatterns={"/testctl"})
public class TestController extends HttpServlet {
    @EJB
    private TestManager mgr;
    @Override
    public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setAttribute("data", mgr.getAll());
        req.getRequestDispatcher("test.jsp").forward(req,  resp);
    }
    @Override
    public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        T1 o = new T1();
        o.setF1(Integer.parseInt(req.getParameter("f1")));
        o.setF2(req.getParameter("f2"));
        mgr.save(o);
        resp.sendRedirect("testctl");
    }
}

Explanation:

@WebServlet(urlPatterns={"/testctl"})

defines URL for servlet to http://host:port/applikationname/specifiedpath (in my case running on local server and application name being test1 then http://localhost:8080/test1/testctl).

Servlet annotations was introduced in 2009 with Java EE 6. Before that the path had to be defined in web.xml file.

@EJB
private TestManager mgr;

uses DI (Dependency Injection) to get a reference to a session EJB.

EJB annotations was introduced in 2006 with Java EE 5. Before that it was necessary to get a reference to home object via JNDI lookup and use that to create a reference to the session EJB itself.

TestManager.java:

package test1.ejb;

import java.util.List;

public interface TestManager {
    public T1 getOne(int id);
    public List<T1> getAll();
    public void save(T1 o);
}

TestManagerBean.java:

package test1.ejb;

import java.util.List;

import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

@Stateless
public class TestManagerBean implements TestManager {
    @PersistenceContext
    private EntityManager em;
    @Override
    public T1 getOne(int id) {
        T1 o = em.find(T1.class, id);
        return o;
    }
    @Override
    public List<T1> getAll() {
        @SuppressWarnings("unchecked")
        List<T1> res = em.createQuery("SELECT t FROM T1 AS t").getResultList();
        return res;
    }
    @Override
    public void save(T1 o) {
        em.persist(o);
    }
}

Explanation:

@Stateless

makes the class a stateless session EJB.

@PersistenceContext
private EntityManager em;

uses DI to get a reference to the O/R-mapper.

EJB annotations was introduced in 2006 with Java EE 5. Before that it was necessary to define EJB's in ejb-jar.xml file.

Even though an EJB now looks like a completely normal POJO, then it is still an EJB and has the traditional characteristics of an EJB.

Calls to methods in an EJB are done in a transaction and if the method throws am EJBException then a rollback is done while a normal return result in a commit.

Default and the following annotaion on method:

@TransactionAttribute(TransactionAttributeType.REQUIRED)

will continue in existing transaction if such exist and otherwise start a new transaction.

@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)

will always start a new transaction even if one already exists.

@TransactionAttribute(TransactionAttributeType.NEVER)

will never participate in a transaction.

@TransactionAttribute(TransactionAttributeType.MANDATORY)

will continue in existing transaction if such exist and otherwise throw a TransactionRequiredException..

For more details about the transaction handling see text and code examples in Transactions - Atomicity.

And let us clarify:

SLSB (StateLess Session Bean)
if some client code make multiple calls to a SLSB then they may end up being processed by different instances of the EJB class
SFSB (StateFul Session Bean)
if some client code make multiple calls to a SFSB then they will always end up being processed by the same instance of the EJB class

T1.java:

package test1.ejb;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name="t1")
public class T1 {
    private int f1;
    private String f2;
    @Id
    @Column(name="f1")
    public int getF1() {
        return f1;
    }
    public void setF1(int f1) {
        this.f1 = f1;
    }
    @Column(name="f2")
    public String getF2() {
        return f2;
    }
    public void setF2(String f2) {
        this.f2 = f2;
    }
}

Explanation:

@Entity

marks that it is a persistable class.

@Table

is used to specify database table name.

@Column

is used to specify database column name.

@Id

is used to specify that database column is primary key.

JPA was introduved in 2006 with Java EE 5. Before that entity EJB's was used for persistence.

JPA and entity EJB's are both O/R-mappers, but they are very different.

Feature entity EJB's JPA
local access yes yes
remote access yes no
declarative security yes no
O/R-mapper yes (CMP) yes
custom data access yes (BMP) no
NoSQL support no yes
query support basic (EJBQL) advanced (JPQL)
Java EE support yes yes
Java SE support no yes

I would say that entity EJB's had a lot of advanced features that was rarely needed but was not very good at simple stuff everybody needs like complex queries. JPA is taking a much more practical approach and focusing on what everybody needs for persistence.

persistence.xml:

<persistence xmlns="http://java.sun.com/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
             version="2.0">
   <persistence-unit name="test" transaction-type="JTA">
      <provider>org.hibernate.ejb.HibernatePersistence</provider>
      <jta-data-source>java:jboss/datasources/TestDS</jta-data-source>
      <class>test1.ejb.T1</class>
      <exclude-unlisted-classes/>
      <properties>
        <--<property name="show_sql">true</property>-->
        <property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5Dialect"/>
      </properties>
   </persistence-unit>
</persistence>

Here is defined the name of the data source, what classes that can be persisted and some database specific properties.

Data source is defined in the application servers configuration and not in the applications configuration.

For a Java SE application persistence.xml could look like:

<persistence xmlns="http://java.sun.com/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
             version="2.0">
   <persistence-unit name="test">
      <provider>org.hibernate.ejb.HibernatePersistence</provider>
      <class>test.T1</class>
      <exclude-unlisted-classes/>
      <properties>
        <--<property name="show_sql">true</property>-->
        <property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver"/>
        <property name="hibernate.connection.url" value="jdbc:mysql://localhost/Test"/>
        <property name="hibernate.connection.username" value=""/>
        <property name="hibernate.connection.password" value=""/>
        <property name="hibernate.connection.pool_size" value="5"/>
      </properties>
   </persistence-unit>
</persistence>

Add remote access to EJB:

Session EJB's can still be accessed remotely via an efficient binary protocol.

This can be used from other servers and in desktop applications as long as they are written in Java.

It actually only requires one more line in the EJB.

TestManagerBean.java:

package test2.ejb;

import java.util.List;

import javax.ejb.Remote;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

@Remote(TestManager.class)
@Stateless
public class TestManagerBean implements TestManager {
    @PersistenceContext
    private EntityManager em;
    @Override
    public T1 getOne(int id) {
        T1 o = em.find(T1.class, id);
        return o;
    }
    @Override
    public List<T1> getAll() {
        @SuppressWarnings("unchecked")
        List<T1> res = em.createQuery("SELECT t FROM T1 AS t").getResultList();
        return res;
    }
    @Override
    public void save(T1 o) {
        em.persist(o);
    }
}

Explanation:

@Remote(TestManager.class)

is what does it.

Additionally the T1 class must be made serializable.

T1.java:

package test2.ejb;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name="t1")
public class T1 implements Serializable {
    private int f1;
    private String f2;
    @Id
    @Column(name="f1")
    public int getF1() {
        return f1;
    }
    public void setF1(int f1) {
        this.f1 = f1;
    }
    @Column(name="f2")
    public String getF2() {
        return f2;
    }
    public void setF2(String f2) {
        this.f2 = f2;
    }
}

In real world code one would probably use a DTO class and convert from the JPA data class to the DTO class. Just to keep a better separation between layers.

And a client program to call it.

TestEJB.java:

package test2.client;

import java.util.Hashtable;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

import test2.ejb.T1;
import test2.ejb.TestManager;

public class TestEJB {
    public static void main(String[] args) throws NamingException {
        Hashtable props = new Hashtable();
        props.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming");
        props.put(Context.INITIAL_CONTEXT_FACTORY,  "org.jboss.naming.remote.client.InitialContextFactory");
        props.put(Context.PROVIDER_URL, "remote://localhost:4447");
        props.put(Context.SECURITY_PRINCIPAL, "arne");
        props.put(Context.SECURITY_CREDENTIALS, "xxxx");
        props.put("jboss.naming.client.ejb.context", true);
        Context ctx = new InitialContext(props);
        TestManager ejb = (TestManager)ctx.lookup("ejb:/test2/TestManagerBean!test2.ejb.TestManager");
        System.out.println(ejb.getOne(2).getF2());
        for(T1 o : ejb.getAll()) {
            System.out.println(o.getF1() + " " + o.getF2());
        }
    }
}

Note that the configuration of the remote access is application server specific and obviously should be moved from Java code to a configuration file in real world code.

Add web services:

In todays SOA world interoperability with non-Java code is important and Java EE obviously supports web services.

We will now expose TestManager as both SOAP XML format RPC style web service and as JSON format RESTful style web service.

TestManagerSOAP.java:

package test3.web;

import java.util.List;

import javax.ejb.EJB;
import javax.jws.WebMethod;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;

import test3.ejb.T1;
import test3.ejb.TestManager;

@WebService(targetNamespace="http://soap.test3/")
@SOAPBinding(style=SOAPBinding.Style.RPC)
public class TestManagerSOAP {
    @EJB
    private TestManager mgr;
    @WebMethod
    public T1 getOne(int id) {
        return mgr.getOne(id);
    }
    @WebMethod
    public T1[] getAll() {
        return mgr.getAll().toArray(new T1[0]);
    }
    @WebMethod
    public void save(T1 o) {
        mgr.save(o);
    }
}

Explanation:

@WebService(targetNamespace="http://soap.test3/")

specifies that the class is a SOAP web service.

@SOAPBinding(style=SOAPBinding.Style.RPC)

specifies that the SOAP web service is RPC style - alternative is DOCUMENT style.

@WebMethod

specifies that the method should be exposed.

SOAP web services was introduced in 2003 with J2EE 1.4 (JAX-RPC) and got a major overhaul in 2006 with Java EE 5 (JAX-WS).

LoadRest.java:

package test3.web;

import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;

@ApplicationPath("/rest")
public class LoadRest extends Application {
}

Explanation:

@ApplicationPath("/rest")

specifies a part of path (see below).

TestManagerREST.java:

package test3.web;

import javax.annotation.ManagedBean;
import javax.ejb.EJB;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

import test3.ejb.T1;
import test3.ejb.TestManager;

@ManagedBean
@Path("/testapi")
public class TestManagerREST {
    @EJB
    private TestManager mgr;
    @GET
    @Produces({MediaType.APPLICATION_JSON})
    @Path("/{id}")
    public T1 getOne(@PathParam("id") int id) {
        return mgr.getOne(id);
    }
    @GET
    @Produces({MediaType.APPLICATION_JSON})
    public T1[] getAll() {
        return mgr.getAll().toArray(new T1[0]);
    }
    @POST
    @Consumes({MediaType.APPLICATION_JSON})
    public void save(T1 o) {
        mgr.save(o);
    }
}

Explanation:

@Path("/testapi")

specified that the class is a REST web service with URL http://host:port/applicationname/applicationpath/thispath.

@Produces({MediaType.APPLICATION_JSON})
@Consumes({MediaType.APPLICATION_JSON})

specified that the format i JSON - alternative format is XML.

@ManagedBean

is necessary fir @EJB to work (servlets, EJB's and SOAP web services automatically get injected dependencies, but POJO's does not - and a RESTful web service is technically a POJO - but @ManagedBean ensures that it automatically get injected dependencies).

RESTful web services was introduced in 2009 with Java EE 6 (JAX-RS).

The SOAP web service can be used from Java as:

package test3.client;

import test3.soap.T1;
import test3.soap.TestManagerSOAP;
import test3.soap.TestManagerSOAPService;

public class TestSOAP {
    public static void main(String[] args) {
        TestManagerSOAPService serv = new TestManagerSOAPService();
        TestManagerSOAP soap = serv.getPort(TestManagerSOAP.class);
        System.out.println(soap.getOne(2).getF2());
        for(T1 o : soap.getAll().getItem()) {
            System.out.println(o.getF1() + " " + o.getF2());
        }
    }
}

after stub is generated with:

wsimport http://localhost:8080/test3/TestManagerSOAP?wsdl

The SOAP web service can be used from C# as:

using System;

using Test3.Soap;

namespace Test3.Client
{
    public class TestSOAP
    {
        public static void Main(string[] args)
        {
            TestManagerSOAPService soap = new TestManagerSOAPService();
            Console.WriteLine(soap.getOne(2).f2);
            foreach(t1 o in soap.getAll())
            {
                Console.WriteLine(o.f1 + " " + o.f2);
            }
        }
    }
}

after stub is generated with:

wsdl /n:Test3.Soap http://localhost:8080/test3/TestManagerSOAP?wsdl

The SOAP web service can be used from Python as:

from suds.client import Client
soap = Client('http://localhost:8080/test3/TestManagerSOAP?wsdl')
print soap.service.getOne(2).f2
for o in soap.service.getAll().item:
    print '%d %s' % (o.f1,o.f2)

The RESTFul web service can be used from Java as:

package test3.client;

import java.io.IOException;

import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;

public class TestREST {
    public static void main(String[] args) throws ClientProtocolException, IOException {
        HttpClient hc = new DefaultHttpClient();
        hc.execute(new HttpGet("http://localhost:8080/test3/rest/testapi/2")).getEntity().writeTo(System.out);
        System.out.println();
        hc.execute(new HttpGet("http://localhost:8080/test3/rest/testapi")).getEntity().writeTo(System.out);
    }
}

Add asynchroneous logic:

We will now make it possible to do save asynchroneously - one submit and get immediatetly acknowledge in browser, but the actual save to database happens later (a desirable scenario in cases where the processing takes long time).

This is done by having TestManagerBean session EJB submit to a message queue and let a MDB (Message Driven Bean) read from message queue and process.

TestManager.java:

package test4.ejb;

import java.util.List;

public interface TestManager {
    public T1 getOne(int id);
    public List<T1> getAll();
    public void save(T1 o);
    public void saveDelayed(T1 o);
}

It has a new method saveDelayed.

TestManagerBean.java:

package test4.ejb;

import java.util.List;

import javax.annotation.Resource;
import javax.ejb.Remote;
import javax.ejb.Stateless;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.JMSException;
import javax.jms.MessageProducer;
import javax.jms.Queue;
import javax.jms.Session;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

@Remote(TestManager.class)
@Stateless
public class TestManagerBean implements TestManager {
    @PersistenceContext
    private EntityManager em;
    @Resource(mappedName="java:/JmsXA")
    private ConnectionFactory cf;
    @Resource(mappedName="java:jboss/exported/jms/queue/testmanager")
    private Queue q;
    @Override
    public T1 getOne(int id) {
        T1 o = em.find(T1.class, id);
        return o;
    }
    @Override
    public List<T1> getAll() {
        @SuppressWarnings("unchecked")
        List<T1> res = em.createQuery("SELECT t FROM T1 AS t").getResultList();
        return res;
    }
    @Override
    public void save(T1 o) {
        em.persist(o);
    }
    @Override
    public void saveDelayed(T1 o) {
        try {
            Connection c = cf.createConnection();
            Session ses = c.createSession(false,  Session.AUTO_ACKNOWLEDGE);
            MessageProducer sender = ses.createProducer(q);
            sender.send(ses.createObjectMessage(o));
            c.close();
        } catch (JMSException e) {
            e.printStackTrace();
        }
    }
}

Explanation:

@Resource(mappedName="java:/JmsXA")
private ConnectionFactory cf;
@Resource(mappedName="java:jboss/exported/jms/queue/testmanager")
private Queue q;

uses DI to get references to message queue connection factory and messsage queue itself. Both java:/JmsXA and java:jboss/exported/jms/queue/testmanager must be defined the the application servers configuration.

Connection c = cf.createConnection();
Session ses = c.createSession(false,  Session.AUTO_ACKNOWLEDGE);
MessageProducer sender = ses.createProducer(q);
sender.send(ses.createObjectMessage(o));
c.close();

sends object to the message queue.

TestManagerService.java:

package test4.ejb;

import javax.ejb.ActivationConfigProperty;
import javax.ejb.EJB;
import javax.ejb.MessageDriven;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.ObjectMessage;

@MessageDriven(name="TestManagerService",
               activationConfig={@ActivationConfigProperty(propertyName="destinationType", propertyValue="javax.jms.Queue"),
                                 @ActivationConfigProperty(propertyName="destination", propertyValue="java:jboss/exported/jms/queue/testmanager")})
public class TestManagerService implements MessageListener {
    @EJB
    private TestManager mgr;
    @Override
    public void onMessage(Message msg) {
        try {
            T1 o = (T1)((ObjectMessage)msg).getObject();
            Thread.sleep(10000); // simulate lot of work
            mgr.save(o);
        } catch (JMSException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

is a MDB and onMessage is the method that is being called to process messages from the message queue.

Explanation:

@MessageDriven(name="TestManagerService",
               activationConfig={@ActivationConfigProperty(propertyName="destinationType", propertyValue="javax.jms.Queue"),
                                 @ActivationConfigProperty(propertyName="destination", propertyValue="java:jboss/exported/jms/queue/testmanager")})

specfies which message queues messages will be processed.

Note that MDB EJBs are transactional just like session EJB's. If they throw an EJBException a rollback is performed and both database operation *and* read from message is roilled back (assuming message queue is configured to be transactional).

Message queues can also be persisted and clustered for high availability.

So a message queue and a MDB is way more robust solution than just delegating a task to another thread or thread pool.

MDB EJB's was introduced in 2001 with J2EE 1.3.

TestController.java:

package test4.web;

import java.io.IOException;

import javax.ejb.EJB;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import test4.ejb.TestManager;
import test4.ejb.T1;

@WebServlet(urlPatterns={"/testctl"})
public class TestController extends HttpServlet {
    @EJB
    private TestManager mgr;
    @Override
    public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setAttribute("data", mgr.getAll());
        req.getRequestDispatcher("test.jsp").forward(req,  resp);
    }
    @Override
    public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        T1 o = new T1();
        o.setF1(Integer.parseInt(req.getParameter("f1")));
        o.setF2(req.getParameter("f2"));
        mgr.saveDelayed(o);
        resp.sendRedirect("testctl");
    }
}

calls the new method.

Now we will show how to process a HTTP request asyncrhoneously with am async servlet and AJAX longpoll in the JSP page.

test.jsp:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" trimDirectiveWhitespaces="true" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Test page</title>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js">
</script>
<script>
function gettime() {
    $.getJSON('gettime', function(data) {
                             $("#time").html(data.txt);
                             gettime();
                         });
}
gettime();
</script>
</head>
<body>
<h1>Test page</h1>
<h2>Data:</h2>
<table border="1">
<tr>
<th>F1</th>
<th>F2</th>
</tr>
<c:forEach var="o" items="${data}">
<tr>
<td>${o.f1}</td>
<td>${o.f2}</td>
</tr>
</c:forEach>
</table>
<h2>Add:</h2>
<form method="post">
F1: <input type="text" name="f1">
<br>
F2: <input type="text" name="f2">
<br>
<input type="submit" value="Save">
</form>
<h2>Time:</h2>
<div id="time"></div>
</body>
</html>

There is just added a little server time div that is updated via AJAX call (the example uses jQuery for the call, but that is not important).

GetTime.java:

package test4.web;

import java.io.IOException;

import javax.ejb.EJB;
import javax.servlet.AsyncContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import test4.ejb.ClientHandler;

@WebServlet(urlPatterns={"/gettime"}, asyncSupported = true)
public class GetTime extends HttpServlet {
    @EJB
    private ClientHandler ch;
    @Override
    public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        AsyncContext ac = req.startAsync();
        ch.register(ac);
    }
}

Explanation:

@WebServlet(urlPatterns={"/gettime"}, asyncSupported = true)

specifies path and that it is an async servlet.

AsyncContext ac = req.startAsync();
ch.register(ac);

registers context in a client handler. And the servlet returns and servlet thread is ready to process a new request, but the socket connection to browser is kept open and can be found via context.

Async servlets was introduced in 2009 with Java EE 6.

ClientHandler.java:

package test4.ejb;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.ejb.Lock;
import javax.ejb.LockType;
import javax.ejb.Schedule;
import javax.ejb.Singleton;
import javax.ejb.Startup;
import javax.servlet.AsyncContext;

@Singleton
@Startup
public class ClientHandler {
    private List<AsyncContext> clients = new ArrayList<AsyncContext>();
    @Lock(LockType.WRITE)
    public void register(AsyncContext ac) {
        clients.add(ac);
    }
    @Schedule(second="*/1", minute="*",hour="*", persistent=false)
    @Lock(LockType.WRITE)
    private void process() {
        for(AsyncContext ac : clients) {
            try {
                ac.getResponse().getOutputStream().println("{ \"txt\":\"" + (new Date()) + "\"}");
                ac.complete();
            } catch (IOException e) {
                e.printStackTrace();
            };
        }
        clients.clear();
    }
}

Explanation:

@Singleton

specifies that it is a session EJB that only exist in one instance (per node).

@Startup

specifies that it should be initialized at server startup.

@Schedule(second="*/1", minute="*",hour="*", persistent=false)

specifies that the method should be excuted once every second

@Lock(LockType.WRITE)

synchronizes access.

ac.getResponse().getOutputStream().println("{ \"txt\":\"" + (new Date()) + "\"}");

writes response.

ac.complete();

completes response and response is sent to browser.

@Singleton, @Startup og @Schedule was introduced in 2009 with Java EE 6.

(a feature similar to @Scheduled called TimedObject EJB was introduced in 2003 with J2EE 1.4)

Add new presentaion layer technology:

The traditional:

model was in 2006 with Java EE 5 supplemented with a new component model JSF (JavaServer Faces).

JSF consist of:

We will now add web UI with JSF and facelets as view technology.

layout.xhtml:

<!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:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:ui="http://java.sun.com/jsf/facelets">
<h:head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js">
</script>
<title><ui:insert name="title">Default title</ui:insert></title>
</h:head>
<h:body>
<h1><ui:insert name="title">Default title</ui:insert></h1>
<div>
<ui:insert name="content"/>
</div>
<div>
<ui:include src="footer.xhtml"/>
</div>
</h:body>
</html>

This is a template page (master page in ASP.NET terminology) that has placeholders for title and content plus an include of a footer.

test.xhtml:

<!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:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:ui="http://java.sun.com/jsf/facelets">
<h:body>
<ui:composition template="layout.xhtml">
<ui:define name="title">Test page</ui:define>
<ui:define name="content">
<h2>Data:</h2>
<h:dataTable value="#{test.data}" var="o" border="1">
<h:column>
<f:facet name="header">F1</f:facet>#{o.f1}</h:column>
<h:column><f:facet name="header">F2</f:facet>#{o.f2}</h:column>
</h:dataTable>
<h2>Add:</h2>
<h:form id="entryform">
F1: <h:inputText id="f1" label="F1" value="#{test.f1}"><f:validateLongRange minimum="1" maximum="100000"/></h:inputText><h:message for="f1"/>
<br/>
F2: <h:inputText id="f2" label="F2" value="#{test.f2}"><f:validateLength minimum="0" maximum="50"/></h:inputText><h:message for="f2"/>
<br/>
<h:commandButton value="Save" action="#{test.save}"/>
</h:form>
</ui:define>
</ui:composition>
</h:body>
</html>

This is the page itself.

Explanation:

<ui:composition template="layout.xhtml">
<ui:define name="title">Test page</ui:define>
<ui:define name="content">
...
</ui:define>
</ui:composition>

does that layout.xhtml is used as template with the specified title and the specified content. Everything outside is ignored.

<h:dataTable value="#{test.data}" var="o" border="1">
<h:column>
<f:facet name="header">F1</f:facet>#{o.f1}</h:column>
<h:column><f:facet name="header">F2</f:facet>#{o.f2}</h:column>
</h:dataTable>

fetches data property from backing bean and display it as a table.

<h:form id="entryform">
F1: <h:inputText id="f1" label="F1" value="#{test.f1}"><f:validateLongRange minimum="1" maximum="100000"/></h:inputText><h:message for="f1"/>
<br/>
F2: <h:inputText id="f2" label="F2" value="#{test.f2}"><f:validateLength minimum="0" maximum="50"/></h:inputText><h:message for="f2"/>
<br/>
<h:commandButton value="Save" action="#{test.save}"/>
</h:form>

at submit the two properties are set in backing bean and after that the save method in the backing bean is called.

validate* and message tags are used for simple data validation in presentation layer.

Test.java:

package test5.web;

import java.util.List;

import javax.ejb.EJB;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;

import test5.ejb.T1;
import test5.ejb.TestManager;

@ManagedBean
@RequestScoped
public class Test {
    @EJB
    private TestManager mgr;
    private Integer f1;
    private String f2;
    public Integer getF1() {
        return f1;
    }
    public void setF1(Integer f1) {
        this.f1 = f1;
    }
    public String getF2() {
        return f2;
    }
    public void setF2(String f2) {
        this.f2 = f2;
    }
    public List<T1> getData() {
        return mgr.getAll();
    }
    public void save() {
        T1 o = new T1();
        o.setF1(f1);
        o.setF2(f2);
        mgr.save(o);
    }
}

is the backing bean.

Explanation:

@ManagedBean
@RequestScoped

specfifies that it is a backing bean with request scope. Alternative scopes are application, session, view and custom.

footer.xhtml:

<!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:h="http://java.sun.com/jsf/html"
      xmlns:ui="http://java.sun.com/jsf/facelets">
<body>
<ui:composition>
<h:outputScript library="js" name="gettime.js" target="head"/>
<h2>Time:</h2>
<div id="time"></div>
</ui:composition>
</body>
</html>

Explanation:

<ui:composition>
...
</ui:composition>

is the relevant (the rest is ignored).

<h:outputScript library="js" name="gettime.js" target="head"/>

generates a script tag up in head element (that otherwise is controlled by layout.xhtml).

gettime.js:

function gettime() {
    $.getJSON('gettime', function(data) {
                             $("#time").html(data.txt);
                             gettime();
                         });
}
gettime();

URL when deployed on local server is http://localhost:8080/test5/test.jsf.

JSF is a huge and very complex technology. Almost everything can be customized including validators and comnponents.

Standard JSF has a limited number of components, but there is a large number of third party JSF components.

Examples:

I think that JSF is smart and that facelets is an excellent view technology, but I do not like the backing bean concept that mixes action and data.

If one has dedicated designer to design HTML and CSS, then there is a cute little trick in facelets, that will allow designers to work with facelets using just editor and browser without any server and data.

Let us first rewrite test.xhtml a little.

test1.xhtml:

<!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:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:ui="http://java.sun.com/jsf/facelets">
<h:body>
<ui:composition template="layout.xhtml">
<ui:define name="title">Test page</ui:define>
<ui:define name="content">
<h2>Data:</h2>
<table border="1">
<tr>
<th>F1</th>
<th>F2</th>
</tr>
<ui:repeat value="#{test.data}" var="o">
<tr>
<td><h:outputText value="#{o.f1}"/></td>
<td><h:outputText value="#{o.f2}"/></td>
</tr>
</ui:repeat>
</table>
<h2>Add:</h2>
<h:form id="entryform">
F1: <h:inputText id="f1" label="F1" value="#{test.f1}"/>
<br/>
F2: <h:inputText id="f2" label="F2" value="#{test.f2}"/>
<br/>
<h:commandButton value="Save" action="#{test.save}"/>
</h:form>
</ui:define>
</ui:composition>
</h:body>
</html>

Not a big difference. The dataTable is just replaced with a repeat and some standard XHTML.

But now we can replace all JSF tags with XHTML tags with a jsfc attribute.

test2.xhtml:

<!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:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:ui="http://java.sun.com/jsf/facelets">
<head>
</head>
<body>
<div jsfc="ui:composition" template="layout.xhtml">
<span jsfc="ui:define" name="title">Test page</span>
<div jsfc="ui:define" name="content">
<h2>Data:</h2>
<table border="1">
<tr>
<th>F1</th>
<th>F2</th>
</tr>
<tr jsfc="ui:repeat" value="#{test.data}" var="o">
<td><span jsfc="h:outputText" value="#{o.f1}"/></td>
<td><span jsfc="h:outputText" value="#{o.f2}"/></td>
</tr>
</table>
<h2>Add:</h2>
<form jsfc="h:form" id="entryform">
F1: <input type="text" jsfc="h:inputText" id="f1" label="F1" value="#{test.f1}"/>
<br/>
F2: <input type="text" jsfc="h:inputText" id="f2" label="F2" value="#{test.f2}"/>
<br/>
<input type="submit" jsfc="h:commandButton" value="Save" action="#{test.save}"/>
</form>
</div>
</div>
</body>
</html>

test2.xhtml generates the exact same output as test1.xhtml og it is almost pure XHTML (XHTML with some extra attributes that the browser will ignore).

The point becomes important when demo data is added:

test3.xhtml:

<!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:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:ui="http://java.sun.com/jsf/facelets">
<head>
</head>
<body>
<div jsfc="ui:composition" template="layout.xhtml">
<h1 jsfc="ui:define" name="title">Test page</h1>
<div jsfc="ui:define" name="content">
<h2>Data:</h2>
<table border="1">
<tr>
<th>F1</th>
<th>F2</th>
</tr>
<tr jsfc="ui:repeat" value="#{test.data}" var="o">
<td><span jsfc="h:outputText" value="#{o.f1}"/></td>
<td><span jsfc="h:outputText" value="#{o.f2}"/></td>
</tr>
<tr jsfc="ui:remove">
<td>1</td>
<td>X</td>
</tr>
<tr jsfc="ui:remove">
<td>2</td>
<td>YY</td>
</tr>
<tr jsfc="ui:remove">
<td>3</td>
<td>ZZZ</td>
</tr>
</table>
<h2>Add:</h2>
<form jsfc="h:form" id="entryform">
F1: <input type="text" jsfc="h:inputText" id="f1" label="F1" value="#{test.f1}"/>
<br/>
F2: <input type="text" jsfc="h:inputText" id="f2" label="F2" value="#{test.f2}"/>
<br/>
<input type="submit" jsfc="h:commandButton" value="Save" action="#{test.save}"/>
</form>
</div>
</div>
</body>
</html>

These demo data are completely ignored when run on the server, but when the browser loads file from local file, then the demo data is shown and the designers can see how the page will look like.

Note that:

For more info on JSF see this article.

Development environment:

There are many tools available and some of the most popular are free.

IDE:

Build tool:

Server:

I use Eclipse + ant + JBoss.

build.xml for test5:

<project name="test5" default="deploy">
    <path id="javaee.classpath">
        <fileset dir="/SUN/Java/glassfish3/glassfish/lib">
            <include name="**/*.jar" />
        </fileset>
    </path>
    <path id="client.classpath">
        <fileset dir="/Apache/httpcomponents-client-4.1.2/lib">
            <include name="**/*.jar" />
        </fileset>
        <fileset dir="/JBoss/jboss-as-7.1.1.Final/bin/client">
            <include name="**/*.jar" />
        </fileset>
        <path refid="javaee.classpath"/>
    </path>
    <target name="compile">
        <javac classpathref="javaee.classpath" srcdir="src" destdir="bin"/>
        <copy file="persistence.xml" todir="bin/META-INF"/>
    </target>
    <target name="build" depends="compile">
        <war warfile="test5.war" webxml="descrip/web.xml">
            <classes dir="bin"/>
            <fileset dir="jsp"/>
            <fileset dir="xhtml"/>
        </war>
    </target>
    <target name="deploy" depends="build">
        <copy file="test5.war" todir="/JBoss/jboss-as-7.1.1.Final/standalone/deployments"/>
    </target>
    <target name="gen">
        <exec executable="wsimport">
            <arg line="-d srcgen -keep -Xnocompile http://localhost:8080/test5/TestManagerSOAP?wsdl"/>
        </exec>
        <javac srcdir="srcgen" destdir="bingen"/>
    </target>
    <target name="test" depends="deploy,gen">
        <javac classpath="bin;bingen" classpathref="client.classpath" srcdir="srccli" destdir="bincli"/>
        <java classpath="bin;bingen;bincli" classpathref="client.classpath" classname="test5.client.TestWeb"/>
        <java classpath="bin;bingen;bincli" classpathref="client.classpath" classname="test5.client.TestEJB"/>
        <java classpath="bin;bingen;bincli" classpathref="client.classpath" classname="test5.client.TestSOAP"/>
        <java classpath="bin;bingen;bincli" classpathref="client.classpath" classname="test5.client.TestREST"/>
    </target>
    <target name="prep">
        <mkdir dir="/JBoss/jboss-as-7.1.1.Final/modules/com/mysql/main"/>
        <copy todir="/JBoss/jboss-as-7.1.1.Final/modules/com/mysql/main">
            <fileset dir="mysql2jboss"/>
        </copy>
        <copy file="/JBoss/jboss-as-7.1.1.Final/standalone/configuration/standalone-full.xml"
              tofile="/JBoss/jboss-as-7.1.1.Final/standalone/configuration/standalone.xml"
              overwrite="yes"/>
        <replace file="/JBoss/jboss-as-7.1.1.Final/standalone/configuration/standalone.xml">
            <replacetoken><![CDATA[ </datasource>]]></replacetoken>
            <replacevalue><![CDATA[ </datasource>
<datasource jndi-name="java:jboss/datasources/TestDS" pool-name="TestDS">
<connection-url>jdbc:mysql://localhost/Test</connection-url>
<driver>mysql</driver>
<pool>
<min-pool-size>5</min-pool-size>
<max-pool-size>10</max-pool-size>
<prefill>true</prefill>
</pool>
<security>
<user-name>root</user-name>
</security>
</datasource>]]></replacevalue>
        </replace>
        <replace file="/JBoss/jboss-as-7.1.1.Final/standalone/configuration/standalone.xml">
            <replacetoken><![CDATA[ </driver>]]></replacetoken>
            <replacevalue><![CDATA[ </driver>
<driver name="mysql" module="com.mysql">
<xa-datasource-class>com.mysql.jdbc.jdbc2.optional.MysqlXADataSource</xa-datasource-class>
</driver>]]></replacevalue>
        </replace>
        <replace file="/JBoss/jboss-as-7.1.1.Final/standalone/configuration/standalone.xml">
            <replacetoken><![CDATA[ </jms-queue>]]></replacetoken>
            <replacevalue><![CDATA[ </jms-queue>
<jms-queue name="testManagerQueue">
<entry name="queue/testmanager"/>
<entry name="java:jboss/exported/jms/queue/testmanager"/>
</jms-queue>]]></replacevalue>
        </replace>
    </target>
</project>

test5.war contains:

     0 Sun Aug 18 10:08:12 EDT 2013 META-INF/
   103 Sun Aug 18 10:08:10 EDT 2013 META-INF/MANIFEST.MF
     0 Sun Aug 18 10:08:12 EDT 2013 WEB-INF/
   610 Sat Aug 17 22:32:56 EDT 2013 WEB-INF/web.xml
     0 Sun Aug 18 10:08:12 EDT 2013 WEB-INF/classes/
     0 Sat Aug 17 22:06:12 EDT 2013 WEB-INF/classes/META-INF/
     0 Sat Aug 17 22:06:12 EDT 2013 WEB-INF/classes/test5/
     0 Sat Aug 17 22:06:12 EDT 2013 WEB-INF/classes/test5/ejb/
     0 Sat Aug 17 22:57:02 EDT 2013 WEB-INF/classes/test5/web/
   787 Sat Aug 17 22:06:12 EDT 2013 WEB-INF/classes/META-INF/persistence.xml
  1692 Sat Aug 17 22:06:12 EDT 2013 WEB-INF/classes/test5/ejb/ClientHandler.class
   703 Sat Aug 17 22:06:12 EDT 2013 WEB-INF/classes/test5/ejb/T1.class
   262 Sat Aug 17 22:06:12 EDT 2013 WEB-INF/classes/test5/ejb/TestManager.class
  1956 Sat Aug 17 22:06:12 EDT 2013 WEB-INF/classes/test5/ejb/TestManagerBean.class
  1052 Sat Aug 17 22:06:12 EDT 2013 WEB-INF/classes/test5/ejb/TestManagerService.class
   771 Sat Aug 17 22:06:12 EDT 2013 WEB-INF/classes/test5/web/GetTime.class
   238 Sat Aug 17 22:06:12 EDT 2013 WEB-INF/classes/test5/web/LoadRest.class
  1035 Sun Aug 18 10:05:02 EDT 2013 WEB-INF/classes/test5/web/Test.class
  1463 Sat Aug 17 22:06:12 EDT 2013 WEB-INF/classes/test5/web/TestController.class
  1034 Sat Aug 17 22:06:12 EDT 2013 WEB-INF/classes/test5/web/TestManagerREST.class
   998 Sat Aug 17 22:06:12 EDT 2013 WEB-INF/classes/test5/web/TestManagerSOAP.class
  1164 Sun Aug 11 23:01:12 EDT 2013 test.jsp
     0 Sat Aug 17 22:51:18 EDT 2013 resources/
     0 Sat Aug 17 22:51:18 EDT 2013 resources/js/
   431 Sat Aug 17 22:51:10 EDT 2013 footer.xhtml
   673 Sat Aug 17 22:15:54 EDT 2013 layout.xhtml
   208 Sat Aug 17 22:43:04 EDT 2013 resources/js/gettime.js
  1109 Sun Aug 18 10:08:02 EDT 2013 test.xhtml

Directory structure is:

 Volume in drive C is ARNEPC4
 Volume Serial Number is F878-3B24

 Directory of C:\Work\modernjee\test5

08/17/2013  10:55 PM    <DIR>          .
08/17/2013  10:55 PM    <DIR>          ..
08/17/2013  10:06 PM    <DIR>          bin
08/17/2013  10:07 PM    <DIR>          bincli
08/17/2013  10:07 PM    <DIR>          bingen
08/17/2013  10:55 PM             3,850 build.xml
08/17/2013  10:32 PM    <DIR>          descrip
08/17/2013  10:02 PM    <DIR>          jsp
08/17/2013  10:02 PM    <DIR>          mysql2jboss
08/17/2013  10:05 PM               787 persistence.xml
08/17/2013  10:03 PM    <DIR>          src
08/17/2013  10:04 PM    <DIR>          srccli
08/17/2013  10:07 PM    <DIR>          srcgen
08/18/2013  10:08 AM            12,390 test5.war
08/18/2013  10:08 AM    <DIR>          xhtml
               3 File(s)         17,027 bytes

 Directory of C:\Work\modernjee\test5\bin

08/17/2013  10:06 PM    <DIR>          .
08/17/2013  10:06 PM    <DIR>          ..
08/17/2013  10:06 PM    <DIR>          META-INF
08/17/2013  10:06 PM    <DIR>          test5
               0 File(s)              0 bytes

 Directory of C:\Work\modernjee\test5\bin\META-INF

08/17/2013  10:06 PM    <DIR>          .
08/17/2013  10:06 PM    <DIR>          ..
08/17/2013  10:06 PM               787 persistence.xml
               1 File(s)            787 bytes

 Directory of C:\Work\modernjee\test5\bin\test5

08/17/2013  10:06 PM    <DIR>          .
08/17/2013  10:06 PM    <DIR>          ..
08/17/2013  10:06 PM    <DIR>          ejb
08/17/2013  10:57 PM    <DIR>          web
               0 File(s)              0 bytes

 Directory of C:\Work\modernjee\test5\bin\test5\ejb

08/17/2013  10:06 PM    <DIR>          .
08/17/2013  10:06 PM    <DIR>          ..
08/17/2013  10:06 PM             1,692 ClientHandler.class
08/17/2013  10:06 PM               703 T1.class
08/17/2013  10:06 PM               262 TestManager.class
08/17/2013  10:06 PM             1,956 TestManagerBean.class
08/17/2013  10:06 PM             1,052 TestManagerService.class
               5 File(s)          5,665 bytes

 Directory of C:\Work\modernjee\test5\bin\test5\web

08/17/2013  10:57 PM    <DIR>          .
08/17/2013  10:57 PM    <DIR>          ..
08/17/2013  10:06 PM               771 GetTime.class
08/17/2013  10:06 PM               238 LoadRest.class
08/18/2013  10:05 AM             1,035 Test.class
08/17/2013  10:06 PM             1,463 TestController.class
08/17/2013  10:06 PM             1,034 TestManagerREST.class
08/17/2013  10:06 PM               998 TestManagerSOAP.class
               6 File(s)          5,539 bytes

 Directory of C:\Work\modernjee\test5\bincli

08/17/2013  10:07 PM    <DIR>          .
08/17/2013  10:07 PM    <DIR>          ..
08/17/2013  10:07 PM    <DIR>          test5
               0 File(s)              0 bytes

 Directory of C:\Work\modernjee\test5\bincli\test5

08/17/2013  10:07 PM    <DIR>          .
08/17/2013  10:07 PM    <DIR>          ..
08/17/2013  10:07 PM    <DIR>          client
               0 File(s)              0 bytes

 Directory of C:\Work\modernjee\test5\bincli\test5\client

08/17/2013  10:07 PM    <DIR>          .
08/17/2013  10:07 PM    <DIR>          ..
08/17/2013  10:07 PM             1,895 TestEJB.class
08/17/2013  10:07 PM             1,012 TestREST.class
08/17/2013  10:07 PM             1,179 TestSOAP.class
08/17/2013  10:07 PM               879 TestWeb.class
               4 File(s)          4,965 bytes

 Directory of C:\Work\modernjee\test5\bingen

08/17/2013  10:07 PM    <DIR>          .
08/17/2013  10:07 PM    <DIR>          ..
08/17/2013  10:07 PM    <DIR>          test5
               0 File(s)              0 bytes

 Directory of C:\Work\modernjee\test5\bingen\test5

08/17/2013  10:07 PM    <DIR>          .
08/17/2013  10:07 PM    <DIR>          ..
08/17/2013  10:07 PM    <DIR>          soap
               0 File(s)              0 bytes

 Directory of C:\Work\modernjee\test5\bingen\test5\soap

08/17/2013  10:07 PM    <DIR>          .
08/17/2013  10:07 PM    <DIR>          ..
08/17/2013  10:07 PM             1,029 ObjectFactory.class
08/17/2013  10:07 PM               193 package-info.class
08/17/2013  10:07 PM               652 T1.class
08/17/2013  10:07 PM               725 T1Array.class
08/17/2013  10:07 PM               860 TestManagerSOAP.class
08/17/2013  10:07 PM             2,088 TestManagerSOAPService.class
               6 File(s)          5,547 bytes

 Directory of C:\Work\modernjee\test5\descrip

08/17/2013  10:32 PM    <DIR>          .
08/17/2013  10:32 PM    <DIR>          ..
08/17/2013  10:32 PM               610 web.xml
               1 File(s)            610 bytes

 Directory of C:\Work\modernjee\test5\jsp

08/17/2013  10:02 PM    <DIR>          .
08/17/2013  10:02 PM    <DIR>          ..
08/11/2013  11:01 PM             1,164 test.jsp
               1 File(s)          1,164 bytes

 Directory of C:\Work\modernjee\test5\mysql2jboss

08/17/2013  10:02 PM    <DIR>          .
08/17/2013  10:02 PM    <DIR>          ..
08/03/2013  11:17 PM             1,343 module.xml
08/03/2013  11:05 PM           855,948 mysql-connector-java-5.1.26-bin.jar
               2 File(s)        857,291 bytes

 Directory of C:\Work\modernjee\test5\src

08/17/2013  10:03 PM    <DIR>          .
08/17/2013  10:03 PM    <DIR>          ..
08/17/2013  10:02 PM    <DIR>          test5
               0 File(s)              0 bytes

 Directory of C:\Work\modernjee\test5\src\test5

08/17/2013  10:02 PM    <DIR>          .
08/17/2013  10:02 PM    <DIR>          ..
08/17/2013  10:04 PM    <DIR>          ejb
08/17/2013  10:56 PM    <DIR>          web
               0 File(s)              0 bytes

 Directory of C:\Work\modernjee\test5\src\test5\ejb

08/17/2013  10:04 PM    <DIR>          .
08/17/2013  10:04 PM    <DIR>          ..
08/17/2013  10:04 PM               881 ClientHandler.java
08/17/2013  10:04 PM               605 T1.java
08/17/2013  10:04 PM               198 TestManager.java
08/17/2013  10:04 PM             1,381 TestManagerBean.java
08/17/2013  10:04 PM               964 TestManagerService.java
               5 File(s)          4,029 bytes

 Directory of C:\Work\modernjee\test5\src\test5\web

08/17/2013  10:56 PM    <DIR>          .
08/17/2013  10:56 PM    <DIR>          ..
08/17/2013  10:04 PM               697 GetTime.java
08/17/2013  10:04 PM               174 LoadRest.java
08/18/2013  10:04 AM               688 Test.java
08/17/2013  10:04 PM               983 TestController.java
08/17/2013  10:04 PM               806 TestManagerREST.java
08/17/2013  10:04 PM               603 TestManagerSOAP.java
               6 File(s)          3,951 bytes

 Directory of C:\Work\modernjee\test5\srccli

08/17/2013  10:04 PM    <DIR>          .
08/17/2013  10:04 PM    <DIR>          ..
08/17/2013  10:02 PM    <DIR>          test5
               0 File(s)              0 bytes

 Directory of C:\Work\modernjee\test5\srccli\test5

08/17/2013  10:02 PM    <DIR>          .
08/17/2013  10:02 PM    <DIR>          ..
08/17/2013  10:05 PM    <DIR>          client
               0 File(s)              0 bytes

 Directory of C:\Work\modernjee\test5\srccli\test5\client

08/17/2013  10:05 PM    <DIR>          .
08/17/2013  10:05 PM    <DIR>          ..
08/17/2013  10:05 PM             1,029 TestEJB.java
08/17/2013  10:05 PM               676 TestREST.java
08/17/2013  10:05 PM               476 TestSOAP.java
08/17/2013  10:05 PM               527 TestWeb.java
               4 File(s)          2,708 bytes

 Directory of C:\Work\modernjee\test5\srcgen

08/17/2013  10:07 PM    <DIR>          .
08/17/2013  10:07 PM    <DIR>          ..
08/17/2013  10:07 PM    <DIR>          test5
               0 File(s)              0 bytes

 Directory of C:\Work\modernjee\test5\srcgen\test5

08/17/2013  10:07 PM    <DIR>          .
08/17/2013  10:07 PM    <DIR>          ..
08/17/2013  10:07 PM    <DIR>          soap
               0 File(s)              0 bytes

 Directory of C:\Work\modernjee\test5\srcgen\test5\soap

08/17/2013  10:07 PM    <DIR>          .
08/17/2013  10:07 PM    <DIR>          ..
08/17/2013  10:07 PM             1,680 ObjectFactory.java
08/17/2013  10:07 PM                93 package-info.java
08/17/2013  10:07 PM             1,687 T1.java
08/17/2013  10:07 PM             1,804 T1Array.java
08/17/2013  10:07 PM             1,133 TestManagerSOAP.java
08/17/2013  10:07 PM             3,241 TestManagerSOAPService.java
               6 File(s)          9,638 bytes

 Directory of C:\Work\modernjee\test5\xhtml

08/18/2013  10:08 AM    <DIR>          .
08/18/2013  10:08 AM    <DIR>          ..
08/17/2013  10:51 PM               431 footer.xhtml
08/17/2013  10:15 PM               673 layout.xhtml
08/17/2013  10:51 PM    <DIR>          resources
08/18/2013  10:08 AM             1,109 test.xhtml
               3 File(s)          2,213 bytes

 Directory of C:\Work\modernjee\test5\xhtml\resources

08/17/2013  10:51 PM    <DIR>          .
08/17/2013  10:51 PM    <DIR>          ..
08/17/2013  10:51 PM    <DIR>          js
               0 File(s)              0 bytes

 Directory of C:\Work\modernjee\test5\xhtml\resources\js

08/17/2013  10:51 PM    <DIR>          .
08/17/2013  10:51 PM    <DIR>          ..
08/17/2013  10:43 PM               208 gettime.js
               1 File(s)            208 bytes

     Total Files Listed:
              54 File(s)        921,342 bytes
              83 Dir(s)  878,734,204,928 bytes free

The directory structure and files depends on the tools used so consider it inspiration only.

Article history:

Version Date Description
1.0 August 26th 2013 Initial version (in Danish) published on Eksperten.dk
2.0 August 16th 2016 Translation to English and complete reformatting and publishing here
2.1 October 8th 2016 Add content overview
2.2 October 6th 2017 Add Java EE 8 info
2.3 October 11th 2022 Add Jakarta EE 10 info

Other articles:

See list of all articles here

Comments:

Please send comments to Arne Vajhøj