Java EE 8 is a big release, desired and anticipated by the global community for about four years. More than ever before, the whole platform is now even more robust, mature, and stable.
This chapter will cover the main APIs that we can highlight for Java EE 8. Not that they are the only topics covered by this releaseâfar from itâbut they have a big role in the enterprise context and are worthy of a careful look inside.
In this chapter, we will cover the following recipes:
- Running your first Bean Validation 2.0 codeÂ
- Running your first CDI 2.0 codeÂ
- Running your first JAX-RS 2.1 codeÂ
- Running your first JSF 2.3 codeÂ
- Running your first JSON-P 1.1 codeÂ
- Running your first JSON-B 1.0Â
- Running your first Servlet 4.0 codeÂ
- Running your first Security API 1.0Â
- Running your first MVC 1.0 codeÂ
Bean Validation is a Java specification that basically helps you to protect your data. Through its API, you can validate fields and parameters, express constraints using annotations, and extend your customs' validation rules.
It can be used both with Java SE and Java EE.
In this recipe, you will have a glimpse of Bean Validation 2.0. It doesn't matter whether you are new to it or already using version 1.1; this content will help you get familiar with some of its new features.
First, you need to add the right Bean Validation dependency to your project, as follows:
<dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <dependency> <groupId>org.hamcrest</groupId> <artifactId>hamcrest-core</artifactId> <version>1.3</version> <scope>test</scope> </dependency> <dependency> <groupId>javax</groupId> <artifactId>javaee-api</artifactId> <version>8.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.hibernate.validator</groupId> <artifactId>hibernate-validator</artifactId> <version>6.0.8.Final</version> </dependency> <dependency> <groupId>org.glassfish</groupId> <artifactId>javax.el</artifactId> <version>3.0.1-b10</version> </dependency> </dependencies>
- First, we need to create an object with some fields to be validated:
public class User {
@NotBlank
private String name;
@Email
private String email;
@NotEmpty
private List<@PositiveOrZero Integer> profileId;
public User(String name, String email, List<Integer> profileId) {
this.name = name;
this.email = email;
this.profileId = profileId;
}
}
- Then we create a
test
class to validate those constraints:
public class UserTest { private static Validator validator; @BeforeClass public static void setUpClass() { validator = Validation.buildDefaultValidatorFactory() .getValidator(); } @Test public void validUser() { User user = new User( "elder", "[email protected]", asList(1,2)); Set<ConstraintViolation<User>> cv = validator .validate(user); assertTrue(cv.isEmpty()); } @Test public void invalidName() { User user = new User( "", "[email protected]", asList(1,2)); Set<ConstraintViolation<User>> cv = validator .validate(user); assertEquals(1, cv.size()); } @Test public void invalidEmail() { User user = new User( "elder", "elder-eldermoraes_com", asList(1,2)); Set<ConstraintViolation<User>> cv = validator .validate(user); assertEquals(1, cv.size()); } @Test public void invalidId() { User user = new User( "elder", "[email protected]", asList(-1,-2,1,2)); Set<ConstraintViolation<User>> cv = validator .validate(user); assertEquals(2, cv.size()); } }
Our User
 class uses three of the new constraints introduced by Bean Validation 2.0:
@NotBlank
: Assures that the value is not null, empty, or an empty string (it trims the value before evaluation, to make sure there aren't spaces).@Email
: Allows only a valid email format. Forget those crazy JavaScript functions!@NotEmpty
: Ensures that a list has at least one item.@PositiveOrZero
: Guarantees that a number is equal or greater than zero.
Then we create a test
class (using JUnit) to test our validations. It first instantiates Validator
:
@BeforeClass public static void setUpClass() { validator = Validation.buildDefaultValidatorFactory().getValidator(); }
Validator
is an API that validates beans according to the constraints defined for them.
Our first test
method tests a valid user, which is a User
object that has:
- Name not empty
- Valid email
profileId
list only with integers greater than zero:
User user = new User( "elder", "[email protected]", asList(1,2));
And finally, the validation:
Set<ConstraintViolation<User>> cv = validator.validate(user);
The validate()
method from Validator
returns a set of constraint violations found, if any, or an empty set if there are no violations at all.
So, for a valid user it should return an empty set:
assertTrue(cv.isEmpty());
And the other methods work with variations around this model:
invalidName()
: Uses an empty nameinvalidEmail()
: Uses a malformed emailinvalidId()
: Adds some negative numbers to the list
Note that the invalidId()
method adds two negative numbers to the list:
asList(-1,-2,1,2));
So, we expect two constraint violations:
assertEquals(2, cv.size());
In other words, Validator
checks not only the constraints violated, but how many times they are violated.
- You can check the Bean Validation 2.0 specification at http://beanvalidation.org/2.0/spec/
- The full source code of this recipe is at https://github.com/eldermoraes/javaee8-cookbook/tree/master/chapter01/ch01-beanvalidation/
Context and Dependency Injection (CDI) is certainly one of the most important APIs for the Java EE platform. In version 2.0, it also works with Java SE.
Nowadays, CDI has an impact on many other APIs in the Java EE platform. As said in an interview for Java EE 8 â The Next Frontier project:
"If there was CDI by the time we created JSF, it would be made completely different." Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â â Ed Burns, JSF Spec Lead
There is a lot of new features in CDI 2.0. This recipe will cover Observer Ordering to give you a quick start.
First, you need to add the right CDI 2.0 dependency to your project. To make things easier at this point, we are going to use CDI SE, the dependency that allows you to use CDI without a Java EE server:
<dependency> <groupId>org.jboss.weld.se</groupId> <artifactId>weld-se-shaded</artifactId> <version>3.0.0.Final</version> </dependency>
This recipe will show you one of the main features introduced by CDI 2.0: Ordered Observers. Now, you can turn the observers job into something predictable:
- First, let's make an event to be observed:
public class MyEvent { private final String value; public MyEvent(String value){ this.value = value; } public String getValue(){ return value; } }
- Now, we build our observers and the server that will fire them:
public class OrderedObserver { public static void main(String[] args){ try(SeContainer container = SeContainerInitializer.newInstance().initialize()){ container .getBeanManager() .fireEvent(new MyEvent("event: " + System.currentTimeMillis())); } } public void thisEventBefore( @Observes @Priority(Interceptor.Priority .APPLICATION - 200) MyEvent event){ System.out.println("thisEventBefore: " + event.getValue()); } public void thisEventAfter( @Observes @Priority(Interceptor.Priority .APPLICATION + 200) MyEvent event){ System.out.println("thisEventAfter: " + event.getValue()); } }
Also, don't forget to add the beans.xml
file into the META-INF
folder:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd" bean-discovery-mode="all"> </beans>
- Once you run it, you should see a result like this:
INFO: WELD-ENV-002003: Weld SE container 353db40d-e670-431d-b7be-4275b1813782 initialized thisEventBefore: event -> 1501818268764 thisEventAfter: event -> 1501818268764
First, we are building a server to manage our event and observers:
public static void main(String[] args){ try(SeContainer container = SeContainerInitializer.newInstance().initialize()){ container .getBeanManager() .fireEvent(new ExampleEvent("event: " + System.currentTimeMillis())); } }
This will give us all the resources needed to run the recipe as if it was a Java EE server.
Then we build an observer:
public void thisEventBefore( @Observes @Priority(Interceptor.Priority.APPLICATION - 200) MyEvent event){ System.out.println("thisEventBefore: " + event.getValue()); }
So, we have three important topics:
@Observes
: This annotation is used to tell the server that it needs to watch the events fired withMyEvent
@Priority
: This annotation informs in which priority order this observer needs to run; it receives anÂint
parameter, and the execution order is ascendantMyEvent event
: The event being observed
On the thisEventBefore
method and thisEventAfter
, we only changed the @Priority
value and the server took care of running it in the right order.
The behavior would be exactly the same in a Java EE 8 server. You just wouldn't need SeContainerInitializer
and would need to change the dependencies to the following:
<dependency> <groupId>javax</groupId> <artifactId>javaee-api</artifactId> <version>8.0</version> <scope>provided</scope> </dependency>
- You can stay tuned with everything related to the CDI Specification at http://www.cdi-spec.org/
- The source code of this recipe is at https://github.com/eldermoraes/javaee8-cookbook/tree/master/chapter01/ch01-cdi
JAX-RS is an API designed to give a portable and standard way for building RESTful web services in Java. This is one of the most used technologies for data transporting between different applications that uses some network (internet included) for communication.
One of the coolest features introduced by the 2.1 release is Server-Sent Events (SSE), which will be covered in this recipe. SSE is a specification created by HTML5 where it has established a channel between server and client, one way only from server to client. It is a protocol that transports a message containing some data.
Let's start by adding the right dependency to our project:
<dependencies> <dependency> <groupId>org.glassfish.jersey.containers</groupId> <artifactId>jersey-container-grizzly2-http</artifactId> <version>2.26-b09</version> </dependency> <dependency> <groupId>org.glassfish.jersey.inject</groupId> <artifactId>jersey-hk2</artifactId> <version>2.26-b09</version> </dependency> <dependency> <groupId>org.glassfish.jersey.media</groupId> <artifactId>jersey-media-sse</artifactId> <version>2.26-b09</version> </dependency> <dependency> <groupId>javax</groupId> <artifactId>javaee-api</artifactId> <version>8.0</version> <scope>provided</scope> </dependency> </dependencies>
You surely noticed that we are using Jersey here. Why? Because Jersey is the reference implementation for JAX-RS, which means that all JAX-RS specifications are first implemented through Jersey.
Moreover, with Jersey we can use Grizzly to start a small local server, which will be useful for this recipe, as we need just a few server features to show the SSE behavior.
Further on in this book, we will use a full GlassFish to build more JAX-RS recipes.
public class ServerMock { public static final URI CONTEXT = URI.create("http://localhost:8080/"); public static final String BASE_PATH = "ssevents"; public static void main(String[] args) { try { final ResourceConfig resourceConfig = new ResourceConfig(SseResource.class); final HttpServer server = GrizzlyHttpServerFactory.createHttpServer(CONTEXT, resourceConfig, false); server.start(); System.out.println(String.format("Mock Server started at %s%s", CONTEXT, BASE_PATH)); Thread.currentThread().join(); } catch (IOException | InterruptedException ex) { System.out.println(ex.getMessage()); } } }
- Then, we create a JAX-RS endpoint to send the events to the clients:
@Path(ServerMock.BASE_PATH)
public class SseResource {
private static volatile SseEventSink SINK = null;
@GET
@Produces(MediaType.SERVER_SENT_EVENTS)
public void getMessageQueue(@Context SseEventSink sink) {
SseResource.SINK = sink;
}
@POST
public void addMessage(final String message, @Context Sse sse)
throws IOException {
if (SINK != null) {
SINK.send(sse.newEventBuilder()
.name("sse-message")
.id(String.valueOf(System.currentTimeMillis()))
.data(String.class, message)
.comment("")
.build());
}
}
}
- Then, we create a client class to consume the events generated from the server:
public class ClientConsumer { public static final Client CLIENT = ClientBuilder.newClient(); public static final WebTarget WEB_TARGET = CLIENT.target(ServerMock.CONTEXT + BASE_PATH); public static void main(String[] args) { consume(); } private static void consume() { try (final SseEventSource sseSource = SseEventSource .target(WEB_TARGET) .build()) { sseSource.register(System.out::println); sseSource.open(); for (int counter=0; counter < 5; counter++) { System.out.println(" "); for (int innerCounter=0; innerCounter < 5; innerCounter++) { WEB_TARGET.request().post(Entity.json("event " + innerCounter)); } Thread.sleep(1000); } CLIENT.close(); System.out.println("\n All messages consumed"); } catch (InterruptedException e) { System.out.println(e.getMessage()); } } }
To try it out, you have to first run the ServerMock
class and then the ClientConsumer
class. If everything worked well, you should see something like this:
InboundEvent{name='sse-message', id='1502228257736', comment='', data=event 0} InboundEvent{name='sse-message', id='1502228257753', comment='', data=event 1} InboundEvent{name='sse-message', id='1502228257758', comment='', data=event 2} InboundEvent{name='sse-message', id='1502228257763', comment='', data=event 3} InboundEvent{name='sse-message', id='1502228257768', comment='', data=event 4}
These are the messages sent from the server to the client.
This recipe is made up of three parts:
- The server, represented by the
ServerMock
class - The SSE engine, represented by the
SseResource
class - The client, represented by the
ClientConsumer
class
So once ServerMock
is instantiated, it registers the SseResource
 class:
final ResourceConfig resourceConfig = new ResourceConfig(SseResource.class); final HttpServer server = GrizzlyHttpServerFactory.createHttpServer(CONTEXT, resourceConfig, false); server.start();
Then two key methods from SseResource
take place. The first one adds messages to the server queue:
addMessage(final String message, @Context Sse sse)
The second one consumes this queue and sends the messages to the clients:
@GET @Produces(MediaType.SERVER_SENT_EVENTS) public void getMessageQueue(@Context SseEventSink sink)
Note that this one has a media type SERVER_SENT_EVENTS
, introduced in this version for this very purpose. And finally, we have our client. In this recipe, it is both posting and consuming messages.Â
It consumes here:
sseSource.register(System.out::println); sseSource.open();
ServerMock.WEB_TARGET.request().post(Entity.json("event " + innerCounter));
- You can stay tuned with everything related to JAX-RS at https://github.com/jax-rs
- The source code of this recipe is at https://github.com/eldermoraes/javaee8-cookbook/tree/master/chapter01/ch01-jaxrs
JavaServer Faces (JSF) is the Java technology made to simplify the process of building a UIs, despite how it's made for the frontend and the UI is built in the backend.
With JSF, you can build components and use (or reuse) them in the UI in an extensible way. You can also use other powerful APIs, such as CDI and Bean Validation, to improve your application and its architecture.
In this recipe, we will use the Validator
and Converter
interfaces with the new feature introduced by version 2.3, which is the possibility of using them with generic parameters.
First, we need to add the dependencies needed:
<dependency> <groupId>javax</groupId> <artifactId>javaee-api</artifactId> <version>8.0</version> <scope>provided</scope> </dependency>
- Let's create a
User
class as the main object of our recipe:
public class User implements Serializable { private String name; private String email; public User(String name, String email) { this.name = name; this.email = email; } //DON'T FORGET THE GETTERS AND SETTERS //THIS RECIPE WON'T WORK WITHOUT THEM }
- Now, we create a
UserBean
class to manage our UI:
@Named @ViewScoped public class UserBean implements Serializable { private User user; public UserBean(){ user = new User("Elder Moraes", "[email protected]"); } public void userAction(){ FacesContext.getCurrentInstance().addMessage(null, new FacesMessage("Name|Password welformed")); } //DON'T FORGET THE GETTERS AND SETTERS //THIS RECIPE WON'T WORK WITHOUT THEM }
- Now, we implement the
Converter
interface with aUser
parameter:
@FacesConverter("userConverter") public class UserConverter implements Converter<User> { @Override public String getAsString(FacesContext fc, UIComponent uic, User user) { return user.getName() + "|" + user.getEmail(); } @Override public User getAsObject(FacesContext fc, UIComponent uic, String string) { return new User(string.substring(0, string.indexOf("|")), string.substring(string.indexOf("|") + 1)); } }
- Now, we implement the
Validator
interface with aUser
parameter:
@FacesValidator("userValidator") public class UserValidator implements Validator<User> { @Override public void validate(FacesContext fc, UIComponent uic, User user) throws ValidatorException { if(!user.getEmail().contains("@")){ throw new ValidatorException(new FacesMessage(null, "Malformed e-mail")); } } }
- And then we create our UI using all of them:
<h:body> <h:form> <h:panelGrid columns="3"> <h:outputLabel value="Name|E-mail:" for="userNameEmail"/> <h:inputText id="userNameEmail" value="#{userBean.user}" converter="userConverter" validator="userValidator"/> <h:message for="userNameEmail"/> </h:panelGrid> <h:commandButton value="Validate" action="#{userBean.userAction()}"/> </h:form> </h:body>
Don't forget to run it in a Java EE 8 server.
The UserBean
class manages the communication between the UI and the server. Once you instantiate the user
object, it is available for both of them.
That's why when you run it, the Name | E-mail
is already filled (the user
 object is instantiated when the UserBean
 class is created by the server).
We associated the userAction()
method from the UserBean
 class to the Validate
 button of the UI:
<h:commandButton value="Validate" action="#{userBean.userAction()}"/>
You can create other methods in UserBean
and do the same to empower your application.
The whole core of our recipe is represented by just a single line in the UI:
<h:inputText id="userNameEmail" value="#{userBean.user}" converter="userConverter" validator="userValidator"/>
So, our two implemented interfaces used here are userConverter
and userValidator
.
Basically, the UserConverter
class (with getAsString
and getAsObject
methods) converts an object to/from a string and vice versa, according to the logic defined by you.
We have just mentioned it in the preceding code snippet:
value="#{userBean.user}"
The server uses the userConverter
object, calls the getAsString
method, and prints the result using the preceding expression language.
Finally, the UserValidator
 class is automatically called when you submit the form, by calling its validate
 method, and applying the rules defined by you.
You could increase the validators by adding a Bean Validation on it and, for example, defining the email
property from User
with an @Email
constraint.
- You can stay tuned with everything related to JSF at https://javaserverfaces.github.io/
- The source code of this recipe is at https://github.com/eldermoraes/javaee8-cookbook/tree/master/chapter01/ch01-jsf
JSON-Pointer is the Java API for JSON processing. By processing, we mean generating, transforming, parsing, and querying JSON strings and/or objects.
In this recipe, you will learn how to use JSON Pointer to get a specific value from a JSON message in a very easy way.
Let's get our dependency
:
<dependency> <dependency> <groupId>javax</groupId> <artifactId>javaee-api</artifactId> <version>8.0</version> <scope>provided</scope> </dependency> </dependency>
- First, we define a JSON message to represent aÂ
User
object:
{ "user": { "email": "[email protected]", "name": "Elder", "profile": [ { "id": 1 }, { "id": 2 }, { "id": 3 } ] } }
- Now, we create a method to read it and print the values we want:
public class JPointer {
public static void main(String[] args) throws IOException{
try (InputStream is =
JPointer.class.getClassLoader().getResourceAsStream("user.json");
JsonReader jr = Json.createReader(is)) {
JsonStructure js = jr.read();
JsonPointer jp = Json.createPointer("/user/profile");
JsonValue jv = jp.getValue(js);
System.out.println("profile: " + jv);
}
}
}
The execution of this code prints the following:
profile: [{"id":1},{"id":2},{"id":3}]
The JSON Pointer is a standard defined by the Internet Engineering Task Force (IETF) under Request for Comments (RFC) 6901. The standard basically says that a JSON Pointer is a string that identifies a specific value in a JSON document.
Without a JSON Pointer, you would need to parse the whole message and iterate through it until you find the desired value; probably lots of ifs, elses, and things like that.
So, JSON Pointer helps you to decrease the written code dramatically by doing this kind of operation in a very elegant way.
- You can stay tuned with everything related to JSON-P at https://javaee.github.io/jsonp/
- The source code of this recipe is at https://github.com/eldermoraes/javaee8-cookbook/tree/master/chapter01/ch01-jsonp
JSON-B is an API for converting Java objects to/from JSON messages in a standardized way. It defines a default mapping algorithm to convert Java classes to JSON and still lets you customize your own algorithms.
With JSON-B, Java EE now has a complete set of tools to work with JSON, such as JSON API, and JSON-P. No third-party frameworks are needed anymore (although you are still free to use them).
This quick recipe will show you how to use JSON-B to convert a Java object to and from a JSON message.
Let's add our dependencies to the project:
<dependencies> <dependency> <groupId>org.eclipse</groupId> <artifactId>yasson</artifactId> <version>1.0</version> </dependency> <dependency> <groupId>org.glassfish</groupId> <artifactId>javax.json</artifactId> <version>1.1</version> </dependency> </dependencies>
- Let's create aÂ
User
class as a model for our JSON message:
public class User { private String name; private String email; public User(){ } public User(String name, String email) { this.name = name; this.email = email; } @Override public String toString() { return "User{" + "name=" + name + ", email=" + email + '}'; } //DON'T FORGET THE GETTERS AND SETTERS //THIS RECIPE WON'T WORK WITHOUT THEM }
- Then, let's create a class to use JSON-B to transform an object:
public class JsonBUser { public static void main(String[] args) throws Exception { User user = new User("Elder", "[email protected]"); Jsonb jb = JsonbBuilder.create(); String jsonUser = jb.toJson(user); User u = jb.fromJson(jsonUser, User.class); jb.close(); System.out.println("json: " + jsonUser); System.out.println("user: " + u); } }
The result printed is:
json: {"email":"[email protected]","name":"Elder"} user: User{name=Elder, [email protected]}
The first line is the object transformed into a JSON string. The second is the same string converted into an object.
It uses the getters and setters defined in the User
class to transform both ways and that's why they are so important.
- You can stay tuned with everything related to JSON-B at http://json-b.net/
- The source code of this recipe is at https://github.com/eldermoraes/javaee8-cookbook/tree/master/chapter01/ch01-jsonb
Servlet 4.0 is one the of biggest APIs of Java EE 8. Since the very beginning of the Java EE platform (the old J2EE), the Servlet specification has always played a key role.
The coolest additions of this version are surely HTTP/2.0 and Server Push. Both of them bring performance improvements to your application.
This recipe will use Server Push to do one of the most basic tasks in a web pageâloading an image.
Let's add the dependencies that we need:
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>8.0</version>
<scope>provided</scope>
</dependency>
- We will create a servlet:
@WebServlet(name = "ServerPush", urlPatterns = {"/ServerPush"}) public class ServerPush extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { PushBuilder pb = request.newPushBuilder(); if (pb != null) { pb.path("images/javaee-logo.png") .addHeader("content-type", "image/png") .push(); } try (PrintWriter writer = response.getWriter();) { StringBuilder html = new StringBuilder(); html.append("<html>"); html.append("<center>"); html.append("<img src='images/javaee-logo.png'><br>"); html.append("<h2>Image pushed by ServerPush</h2>"); html.append("</center>"); html.append("</html>"); writer.write(html.toString()); } } }
- To try it, run the project in a Java EE 8 server and open this URL:
https://localhost:8080/ch01-servlet/ServerPush
We use the PushBuilder
object to send an image to the client before it is requested by the img src
 tag. In other words, the browser doesn't need to do another request (what it usually does with img src
) to have an image available for rendering.
It might seem as if it doesn't make too much difference for a single image, but it would with dozens, hundreds, or thousands of images. Less traffic for your client and from your server. Better performance for all!
If you are using JSF, you can get the benefits from Server Push for free! You don't even need to rewrite a single line of your code, as JSF relies on the Server Push specification.
Just make sure that you run it under the HTTPS protocol, as HTTP/2.0 only works under it.
- You can stay tuned with everything related to the Servlet specification at https://github.com/javaee/servlet-spec
- The source code of this recipe is at https://github.com/eldermoraes/javaee8-cookbook/tree/master/chapter01/ch01-servlet
Security is one of the top concerns when you build an enterprise application. Luckily, the Java EE platform now has this API that handles many of the enterprise requirements in a standardized way.
In this recipe, you will learn how to define roles and give them the right authorization based on rules defined in the methods that manage sensitive data.
We start by adding our dependencies to the project:
<dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <dependency> <groupId>org.apache.tomee</groupId> <artifactId>openejb-core</artifactId> <version>7.0.4</version> </dependency> <dependency> <groupId>javax</groupId> <artifactId>javaee-api</artifactId> <version>8.0</version> <scope>provided</scope> </dependency>
- We first create aÂ
User
entity:
@Entity public class User implements Serializable{ @Id private Long id; private String name; private String email; public User(){ } public User(Long id, String name, String email) { this.id = id; this.name = name; this.email = email; } //DON'T FORGET THE GETTERS AND SETTERS //THIS RECIPE WON'T WORK WITHOUT THEM }
- Here, we create a class to store our security roles:
public class Roles { public static final String ADMIN = "ADMIN"; public static final String OPERATOR = "OPERATOR"; }
- Then, we create a stateful bean to manage our user operations:
@Stateful public class UserBean { @PersistenceContext(unitName = "ch01-security-pu", type = PersistenceContextType.EXTENDED) private EntityManager em; @RolesAllowed({Roles.ADMIN, Roles.OPERATOR}) public void add(User user){ em.persist(user); } @RolesAllowed({Roles.ADMIN}) public void remove(User user){ em.remove(user); } @RolesAllowed({Roles.ADMIN}) public void update(User user){ em.merge(user); } @PermitAll public List<User> get(){ Query q = em.createQuery("SELECT u FROM User as u "); return q.getResultList(); }
- Now, we need to create an executor for each role:
public class RoleExecutor {
public interface Executable {
void execute() throws Exception;
}
@Stateless
@RunAs(Roles.ADMIN)
public static class AdminExecutor {
public void run(Executable executable) throws Exception {
executable.execute();
}
}
@Stateless
@RunAs(Roles.OPERATOR)
public static class OperatorExecutor {
public void run(Executable executable) throws Exception {
executable.execute();
}
}
}
- And finally, we create a test class to try our security rules.
 Our code uses three test methods: asAdmin()
, asOperator()
, and asAnonymous()
.
- First, it tests
asAdmin()
:
//Lot of setup code before this point @Test public void asAdmin() throws Exception { adminExecutor.run(() -> { userBean.add(new User(1L, "user1", "[email protected]")); userBean.add(new User(2L, "user2", "[email protected]")); userBean.add(new User(3L, "user3", "[email protected]")); userBean.add(new User(4L, "user4", "[email protected]")); List<User> list = userBean.get(); list.forEach((user) -> { userBean.remove(user); }); Assert.assertEquals("userBean.get()", 0, userBean.get().size()); }); }
- Then it testsÂ
asOperator()
:
@Test public void asOperator() throws Exception { operatorExecutor.run(() -> { userBean.add(new User(1L, "user1", "[email protected]")); userBean.add(new User(2L, "user2", "[email protected]")); userBean.add(new User(3L, "user3", "[email protected]")); userBean.add(new User(4L, "user4", "[email protected]")); List<User> list = userBean.get(); list.forEach((user) -> { try { userBean.remove(user); Assert.fail("Operator was able to remove user " + user.getName()); } catch (EJBAccessException e) { } }); Assert.assertEquals("userBean.get()", 4, userBean.get().size()); }); }
- And, finally it testsÂ
asAnonymous()
:
@Test public void asAnonymous() { try { userBean.add(new User(1L, "elder", "[email protected]")); Assert.fail("Anonymous user should not add users"); } catch (EJBAccessException e) { } try { userBean.remove(new User(1L, "elder", "[email protected]")); Assert.fail("Anonymous user should not remove users"); } catch (EJBAccessException e) { } try { userBean.get(); } catch (EJBAccessException e) { Assert.fail("Everyone can list users"); } }
The whole point in this recipe is to do with the @RolesAllowed
, @RunsAs
, and @PermitAll
annotations. They define what operations each role can do and what happens when a user tries an operation using the wrong role.
What we did here is called programmatic security; that is, we defined the security rules and roles through our code (the program). There's another approach called declarative security, where you declare the rules and roles through application and server configurations.
One good step up for this recipe is if you evolve the roles management to a source outside the application, such as a database or a service.
- You can stay tuned with everything related to Security API at https://github.com/javaee-security-spec
- The source code of this recipe is at https://github.com/eldermoraes/javaee8-cookbook/tree/master/chapter01/ch01-security
If you are following the news about Java EE 8, you may now be wondering: why is MVC 1.0 here if it was dropped from the Java EE 8 umbrella?
Yes, it is true. MVC 1.0 doesn't belong (anymore) to the Java EE 8 release. But it didn't reduce the importance of this great API and I'm sure it will change the way some other APIs work in future releases (for example, JSF).
So why not cover it here? You will use it anyway.
This recipe will show you how to use a Controller (the C) to inject a Model (the M) into the View (the V). It also brings some CDI and JAX-RS to the party.
Add the proper dependencies to your project:
<dependency> <groupId>javax</groupId> <artifactId>javaee-api</artifactId> <version>8.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.mvc</groupId> <artifactId>javax.mvc-api</artifactId> <version>1.0-pr</version> </dependency>
@ApplicationPath("webresources") public class AppConfig extends Application{ }
- Create a
User
class (this will be your MODEL):
public class User { private String name; private String email; public User(String name, String email) { this.name = name; this.email = email; } //DON'T FORGET THE GETTERS AND SETTERS //THIS RECIPE WON'T WORK WITHOUT THEM }
- Now, create a Session Bean, which will be injected later in your Controller:
@Stateless public class UserBean { public User getUser(){ return new User("Elder", "[email protected]"); } }
- Then, create the Controller:
@Controller @Path("userController") public class UserController { @Inject Models models; @Inject UserBean userBean; @GET public String user(){ models.put("user", userBean.getUser()); return "/user.jsp"; } }
- And finally, the web page (the View):
<head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>User MVC</title> </head> <body> <h1>${user.name}/${user.email}</h1> </body>
Run it on a Java EE 8 server and access this URL:
http://localhost:8080/ch01-mvc/webresources/userController
The main actor in this whole scenario is the Models
class injected into the Controller:
@Inject Models models;
It's a class from MVC 1.0 API that owns the responsibility, in this recipe, of letting the User
object be available for the View layer. It's injected (using CDI) and uses another injected bean, userBean
, to do it:
models.put("user", userBean.getUser());
So, the View can easily access the values from the User
object using expression language:
<h1>${user.name}/${user.email}</h1>
- You can stay tuned with everything related to MVC specification at https://github.com/mvc-spec
- The source code of this recipe is at https://github.com/eldermoraes/javaee8-cookbook/tree/master/chapter01/ch01-mvc