Articles
Java Platform, Enterprise Edition
|
| By Ed Ort, December 2009 |
|
| |
Java Platform, Enterprise Edition (Java EE) is the industry-standard platform for building enterprise-class applications coded in the Java programming language. Based on the solid foundation of Java Platform, Standard Edition (Java SE), Java EE adds libraries and system services that support the scalability, accessibility, security, integrity, and other requirements of enterprise-class applications.
Since its initial release in 1999, Java EE has matured into a functionally rich, high performance platform. Recent releases of the platform have also stressed simplicity and ease of use. In fact, with the current release of the platform, Java EE 5, development of Java enterprise applications has never been easier or faster.
|
|
Java EE 6 adds significant new technologies and extends the usability improvements made in previous Java EE releases.
|
Progress continues. The next release of the platform, Java EE 6, adds significant new technologies, some of which have been inspired by the vibrant Java EE community. It also further simplifies the platform, extending the usability improvements made in previous Java EE releases.
This article highlights some of the significant enhancements in Java EE 6.
| |
Here are the main goals for the Java EE 6 platform:
|
|
Java EE 6 introduces the Web Profile, a subset of the Java EE platform designed for web application development.
|
|
|
More extensibility points and service provider interfaces as well as web tier features such as support for self-registration makes the platform highly extensible.
|
|
|
Usability improvements in many areas of the platform makes it even easier to develop web and enterprise applications.
|
| |
Java EE 6 adds significant new technologies that make the platform even more powerful. Three of these are described below:
Java API for RESTful Web Services (JAX-RS), JSR 311 enables you to rapidly build lightweight web services that conform to the Representational State Transfer (REST) style of software architecture. An important concept in REST is the existence of resources, each of which can be referred to with a global identifier, that is, a URI. In particular, data and functionality are considered resources that can be identified and accessed through URIs. To manipulate these resources, components of the network, clients and servers, communicate through a standardized interface such as HTTP and a small, fixed set of verbs —
GET,
PUT,
POST, and
DELETE — and exchange representations of these resources.
RESTful web services are web services built according to the REST architectural style. Building web services with the RESTful approach has emerged as a popular alternative to using SOAP-based technologies thanks to REST's lightweight nature and the ability to transmit data directly over HTTP.
|
|
JAX-RS makes it simple to create RESTful web services in Java.
|
JAX-RS furnishes a standardized API for building RESTful web services in Java. The API contributes a set of annotations and associated classes and interfaces. Applying the annotations to POJOs enables you to expose web resources. This approach makes it simple to create RESTful web services in Java.
The specification for the initial release of the technology, JAX-RS 1.0, was finalized in October 2008 and a reference implementation named Jersey is also available. Java EE 6 includes the latest release of the technology, JAX-RS 1.1, which is a maintenance release that aligns JAX-RS with new features in Java EE 6.
Let's take a look at a RESTful web service that uses JAX-RS.
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.Get;
import javax.ws.rs.Post;
import javax.ws.rs.Consumes;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core UriInfo;
import javax.ws.rs.core.UriBuilder;
import java.net.URI;
@Path ("items")
@Produces (MediaType.APPLICATION_XML)
Public class ItemsResource {
@Context UriInfo uriInfo;
@GET
Items listItems() {
Return Allitems();
}
@POST
@Consumes (MediaType.APPLICATION_XML)
Public Response create(Item item) throws ItemCreationException {
Item newItem = createItem(item);
URI newItemURI = uriInfo.getRequestUriBuilder().path(newItem.getId()).build();
return Response.created(newItemURI).build();
}
...
}
|
In this example, the
ItemsResource class is a web service that manages a set of items. The imports in the class are for JAX-RS 1.1 annotations, classes, and interfaces.
|
|
Annotations add much of the information needed to identify resources and serve HTTP requests.
|
The
@Path annotation specifies a relative path for the resource, in this case
"items". The URI for the class resource is based on the application context. So if the application context for this example is
http://example.com, the URI for the class resource is
http://example.com/items. This means that if a client directs a request to the URI
http://example.com/items, the
ItemsResource class will serve it.
The
@GET annotation specifies that the annotated method, here the
listItems() method, handles HTTP
GET requests. When a client directs an HTTP
GET request to the URI for the
ItemsResource resource, the JAX-RS runtime invokes the
listItems() method to handle the
GET request.
Notice the
@Produces annotation. It specifies the MIME media types that the methods in the resource can produce and return to the client. In the
ItemsResource example, the
@Produces annotation specifies
MediaType.APPLICATION_XML. The
MediaType class is an abstraction of a MIME media type. Constants supplied to the class identify the particular media type to be abstracted. The
MediaType.APPLICATION_XML specification is an abstraction of the MIME media type for XML content, "application/xml".
|
|
JAX-RS automatically translates between Java types and MIME media types.
|
Annotations such as
@Produces suggest some of the content type translation that JAX-RS handles automatically. For example, the
listItems() method returns a Java object of type
Items. JAX-RS automatically translates that Java type to the "application/xml" MIME type to be used in the HTTP response to the client. Note that the translation is automatic only if the returned type is supported by default. For instance, if
Items is a JAXB-annotated bean, then the translation would be automatic. However, if
Items is a POJO, you would need to implement a
MessageBodyReader to handle the serialization.
You can also specify a
@Produces annotation on a method. In that case, the MIME type you specify on the method overrides the MIME types in any
@Produces annotation that you specify on the class. For example, you could specify a
@Produces annotation for the
listItems() method as follows:
@GET
@Produces (MediaType.TEXT_PLAIN)
Items listItems() {
Return Allitems();
}
|
JAX-RS would then translate the
Items Java type to the "text/plain" MIME type, which represents plain text, and return content of that type in the HTTP response to the client.
The
@POST annotation specifies that the annotated method, in this case, the
create() method, responds to HTTP POST requests. In this example, the method creates a new item, perhaps in a database, and then returns a response indicating that it created the new item. When a client directs an HTTP
POST request to the URI for the
ItemsResource resource, the JAX-RS runtime invokes the
create() method to handle the
POST request.
Notice that the
@Consumes annotation is specified on the
create() method. The annotation specifies the MIME media types that the methods in the resource can accept from the client. As is the case for the
@Produces annotation, if you specify
@Consumes on a class, it applies to all the methods in the class. If you specify
@Consumes on a method, it overrides the MIME type in any
@Consumes annotation that you specify for the class. In the example, the
@Consumes annotation specifies that the
create() method can accept XML content, that is, the MIME type "application/xml". Here the type translation is from MIME type to Java type. When a client submits XML content in a
POST request to the URI for the
ItemsResource class, JAX-RS invokes the
create() method and automatically translates the incoming XML to the
Item Java type required for the method's argument.
|
|
Utility classes and interfaces further simplify actions related to building and using RESTful web services.
|
JAX-RS also includes a number of utility classes and interfaces that further simplify actions related to building and using RESTful web services in Java. You've already seen one of them:
MediaType, a class for abstracting MIME media types. Some others are:
UriInfo, an interface for accessing URI information. In this example, the
@Context annotation injects the
UriInfo interface into the
uriInfo field of the
ItemsResource class.
UriBuilder, a class for building URIs from their components
Response, a class represents an HTTP response
Response.ResponseBuilder, a class that builds
Response objects, in accordance with the well-known Builder Pattern
These classes and interfaces are used in the following statements in the example:
URI newItemURI = uriInfo.getRequestUriBuilder().path(newItem.getId()).build(); return Response.created(newItemURI).build(); |
The first statement builds a URI for the new item. The
getRequestUriBuilder() method is a
UriInfo method that creates a
UriBuilder object. The
path() and
build() methods are
UriBuilder methods that together construct the URI for the new item.
The second statement creates a
Response object for the new item to be returned to the client. The
created method is a
Response method that creates a
Response.ResponseBuilder object. The
build() method is a
Response.ResponseBuilder method that creates the
Response object for the new item. This object delivers metadata to the JAX-RS runtime to construct the HTTP response.
|
|
JAX-RS eliminates a lot of the low-level programming required in HTTP-aware web applications.
|
These utility classes and interfaces hide a lot of the complexity of HTTP programming — another reason why using JAX-RS is a simple way to build RESTful web services. However, this simplicity also extends beyond web services. JAX-RS can simplify the development of many types of HTTP-aware web applications. For example, if you need to build a web application that examines HTTP headers, you can probably code it in a much simpler way by using JAX-RS rather than other approaches.
JAX-RS has other convenient features. For example, JAX-RS includes a number of parameter-based annotations to extract information from a request. One of these is
@QueryParam, with which you can extract query parameters from the
Query component of a request URL. Some other parameter-based annotations are
@MatrixParam, which extracts information from URL path segments,
@HeaderParam, which extracts information from HTTP headers, and
@CookieParam which extracts information from the cookies declared in cookie-related HTTP headers.
For information about all the features in JAX-RS 1.1, see Java API for RESTful Web Services (JAX-RS), JSR 311.
|
|
CDI unifies and simplifies the EJB and JSF programming models. It allows enterprise beans to act as JSF managed beans in a JSF application, and brings transactional support to the web tier.
|
Contexts and Dependency Injection for the Java EE Platform (CDI), JSR 299 is a technology that supplies a powerful set of services to Java EE components. These services allow Java EE components, including EJB session beans and JavaServer Faces (JSF) managed beans, to be bound to lifecycle contexts, to be injected, and to interact in a loosely coupled way by firing and observing events. Perhaps most significantly, CDI unifies and simplifies the EJB and JSF programming models. It allows enterprise beans to replace JSF managed beans in a JSF application.
In essence, CDI helps bridge what was a major gap between the web tier of the Java EE platform and the enterprise tier. The enterprise tier, through technologies such as EJB and JPA, has strong support for transactional resources. For example, using EJB and JPA you can easily build an application that interacts with a database, commits or rolls back transactions on the data, and persists the data. The web tier, by comparison, is focused on presentation. Web tier technologies such as JSF and JavaServer Pages (JSP pages) render the user interface and display its content, but have no integrated facilities for handling transactional resources.
Through its services, CDI brings transactional support to the web tier. This can make it a lot easier to access transactional resources in web applications. For example, CDI makes it a lot easier to build a Java EE web application that accesses a database with persistence provided by JPA.
Let's look at some key parts of a web application that uses CDI services. The application, which processes user login and user logout requests, includes both JSF and EJB components. Here is the code for an input form on a JSF page that displays a login prompt for the web application:
<f:view>
<h:form>
<h:panelGrid columns="2" rendered="#{!login.loggedIn}">
<h:outputLabel for="username">Username:</h:outputLabel>
<h:inputText id="username" value="#{credentials.username}"/>
<h:outputLabel for="password">Password:</h:outputLabel>
<h:inputText id="password" value="#{credentials.password}"/>
</h:panelGrid>
<h:commandButton value="Login" action="#{login.login}" rendered="#{!login.loggedIn}"/>
<h:commandButton value="Logout" action="#{login.logout}" rendered="#{login.loggedIn}"/>
</h:form>
</f:view>
|
As you can see from the code, the login prompt displays fields for a user to enter a user name and password. It also displays a Login button and a Logout button. Notice the unified expression language (EL) expressions such as
#{credentials.username} and
#{login.login}. These expressions refer to beans, named
credentials and
login.
|
|
CDI builds on managed beans, which are designed to unify all of the various types of beans in Java EE 6.
|
Note that CDI builds on a new concept introduced in Java EE 6 called managed beans, which is designed to unify all of the various types of beans in Java EE 6. A managed bean is a Java class that is treated as a managed component by the Java EE container. Optionally, you can give it a name in the same namespace as that used by EJB components. A managed bean can also rely on a small number of container-provided services, mostly related to lifecycle management and resource injection. Other Java EE technologies such as JSF, EJB, and CDI build on this basic definition of a managed bean by adding services. So for example, a JSF managed bean adds lifecycle scopes, an EJB session bean adds services such as support for transactions, and CDI adds services such as dependency injection. In CDI a managed bean or simply a bean is a Java EE component that can be injected into other components, associated with a context, or reached through EL expressions.
You declare a managed bean by annotating its class with the
javax.annotation.ManagedBean annotation or by using one of several CDI annotations such as a scope annotation or a qualifier annotation. Scope annotations and qualifier annotations are discussed later in this section. The annotation-based programming model makes it possible for a bean to begin as a POJO and later become another type of Java EE component such as an EJB component — perhaps to take advantage of more advanced functionality, such as transactional and security annotations or the instance pooling facility offered by EJB containers. For example, you can turn a POJO into a stateful session bean by adding a
@Stateful annotation to the object. Clients that use CDI to access a bean are unaffected by the bean's transition from POJO to EJB.
Any bean can be bound to a lifecycle context, can be injected, and can interact with other beans in a loosely coupled way by firing and observing events. In addition, a bean may be called directly from Java code, or as in this example, it may be invoked in a unified EL expression. This enables a JSF page to directly access a bean, even a bean that is implemented as an EJB component such as a session bean.
In this application, a bean named
Credentials has a lifecycle that is bound to the JSF request. The
Credentials bean is implemented as a JavaBean as follows:
@Model
public class Credentials {
private String username;
private String password;
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
}
|
To request CDI services, you annotate a Java EE component with CDI annotations. The
@Model annotation is a CDI annotation that identifies the
Credentials bean as a model object in an Model-View-Controller (MVC) architecture. The annotation, which is built into CDI, is a stereotype annotation. A stereotype annotation marks a class as fulfilling a specific role within the application.
|
|
CDI services allow Java EE components, including EJB components, to be bound to lifecycle events.
|
The application also includes a
Login bean whose lifecycle is bound to the HTTP session. The
Login bean is implemented as an EJB stateful session bean, as follows:
@Stateful
@SessionScoped
@Model
public class Login {
@Inject Credentials credentials;
@Inject EntityManager userDatabase;
private User user;
@TransactionAttribute(REQUIRES_NEW)
@RolesAllowed("guest")
public void login() {
...
}
public void logout() {
user = null;
}
public boolean isLoggedIn() {
return user!=null;
}
@RolesAllowed("user")
@Produces @LoggedIn User getCurrentUser() {
...
}
}
|
The
@Stateful annotation is an EJB annotation that specifies that this bean is an EJB stateful session bean. The
@TransactionAttribute and
@RolesAllowed annotations are also EJB annotations. They declare the EJB transaction demarcation and security attributes of the annotated methods.
|
|
All beans have a scope. Among other things, this gives EJB components access to the request, session, and application contexts of web tier components.
|
The
@SessionScoped annotation is a CDI annotation that specifies a scope for the bean. All beans have a scope that determines the lifecycle of its instances and the instances of the bean that are made visible to instances of other beans. This is an important feature because components such as EJB components do not have a well-defined scope. In particular, EJB components are not aware of the request, session, and application contexts of web tier components such as JSF managed beans, and do not have access to the state associated with those contexts. In addition, the lifecycle of a stateful EJB component cannot be scoped to a web-tier context.
By contrast, scoped objects in CDI exist in a well-defined lifecycle context that is managed by the Java EE container. Scoped objects may be automatically created when needed and then automatically destroyed when the context in which they were created ends. Significantly, the state of a scoped object is automatically shared by clients that execute in the same context. This means that clients such as other beans that execute in the same context as a scoped object see the same instance of the object. But clients in a different context see a different instance. The
@SessionScoped annotation specifies that the scope type for the
Login bean is session scope. Objects that are not associated with any of the usual scopes, but instead exist for the exclusive benefit of an object that triggered their creation, are said to be
dependents of their owner. The lifecycle of these dependent objects is tied to that of the owner. In particular, a dependent object is destroyed whenever the owner is destroyed.
|
|
CDI services allow Java EE components, including EJB and JPA components, to be injected.
|
Beans typically acquire references to other beans through dependency injection. The dependency injection mechanism is completely type safe. CDI uses the annotations specified in
JSR 330: Dependency Injection for Java for dependency injection. One of those annotations,
@Inject, identifies a point at which a dependency on a Java class or interface can be injected. The container then provides the needed resource. In this example, the
Login bean specifies two injection points. The first use of the
@Inject annotation in the example injects a dependency on the
Credentials bean. In response, the container will inject the
Credentials bean into any instance of
Login created within this context. The second
@Inject annotation injects a dependency on the JPA
EntityManager. The container will inject the
EntityManager to manage the persistence context. Refer to
Standardized Annotations for Dependency Injection to learn more about the
@Inject annotation and other annotations in JSR 330.
The
@Produces annotation identifies the
getCurrentUser() method as a producer method. A producer method is called whenever another bean in the system needs an injected object of the specified type. In this case, the injected object is the currently logged-in user, which is injected by the qualifier annotation
@LoggedIn. A
qualifier identifies a specific implementation of a Java class or interface to be injected. In order to use a qualifier annotation, you first need to define its type as a qualifier. You use the
@Qualifier annotation, another JSR 330 annotation, to do that. For example:
@Target( { TYPE, METHOD, PARAMETER, FIELD })
@Retention(RUNTIME)
@Documented
@Qualifier
public @interface LoggedIn {...}
|
Let's return to the login prompt discussed earlier. When a user responds to the prompt and clicks the Submit button, CDI technology goes into action. The Java EE container automatically instantiates a contextual instance of the
Credentials bean and the
Login bean. An instance of a bean that is bound to a context is called a
contextual instance. JSF assigns the user name and password the user entered to the
Credentials bean contextual instance. Next, JSF calls the
login() method in the
Login bean contextual instance. This instance continues to exist for and is available to other requests in the same HTTP session, and provides the
User object that represents the current user to any other bean that requires it.
This example demonstrates only some of the features in this powerful technology. Another feature enables beans to produce or consume events. Yet another lets you define interceptors that bind additional function across all bean types, or define decorators that apply the additional function to a specific bean type. To learn about these and the other features in CDI technology, see Contexts and Dependency Injection for the Java EE Platform, JSR 299.
Validating data is a common task that occurs throughout an application. For example, in the presentation layer of an application, you might want to validate that the number of characters a user enters in a text field is at most 20 characters or that the number a user enters in a numeric field is positive. If you set those constraints, you probably want the same data to be validated before it's used in the business logic of the application and when the data is stored in a database.
Developers often code the same validation logic in multiple layers of an application, something that's time consuming and error-prone. Or they put the validation logic in their data model, cluttering it with what is essentially metadata.
|
|
Bean Validation affords a standard framework for validation, in which the same set of validations can be shared by all the layers of an application.
|
Bean Validation, JSR 303 makes validation simpler and reduces the duplication, errors, and clutter that characterizes the way validation is often handled in enterprise applications. Bean Validation affords a standard framework for validation, in which the same set of validations can be shared by all the layers of an application.
Specifically, Bean Validation offers a framework for validating Java classes written according to JavaBeans conventions. You use annotations to specify constraints on a JavaBean — you can annotate a JavaBean class, field, or property. You can also extend or override these constraints through XML descriptors. A validator class then validates each constraint. You specify which validator class to use for a given type of constraint.
Here, for example, is part of a class that declares some constraints through Bean Validation annotations:
public class Address {
@NotNull @Size(max=30)
private String addressline1;
@Size(max=30)
private String addressline2;
...
public String getAddressline1() {
return addressline1;
}
public void setAddressline1(String addressline1) {
this.addressline1 = addressline1;
}
...
}
|
The
@NotNull annotation specifies that the annotated element,
addressline1, must not be null. The
@Size annotation specifies that the annotated elements,
addressline1 and
addressline2, must not be longer than the specified maximum, 30 characters.
When an
Address object is validated, the
addressline1 value is passed to a validator class that is defined for the
@NotNull constraint as well as to a validator class defined for the
@Size constraint. The
addressline2 value is also passed to the validator class for the
@Size constraint. The pertinent validator classes perform the validations.
|
|
Bean Validation includes a number of built-in constraint definitions. You add your own constraints by declaring an annotation type that specifies a validator class.
|
Both the
@NotNull and
@Size constraints are built into the Bean Validation framework so you do not need to define validator classes for them. However, you can add your own constraints to the built-in constraints, in which case, you need to define validator classes. For example, you can define a constraint named
@ZipCode as follows:
@Size(min=5, max=5)
@ConstraintValidator(ZipcodeValidator.class)
@Documented
@Target({ANNOTATION_TYPE, METHOD, FIELD})
@Retention(RUNTIME)
public @interface ZipCode {
String message() default "Wrong zipcode";
String[] groups() default {};
}
|
Then you can specify the
@ZipCode constraint on a class, field, or property just like any other constraint. Here is an example:
public class Address {
...
@ZipCode
private String zipCode;
public String getZipCode() {
return zipCode;
}
public void setZipCode(String zipCode) {
this.zipCode = zipCode;
}
...
}
|
When an
Address object is validated, the
zipCode value is passed to the
ZipcodeValidator class for validation. Notice that the constraint definition includes another constraint:
@Size(min=5, max=5). This means that an element annotated by the
@ZipCode annotation must be exactly 5 characters in length. The element is validated against this constraint in addition to the primary constraint check that
ZipcodeValidator performs. Bean Validation allows you to create a constraint that is composed of other constraints. In fact, any composing constraint can itself be composed of constraints. Notice too that the constraint definition specifies an error message to be returned if the constraint fails the validation check. Here, the error message is "Wrong zipcode".
|
|
In addition to validating individual objects, you can use Bean Validation to validate an entire object graph.
|
You can also use Bean Validation to validate an entire object graph in a straightforward way. An
object graph is an object composed of other objects. If you specify the
@Valid annotation on the root object of an object graph, it directs the pertinent validator to recursively validate the associated objects in the object graph. Consider the following example:
public class Order {
@OrderNumber private String orderNumber;
@Valid @NotNull private Address delivery;
}
|
When an
Order object is validated, the
Address object and the associated objects in its object graph are validated too.
|
|
Bean Validation is integrated across the Java EE 6 platform.
|
To meet the objective of sharing the same set of validations across all the layers of an application, Bean Validation is integrated across the Java EE 6 platform. For example, presentation-layer technologies such as JSF and enterprise-layer technologies such as JPA have access to the constraint definitions and validators available through the Bean Validation framework. You no longer need to specify constraints in multiple places and in multiple ways across the layers of an application.
To learn more about Bean Validation, see Bean Validation, JSR 303.
» Continue to the next part of this article
| |
| |