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.
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.
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 |
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 |
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:
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.
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:
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>
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.
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);
}
}
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)
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.
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.
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 |
See list of all articles here
Please send comments to Arne Vajhøj