星期二, 四月 19, 2016

Getting started with Java EE8 MVC(9)-View engine

View engine

Spring MVC provides View and ViewResolver to find view and render view. MVC provides ViewEngine to do the same job.

View Engine

By default, ozark support tow built-in view engines to render JSP and Facelets, respectively.
In the org.glassfish.ozark.ext (check org.glassfish.ozark.ext) package, ozark provides several ViewEngine implementations for the popular template.
  • Thymeleaf
  • Freemarker
  • Velocity
  • Handlebars
  • Mustache
  • Asciidoc
  • Stringtemplate
  • Jade
  • JSR223(Script engine)

Facelets

Originally, Facelets is part of JSF specification, now it can be worked as a standard template engine in MVC.
  1. Activate Facelets in web.xml.
    <servlet>
        <servlet-name>Faces Servlet</servlet-name>
        <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>Faces Servlet</servlet-name>
        <url-pattern>*.xhtml</url-pattern>
    </servlet-mapping>
    
    We do not need face-config.xml file to activate JSF here.
  2. Convert all jsp files in the mvc sample to facelets template. Facelets supports Composite View pattern, we can define a template for all views.
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE html>
    <html xmlns="http://www.w3.org/1999/xhtml"
        xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
        xmlns:f="http://xmlns.jcp.org/jsf/core"
        xmlns:h="http://xmlns.jcp.org/jsf/html"
        xmlns:c="http://java.sun.com/jsp/jstl/core">
        <f:view contentType="text/html" encoding="UTF-8">
            <ui:insert name="metadata"></ui:insert>
            <h:head>
                <meta charset="utf-8" />
                <title>Task List</title>
                <meta name="viewport" content="width=device-width, initial-scale=1.0" />
                <meta name="description" content="" />
                <meta name="author" content="" />
    
                <!-- styles -->
                <link href="#{request.contextPath}/webjars/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet" />
                <link href="#{request.contextPath}/webjars/font-awesome/4.3.0/css/font-awesome.min.css" rel="stylesheet" />
                <link href="#{request.contextPath}/webjars/bootstrap-material-design/0.3.0/css/material.min.css" rel="stylesheet"/>
                <link href="#{request.contextPath}/webjars/bootstrap-material-design/0.3.0/css/ripples.min.css" rel="stylesheet"/>
                <link href="#{request.contextPath}/webjars/bootstrap-material-design/0.3.0/css/roboto.min.css" rel="stylesheet"/>
    
                <link href="#{request.contextPath}/resources/css/main.css" rel="stylesheet" />
                <!-- HTML5 shim, for IE6-8 support of HTML5 elements -->
                <![CDATA[
                <!--[if lt IE 9]>
                <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
                <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
                <![endif]-->
                ]]>
    
                <ui:insert name="headIncludes"></ui:insert>
            </h:head>
    
            <h:body>
                <ui:include src="/WEB-INF/layout/header.xhtml" />
                <h:panelGroup layout="block" styleClass="container">
                    <ui:include src="/WEB-INF/layout/alert.xhtml" />
                    <h:panelGroup layout="block" styleClass="page-header">
                        <h1><ui:insert name="pageTitle"></ui:insert></h1>
                    </h:panelGroup>
                    <ui:insert name="content"/>
                </h:panelGroup>
    
                <ui:include src="/WEB-INF/layout/footer.xhtml"/>
                <!-- Placed at the end of the document so the pages load faster -->
                <script type="text/javascript" src="#{request.contextPath}/webjars/jquery/2.1.3/jquery.min.js">
                    /** stop autoclosing **/
                </script>
                <script type="text/javascript" src="#{request.contextPath}/webjars/bootstrap/3.3.5/js/bootstrap.min.js">
    
                </script>
                <script type="text/javascript" src="#{request.contextPath}/webjars/bootstrap-material-design/0.3.0/js/material.min.js">
    
                </script>
                <script type="text/javascript" src="#{request.contextPath}/webjars/bootstrap-material-design/0.3.0/js/ripples.min.js">
    
                </script>
                <script>
                    $.material.init();
                </script>
            </h:body>
        </f:view>
    </html>
    
    The template includes header and footer fragments directly, it also define a content placeholder, in the extended views the content will be inserted in the content definition.
    If you are familiar with JSF before, it is easy to understand the codes.
    Check out the codes and explore them yourself.
  3. Do not forget to change the view postfix to .xhtml in the TaskController. eg. the view in allTasks method.
    @GET
    @View("tasks.xhtml")
    public void allTasks() {
        log.log(Level.INFO, "fetching all tasks");
    
        List<Task> todotasks = taskRepository.findByStatus(Task.Status.TODO);
        List<Task> doingtasks = taskRepository.findByStatus(Task.Status.DOING);
        List<Task> donetasks = taskRepository.findByStatus(Task.Status.DONE);
    
        log.log(Level.INFO, "got all tasks: todotasks@{0}, doingtasks@{1}, donetasks@{2}", new Object[]{todotasks.size(), doingtasks.size(), donetasks.size()});
    
        models.put("todotasks", todotasks);
        models.put("doingtasks", doingtasks);
        models.put("donetasks", donetasks);
    
    } 
    

Source Codes

  1. Clone the codes from my github.com account.
    https://github.com/hantsy/ee8-sandbox/
  2. Open the mvc-facelets project in NetBeans IDE.
  3. Run it on Glassfish.
  4. After it is deployed and runging on Glassfish application server, navigate http://localhost:8080/ee8-mvc/mvc/tasks in browser.

Getting started with Java EE 8 MVC(8)-Parameter conversion

Parameter conversion

Spring MVC provides ConversionService to convert data to bean target type from from data.
In MVC, we can use custom ParamConverter to convert form data to the target type.

Parameter conversion

As an example, add a dueDate to Task entity. We need to convert the form field value(it is a String) to LocalDate type.
  1. Create a custom ParamConverterProvider for LocalDate.
    @Provider
    public class CustomConverterProvider implements ParamConverterProvider {
    
        final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ISO_DATE;
    
        @Override
        public <T> ParamConverter<T> getConverter(Class<T> rawType, Type genericType, Annotation[] annotations) {
    
            if (rawType.getName().equals(LocalDate.class.getName())) {
    
                return new ParamConverter<T>() {
                    @Override
                    public T fromString(String value) {
                        return value != null ? (T) LocalDate.parse(value, DATE_FORMAT) : null;
                    }
    
                    @Override
                    public String toString(T value) {
                        return value != null ? ((LocalDate) value).format(DATE_FORMAT) : "";
                    }
                };
            } 
            ....
    }
    
  2. Add a new field dueDate to TaskForm, which type is LocalDate.
    @NotNull
    @FormParam("duedate")
    private LocalDate dueDate;
    
  3. In the view, add a new form field named duedate.
    <input type="text" id="duedate" name="duedate" class="form-control" placeholder="Due date"/>
    
When the form is submitted, the duedate will be converted to LocalDate type and bind to dueDate property of TaskForm.

Format

Spring provides Formatting service in the display view. eg. @DateFormat and custom Formatters.
Unfortunately, MVC does not support such features. And JSTL fmt:formatDate does not accept Java 8 DateTime APIs.
We can define a custom CDI beans for date formatting, and call it via expression language.
  1. Create RequestScoped bean Formatters.
    @RequestScoped
    @Named("formatters")
    public class Formatters {
    
        static DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ISO_DATE;
    
        public String formatDate(LocalDate data) {
            return DATE_FORMATTER.format(data);
        }
    
    }
    
  2. Apply it on the dueDate in the details.jspx page.
    <dt>Due date:</dt>
    <dd>${formatters.formatDate(details.dueDate)}</dd>
    
The result looks like:
date formatting result

Source Codes

  1. Clone the codes from my github.com account.
    https://github.com/hantsy/ee8-sandbox/
  2. Open the mvc project in NetBeans IDE.
  3. Run it on Glassfish.
  4. After it is deployed and runging on Glassfish application server, navigate http://localhost:8080/ee8-mvc/mvc/tasks in browser.

Getting started with Java EE 8 MVC(7)-MVC Security

MVC Security

MVC has built-in some security features to protect pages, eg. CSRF protection.

CSRF protection

MVC has built-in CSRF protection, there is a Csrf interface.
  1. Configure Csrf in the Application class. Override the getProperties method.
    @Override
    public Map<String, Object> getProperties() {
        Map<String, Object> props = new HashMap<>();
    
        props.put(Csrf.CSRF_PROTECTION, Csrf.CsrfOptions.EXPLICIT);
    
        //view folder
        //props.put(ViewEngine.DEFAULT_VIEW_FOLDER, ViewEngine.VIEW_FOLDER);
        return super.getProperties();
    }
    
    And there are some options to configure CSRF via Csrf.CsrfOptions.
    • OFF to disable Csrf.
    • EXPLICIT to enable Csrf wtih annotation @CsrfValid on the Controller method.
    • IMPLICIT to enable Csrf autmaticially. No need @CsrfValid.
  2. Add annotation @CsrfValid on the Controller method.
    @POST
    @CsrfValid
    @ValidateOnExecution(type = ExecutableType.NONE)
    public Response save(@Valid @BeanParam TaskForm form) {
    }
    
  3. In the view, add hidden field to insert the Csrf value.
    <input type="hidden" name="${mvc.csrf.name}" value="${mvc.csrf.token}"/>
    
When you run the codes on Glassfish, in the view, the Csrf field looks like:
<input value="f3ca389f-efba-4f28-afe7-2a1e7231a238" name="X-Requested-By" type="hidden" />
Every request will generate a unique X-Requested-By value.
When the form is submitted, and it will be validated by MVC provider.

MvcContext

MvcContext interface includes the contextual data of MVC, such as context path, application path, etc. And also includes MVC security, such as Csrf and Encoders.
In the above section, we have used Csrf.
At the runtime environment, MvcContext is exposed by EL ${mvc} in the view.
  • ${mvc.contextPath} will get context path.
  • ${mvc.applicationPath} will get the application path declared in the Application class.
  • ${mvc.csrf.name} generate the Csrf token name.
  • ${mvc.csrf.token} generate the Csrf token value.
  • ${mvc.encoders.js(jsValue)} will escape the js scripts.
  • ${mvc.encoders.html(htmlValue)} will escape the html snippets.

Source Codes

  1. Clone the codes from my github.com account.
    https://github.com/hantsy/ee8-sandbox/
  2. Open the mvc project in NetBeans IDE.
  3. Run it on Glassfish.
  4. After it is deployed and runging on Glassfish application server, navigate http://localhost:8080/ee8-mvc/mvc/tasks in browser.

Getting started with Java EE8 MVC(6)-MVC and CDI

MVC and CDI

In before posts, we have already use @Inject to inject CDI beans into the Controller class. In fact, MVC embraced CDI internally.

RedirectScoped

There is a new CDI compatible scope named RedirectScoped introduced in MVC 1.0.
A bean annotated with @RedirectScoped annotation can be transfered between two requests, conceptly, it is similar with JSF Flash.
The AlertMessage is an example.
  1. Declare a bean with @RedirectScoped annotation.
    @RedirectScoped
    @Named("flashMessage")
    public class AlertMessage implements Serializable {}
    
  2. Inject it into a Controller class.
    @Inject
    AlertMessage flashMessage;
    
  3. Access the bean properties in the view via EL.
    <c:if test="${not empty flashMessage and not empty flashMessage.text}">
        <div class="alert alert-${flashMessage.type} alert-dismissible"
             role="alert">
            <button type="button" class="close" data-dismiss="alert"
                    aria-label="Close">
                <span aria-hidden="true"><![CDATA[&times;]]></span>
            </button>
            <p>${flashMessage.text}</p>
        </div>
    </c:if>
    
It is easy to understand the code snippets.
AlertMessage is a RedirectScoped bean, which means it is can be access in current request and the next request. It is named with CDI @Named which indicates it can be accessed in view via EL by name flashMessage.

MVC Event

MVC defined a series of CDI compatible events, with which you can track the MVC request lifecycle.
MVC provides a MvcEvent interface as the base of all events in MVC, there are several specific event implementations.
  • AfterControllerEvent
  • AfterProcessViewEvent
  • BeforeControllerEvent
  • BeforeProcessViewEvent
  • ControllerRedirectEvent
MVC itself will fire these events in the request processing progress, you can observe the events via CDI @Observes.
@ApplicationScoped
public class MvcEventListener {

    @Inject Logger LOGGER;

    private void onControllerMatched(@Observes BeforeControllerEvent event) {
        LOGGER.info(() -> "Controller matched for " + event.getUriInfo().getRequestUri());
    }

    private void onViewEngineSelected(@Observes BeforeProcessViewEvent event) {
        LOGGER.info(() -> "View engine: " + event.getEngine());
    }

    @PostConstruct
    private void init() {
        LOGGER.config(() -> this.getClass().getSimpleName() + " created");
    }
}

Source codes

  1. Clone the codes from my github.com account.
    https://github.com/hantsy/ee8-sandbox/
  2. Open the mvc project in NetBeans IDE.
  3. Run it on Glassfish.
  4. After it is deployed and runging on Glassfish application server, navigate http://localhost:8080/ee8-mvc/mvc/tasks in browser.

Getting started with Java EE8 MVC(5)-Page navigation

Page navigation

As introducing the feature pieces of the MVC specification, we have visited most of views in this demo. In fact, when you desgin a website, you should have a map for the page navigation among all views.

View navigation summary

  1. When the appliation starts up, it will display the task list view.
  2. User can create a new task by clicking the New Task button, the task creating view is displayed.
  3. When user input the task title and description, submit the form.
    • If the task is submitted successfully, return the main task list view. The new created task should be in the list.
    • If the task form data is invalid, show the validation errors.
  4. User can edit the new created task by click edit icon.
    • It will read the task from database and fill the existing data into form fields
    • The submission progress is similiar with the new task subflow described in above 3.
  5. User can update the task status(TODO to DOING, or DOING to DONE). When the status is changed successfully, return to the task list.
  6. User can delete the DONE task. When it is deleted, return to the task list.
//todo add a graph to display the navigations []()

View navigation path definition

We have used GET, POST, PUT, DELETE http methods to process basic CRUD operations, listed in the following table.
Navigation path HTTP method View page Descritption
/tasks GET tasks.jspx Task list view
/tasks/new GET add.jspx Display new task view
/tasks POST redirect:tasks Submit task form, save task, and redirect to task list view
/tasks/{id}/edit GET edit.jspx Display edit task view
/tasks/{id} PUT redirect:tasks Submit task form, update task, and redirect to task list view
/tasks/{id} DELETE redirect:tasks Submit task form, delete task, and redirect to task list view
/tasks/{id}/status PUT redirect:tasks Submit task form, update task status, and redirect to task list view
It is easy to understand. As you see, the navigation rules are very similar with the paths for RESTful APIs.

Display views

In the above paths, the task list view, new task view and edit task view can display the views directly via inputing the url path in browser locaiton bar. Others are POST submission, they will redirect to the task list view.
There are several approaches provided in MVC specification to navigate the view path.
  1. Add @View annotation to a void controller method.
  2. Return a Viewable object to a controller method.
  3. Return a Response entity which body content includes the view path.
    public Response someAction(){
         return Response.ok("redirect:tasks").build();
    }
    
  4. Return a String type value directly.
    public String someAction(){
         return "redirect:tasks";
    }   
    
The 1 and 2 are new added to the MVC specification, and the 3rd is reusing the existing JAXRS specification.

POST-REDIRECT-GET pattern

The new task form submission and edit task form submission follow the POST-REDIRECT-GET pattern.
  1. When the form is submitted successfully, a form POST request is sent to the server.
  2. After the server received the request, and processed it, it will REDIRECT to the target view.
  3. And perform a new GET request on the target view.

Source codes

  1. Clone the codes from my github.com account.
    https://github.com/hantsy/ee8-sandbox/
  2. Open the mvc project in NetBeans IDE.
  3. Run it on Glassfish.
  4. After it is deployed and runging on Glassfish application server, navigate http://localhost:8080/ee8-mvc/mvc/tasks in browser.

Getting started with Java EE8 MVC(4)-Handle PUT and DELETE request

Handle PUT and DELETE request

When you try to submit a PUT or DELETE http request via form submission, and you want to invoke the methods annotated with @PUT or @DELETE in the Controller to handle the request.
Unfortunately it does not work.
There is a simple solution which can help us to overcome this issue.
If you have used Spring MVC before and I think you could know there is a HiddenMethodFilter in the Spirng MVC to fix this issue. Java EE 8 MVC does not ship the similar Filter, we can create one.

Solution

  1. Create a Filter to convert the request method to the target HTTP method.
    @WebFilter(filterName = "HiddenHttpMethodFilter", urlPatterns = {"/*"}, dispatcherTypes = {DispatcherType.REQUEST})
    public class HiddenHttpMethodFilter implements Filter {
    
        @Inject
        Logger log;
    
        /**
        * Default method parameter: {@code _method}
        */
        public static final String DEFAULT_METHOD_PARAM = "_method";
    
        private String methodParam = DEFAULT_METHOD_PARAM;
    
        /**
        * Set the parameter name to look for HTTP methods.
        *
        * @see #DEFAULT_METHOD_PARAM
        */
        public void setMethodParam(String methodParam) {
            this.methodParam = methodParam;
        }
    
        @Override
        public void doFilter(ServletRequest req, ServletResponse res, FilterChain filterChain)
                throws ServletException, IOException {
            log.log(Level.INFO, "entering HttpHiddenFilter...");
            HttpServletRequest request = (HttpServletRequest) req;
            HttpServletResponse response = (HttpServletResponse) res;
    
            String paramValue = request.getParameter(this.methodParam);
            log.log(Level.INFO, "paramValue @" + paramValue);
            log.log(Level.INFO, "request method @" + request.getMethod());
    
            if ("POST".equals(request.getMethod()) && paramValue != null && paramValue.trim().length() > 0) {
                String method = paramValue.toUpperCase(Locale.ENGLISH);
                HttpServletRequest wrapper = new HttpMethodRequestWrapper(request, method);
                filterChain.doFilter(wrapper, response);
            } else {
                filterChain.doFilter(request, response);
            }
        }
    
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
    
        }
    
        @Override
        public void destroy() {
    
        }
    
        /**
        * Simple {@link HttpServletRequest} wrapper that returns the supplied
        * method for {@link HttpServletRequest#getMethod()}.
        */
        private static class HttpMethodRequestWrapper extends HttpServletRequestWrapper {
    
            private final String method;
    
            public HttpMethodRequestWrapper(HttpServletRequest request, String method) {
                super(request);
                this.method = method;
            }
    
            @Override
            public String getMethod() {
                return this.method;
            }
        }
    
    }
    
  2. Add a hidden field named _method in the form, which value is the target HTTP method.
    <form action="${markDoingUrl}" method="post">
        <input type="hidden" name="_method" value="PUT"><jsp:text /></input>
        <input type="hidden" name="status" value="DOING"><jsp:text /></input>
        <button type="submit" class="btn btn-sm btn-primary">
            <span class="glyphicon glyphicon-play" aria-hidden="true"><jsp:text /></span>
            START
        </button>
    </form>
    
  3. The @PUT method in the TaskController.
    @PUT
    @Path("{id}/status")
    public Response updateStatus(@PathParam(value = "id") Long id, @NotNull @FormParam(value = "status") String status) {
        log.log(Level.INFO, "updating status of the existed task@id:{0}, status:{1}", new Object[]{id, status});
    
        Task task = taskRepository.findById(id);
    
        task.setStatus(Task.Status.valueOf(status));
    
        taskRepository.update(task);
    
        flashMessage.notify(Type.info, "Task status was updated successfully!");
    
        return Response.ok("redirect:tasks").build();
    }
    

Source codes

  1. Clone the codes from my github.com account.
    https://github.com/hantsy/ee8-sandbox/
  2. Open the mvc project in NetBeans IDE.
  3. Run it on Glassfish.
  4. After it is deployed and runging on Glassfish application server, navigate http://localhost:8080/ee8-mvc/mvc/tasks in browser.

Getting started with Java EE 8 MVC(3)-Exception Handling and form validation

Exception Handling and form validation

When submitting a form, it should validate the form data before it is stored in the backend database.

Form binding and validation

Like Spring MVC, Struts, Stripes, JSF etc. MVC provides the similiar progress to process form submission.
  1. Gather user input form data.
  2. Convert form data to the target form bean. If there are some conversion failure, it is possbile to stop the progress and notify user.
  3. Bind the converted value to the form bean.
  4. Validate the form bean via Bean Validation. If there are some constraint voilations, it is possbile to stop the progress and notify user.
  5. Continue to process form.
MVC provides a BindingResult to gather all of the binding errors and constraint voilations in the request.

Handling form validation

Inject it in the controller class.
@Inject
private BindingResult validationResult;
In the controller method, add @Valid annotation on the methed parameters.
@ValidateOnExecution(type = ExecutableType.NONE)
public Response save(@Valid @BeanParam TaskForm form) {
    log.log(Level.INFO, "saving new task @{0}", form);

    if (validationResult.isFailed()) {
        AlertMessage alert = AlertMessage.danger("Validation voilations!");
        validationResult.getAllViolations()
                .stream()
                .forEach((ConstraintViolation t) -> {
                    alert.addError(t.getPropertyPath().toString(), "", t.getMessage());
                });
        models.put("errors", alert);
        return Response.status(BAD_REQUEST).entity("add.jspx").build();
    }
}
If the validation is failed, the isFailed method should return true.
You can iterate all voilations(validationResult.getAllViolations()) and gather the voilation details for each properties.
Then display the error messages in the JSP pages.
<c:if test="${not empty errors}">
     <c:forEach items="${errors.errors}" var="error">
    <div class="alert alert-danger alert-dismissible"
         role="alert">
        <button type="button" class="close" data-dismiss="alert"
                aria-label="Close">
            <span aria-hidden="true"><![CDATA[&times;]]></span>
        </button>
        <p>${error.field}: ${error.message}</p>
    </div>
    </c:forEach>
</c:if>

Handling exception

Like JAX-RS exception handling, you can handle exception via ExceptionMapper and display errors in the certain view.
Create a custom ExceptionMapper and add annotation @Provider.
@Provider
public class TaskNotFoundExceptionMapper implements ExceptionMapper<TaskNotFoundException>{

    @Inject Logger log;

    @Inject Models models;

    @Override
    public Response toResponse(TaskNotFoundException exception) {
        log.log(Level.INFO, "handling exception : TaskNotFoundException");
        models.put("error", exception.getMessage());
        return Response.status(Response.Status.NOT_FOUND).entity("error.jspx").build();
    }     
}
Different from JAX-RS, the entity value is the view that will be returned. In the error.jspx file, display the error model via EL directly.
<div class="container">
    <div class="page-header">
        <h1>Psst...something was wrong!</h1>
    </div>
    <div class="row">
        <div class="col-md-12">
            <p class="text-danger">${error}</p>
        </div>
    </div>
</div>
When the TaskNotFoundException is caught, it will display the erorr like the following.
mvc error

Source codes

  1. Clone the codes from my github.com account.
    https://github.com/hantsy/ee8-sandbox/
  2. Open the mvc project in NetBeans IDE.
  3. Run it on Glassfish.
  4. After it is deployed and runging on Glassfish application server, navigate http://localhost:8080/ee8-mvc/mvc/tasks in browser.

Getting started wtih Java EE 8 MVC(2)-Handling form submission

Handling form submission

In the tranditional MVC applications, the mostly-used HTML methods should GET and POST.
POST method is usually used in the form submission, aka form post.
In order to create a new task, you should follow the flow.
  1. In the task list view, click Add Task button to enter the creating task view.
  2. Fill the fields of the task form and submit. It will return to task list view.

Display the creating page

Firstly display the task creating page.
@GET
@Path("new")
public Viewable add() {
    log.log(Level.INFO, "add new task");

    return new Viewable("add.jspx");
}
Viewable is another approach to indicate a view path, I have motioned the @View annotation in the first post.
The add.jspx page code snippets.
<div class="row">
    <div class="col-md-12">
        <c:url var="formActionUrl" value="/mvc/tasks"/>
        <form id="form" name="form" role="form" class="form" action="${formActionUrl}" method="post">
            <div
                class="form-group">
                <label class="control-label" for="name">Task Name:</label>
                <input id="name" name="name" type="text" class="form-control"/>
                <div class="help-block">
                    <jsp:text/>
                </div>
            </div>
             <div
                class="form-group">
                <label class="control-label" for="description">Task Description:</label>
                <textarea id="description" name="description" class="form-control" rows="8"><jsp:text/></textarea>
                <div class="help-block">
                    <jsp:text/>
                </div>
            </div>

            <div class="form-group">
                <button id="submitTask" type="submit" class="btn btn-lg btn-primary">Add Task</button>
            </div>
        </form>
    </div>
</div>
Unlike other MVC frameworks, such as Spring MVC, MVC 1.0 does not provides specific taglib for form field wrapping. Most of the codes are pure HTML codes and some addtional standard JSTL taglib in JSP specification.
When the Add Task button is clicked, the form is submitted via POST method to the backend controller.

Handling form submission

Let's have a look at the controller to process the form submission.
@POST
public Response save(@Valid @BeanParam TaskForm form) {
    log.log(Level.INFO, "saving new task @{0}", form);

    if (validationResult.isFailed()) {
        AlertMessage alert = AlertMessage.danger("Validation voilations!");
        validationResult.getAllViolations()
                .stream()
                .forEach((ConstraintViolation t) -> {
                    alert.addError(t.getPropertyPath().toString(), "", t.getMessage());
                });
        models.put("errors", alert);
        return Response.status(BAD_REQUEST).entity("add.jsp").build();
    }

    Task task = new Task();
    task.setName(form.getName());
    task.setDescription(form.getDescription());

    taskRepository.save(task);

    flashMessage.notify(Type.success, "Task was created successfully!");

    return Response.ok("redirect:tasks").build();
}
You could have notice there is @BeanParam annotation with TaskForm bean.
Let's dig in the TaskForm code.
@RequestScoped
public class TaskForm implements Serializable {

    private static final long serialVersionUID = 1L;

    @NotNull
    @FormParam("name")
    private String name;

    @NotNull
    @FormParam("description")
    private String description;

    //setters and getters are omitted.
    //equals and hashcode methods are omitted.

}   
In the TaskForm, there are two fields defined, name and description, and they are annotated with a @FormParam annotation, it means the fields in the input form will be bound to these fields by name.
When the form is submitted, the value of input field named name will be bound to the name field of the TaskForm instance. The save method will read the form data and save it into database, and return to the task list view.

Source codes

  1. Clone the codes from my github.com account.
    https://github.com/hantsy/ee8-sandbox/
  2. Open the mvc project in NetBeans IDE.
  3. Run it on Glassfish 4.1.1.
  4. After it is deployed and runging on Glassfish application server, navigate http://localhost:8080/ee8-mvc/mvc/tasks in browser.
  5. Click the Add Task button and try to create a new task.
    https://github.com/hantsy/ee8-sandbox/wiki/mvc-new-task.png

Getting started with Java EE 8 MVC

Getting started with Java EE 8 MVC

MVC is a new specification introduced in the upcoming Java EE 8.
It is based on the existing JAXRS.
At the moment I wrote down these posts, most of Java EE 8 specficitaions are still in the early disscussion stage, and MVC 1.0 is also not finalized, maybe some changes are included in future. I will update the Wiki pages and codes aglined with final Java EE 8 specficitaions when it is released.
I will use the latest Java 8, Glassfish 4.1.1, and NetBeans IDE for these posts.

Prequisition

  • Oracle JDK 8 or OpenJDK 8
    Oracle Java 8 is required, go to Oracle Java website to download it and install into your system.
    Optionally, you can set JAVA_HOME environment variable and add <JDK installation dir>/bin in your PATH environment variable.
  • The latest Apache Maven
    Download the latest Apache Maven from http://maven.apache.org, and uncompress it into your local system.
    Optionally, you can set M2_HOME environment varible, and also do not forget to append <Maven Installation dir>/bin your PATH environment variable.
  • NetBeans IDE
    Download the latest NetBeans IDE from http://www.netbeans.org, and installed it into your local disk.
  • Glassfish Server
    Download the latest Glassfish from Glassfish official website. Currently the latest GA version is 4.1.1. Extracted it into your local disk.
NOTE: You can download the JDK and NetBeans bundle from Oracle website instead of installing JDK and NetBeans IDE respectively.
After you installed all of these, start NetBeans IDE, add Glassfish into Service tab in NetBeans IDE.

The Sample application

To demonstrate the basic usage of MVC spection, I will port the task board sample which I have implemented in the Spring4 Sandbox to demonstrate Spring MVC.
Simply, it includes the following features.
  1. List tasks by status, display the tasks in 3 columns(like a simple kanban).
  2. Create a new task.
  3. Edit and update task.
  4. Update task stauts(move to different columns in the task list).
  5. Delete task if it is done.

Create a project skeleton

First of first, you should create a simple project skeleton as start point.
If you are using NetBeans IDE, it is easy to create a Maven based Java EE 7 web project in IDE directly, and add the registered Glassfish as runtime server.
It should include Java EE 7 web artifact as the dependency.
<dependency>
    <groupId>javax</groupId>
    <artifactId>javaee-web-api</artifactId>
    <version>7.0</version>
    <scope>provided</scope>
</dependency>   
Add additional MVC api and the implemnetation dependencies.
<dependency>
    <groupId>javax.mvc</groupId>
    <artifactId>javax.mvc-api</artifactId>
    <version>1.0-edr2</version>
</dependency>
<dependency>
    <groupId>org.glassfish.ozark</groupId>
    <artifactId>ozark</artifactId>
    <version>1.0.0-m02</version>
    <scope>runtime</scope>
</dependency>
ozark is the default reference implemnetation of MVC 1.0 specificaiton, which will shipped with Glassfish 5. Currently it is still under active development, what we are using here may be changed in the future.
Here we used Glassfish 4.1.1 as target runtime, so you should include them in the deployment package.
When Glassfish 5 is ready for Java EE 8, these two dependencies can be excluded and removed from POM.

Declare the MVC application

MVC does not reinvent the wheel, it reuses the effort of JAXRS specificaiton.
Similar with activiating JAXRS application, You can declare a custom Application as our MVC application entry.
@ApplicationPath("mvc")
public class MvcConfig extends Application {

    @Override
    public Set<Class<?>> getClasses() {
        return Collections.singleton(TaskController.class);
    }
}
TaskController is a controller, it acts as the C in MVC pattern.

Controller

MVC uses a @Controller annotation to declare a JAXES resource as Controller.
@Path("tasks")
@Controller
public class TaskController {

    @GET
    @View("tasks.jspx")
    public void allTasks() {
        log.log(Level.INFO, "fetching all tasks");

        List<Task> todotasks = taskRepository.findByStatus(Task.Status.TODO);
        List<Task> doingtasks = taskRepository.findByStatus(Task.Status.DOING);
        List<Task> donetasks = taskRepository.findByStatus(Task.Status.DONE);

        log.log(Level.INFO, "got all tasks: todotasks@{0}, doingtasks@{1}, donetasks@{2}", new Object[]{todotasks.size(), doingtasks.size(), donetasks.size()});

        models.put("todotasks", todotasks);
        models.put("doingtasks", doingtasks);
        models.put("donetasks", donetasks);

    }
}
@View annotation indicates the view(eg. JSP pages) a void method will return.

Model

Models is a contrainer to hold the model datas that will be transfered to the view.

View

Have a look at the tasks.jspx file, I just copied some code snippets here, please checkout the source codes for details.
<div class="col-md-4 col-xs-12">
    <div class="panel panel-default">
        <!-- Default panel contents -->
        <div class="panel-heading">
            <span class="glyphicon glyphicon-list-alt" aria-hidden="true"><jsp:text /></span>
            TODO
        </div>
        <div class="panel-body">
            <p>Tasks newly added in the backlog.</p>
        </div>

        <!-- List group -->
        <c:if test="${not empty todotasks}">
            <ul id="todotasks" class="list-group">
                <c:forEach var="task" begin="0" items="${todotasks}">
                    <li class="list-group-item">
                        <h4>
                            <span>#${task.id} ${task.name}</span> <span class="pull-right">
                                <c:url var="taskUrl" value="tasks/${task.id}" /> <c:url
                                    var="taskEditUrl" value="tasks/${task.id}/edit" /> <a
                                    href="${taskUrl}"> <span class="glyphicon glyphicon-file"
                                                         aria-hidden="true"><jsp:text /></span>
                                </a> <a href="${taskEditUrl}"> <span
                                        class="glyphicon glyphicon-pencil" aria-hidden="true"><jsp:text /></span>
                                </a>
                            </span>
                        </h4>
                        <p>${task.description}</p>
                        <p>
                            <c:url var="markDoingUrl"
                                   value="/mvc/tasks/${task.id}/status" />
                        <form action="${markDoingUrl}" method="post">
                            <input type="hidden" name="_method" value="PUT"><jsp:text /></input>
                            <input type="hidden" name="status" value="DOING"><jsp:text /></input>
                            <button type="submit" class="btn btn-sm btn-primary">
                                <span class="glyphicon glyphicon-play" aria-hidden="true"><jsp:text /></span>
                                START
                            </button>
                        </form>
                        </p>
                    </li>
                </c:forEach>
            </ul>
        </c:if>
    </div>
</div>
No surprise, just pure JSP files, I used the JSP xml form in this sample.
By default the views should be put in the /WEB-INF/views folder in projects.
In this example, when you send a GET request to /ee8-mvc/mvc/tasks, the allTasks() method will handle this request, then find data(tasks by status here) from database, and put the query results into a Models container, in the view pages the model data can be accessed via el directly.
The path(/ee8-mvc/mvc/tasks) is the combination of context path, mvc application path, and controller path.

Source codes

  1. Clone the codes from my github.com account.
    https://github.com/hantsy/ee8-sandbox/
  2. Open the mvc project in NetBeans IDE.
  3. Run it on Glassfish server.
  4. After it is deployed and running on the Glassfish application server, navigate http://localhost:8080/ee8-mvc/mvc/tasks in your favorite browser.
    https://github.com/hantsy/ee8-sandbox/wiki/mvc-tasks.png

星期四, 三月 05, 2015

Spring JCache support


Spring JCache support

Spring 4 provides seamless JCache integration.

Enable Caching

In this example, use EhCache as JCache specification provider.
Add @EnableCaching(mode=AdviceMode.ASPECTJ) annotation on the configuration class if you are using Java based configuration.
@Configuration
...
@EnableCaching(mode=AdviceMode.ASPECTJ)
public class JpaConfig {...}
Then, specify a CacheManager provider in your configuration.
@Override
@Bean
public CacheManager cacheManager() {
 JCacheCacheManager cacheManager = new JCacheCacheManager();
 cacheManager.setCacheManager(new JCacheManager(
   new JCacheCachingProvider(), ehcache(), null, null));
 return cacheManager;
}

private net.sf.ehcache.CacheManager ehcache() {
 return new net.sf.ehcache.CacheManager();
}

@Override
@Bean
public KeyGenerator keyGenerator() {
 return new SimpleKeyGenerator();
}
Do not forget to add the ehcache-core, jcache, cache-api dependencies in your Maven pom.xml file.
<dependency>
 <groupId>net.sf.ehcache</groupId>
 <artifactId>ehcache-core</artifactId>
</dependency>

<dependency>
 <groupId>org.ehcache</groupId>
 <artifactId>jcache</artifactId>

</dependency>
<dependency>
 <groupId>javax.cache</groupId>
 <artifactId>cache-api</artifactId>
</dependency>

Use JCache API

Like Spring core, JCache APIs provides a series of annotations for cache operations.
@Repository
public class JpaConferenceRepositoryImpl implements ConferenceRepository {
 private static final Logger log = LoggerFactory
   .getLogger(JpaConferenceRepositoryImpl.class);

 @PersistenceContext
 private EntityManager entityManager;

 @Override
 @CacheResult(cacheName = "conference")
 public Conference findById(Long id) {
  return (Conference) entityManager.find(Conference.class, id);

 }

 @Override
 @CachePut(cacheName = "conference" )
 public Conference save(final Conference conference) {
  if (conference.isNew()) {
   entityManager.persist(conference);
   entityManager.flush();
   return conference;
  } else {
   Conference conf = entityManager.merge(conference);
   entityManager.flush();
   return conf;
  }
 }

 
 @Override
 @CacheRemove(cacheName = "conference" )
 public void delete(final Long id) {
  entityManager.remove(entityManager.find(Conference.class, id));
  entityManager.flush();
 }
 
 @Override
 @CacheRemove(cacheName = "conference" )
 public void delete(final Conference conf) {
  entityManager.remove(entityManager.merge(conf));
  entityManager.flush();
 }

 @Override
 @CacheRemoveAll(cacheName = "conference")
 public void deleteAll() {
  List<Conference> all = entityManager.createQuery("from Conference",
    Conference.class).getResultList();
  for (Conference c : all) {
   delete(c);
  }
  entityManager.flush();
 }

 @Override
 @CacheResult(cacheName = "conference")
 public Conference findBySlug(String slug) {
  List<Conference> all = entityManager
    .createQuery("from Conference where slug=:slug",
      Conference.class).setParameter("slug", slug)
    .getResultList();
  if (!all.isEmpty()) {
   return all.get(0); 
  }

  return null;
 }
}
Spring JCache support can reuse the Spring Caching configuration, but use JCache APIs in codes..

Code sample

Check out the sample codes.
And explore the spring-jcache folder.

星期三, 三月 04, 2015

Caching with Infinispan(Remote standalone server)


Spring Caching with Infinispan(Remote standalone server)

Infinispan Spring Integration provides SpringRemoteCacheManager to connect remote infinispan server.

Get JBoss Infinispan

Download a copy of JBoss Infinispan.
Install it into your local system, and make sure it is running.

Enable Caching

Add @EnableCaching(mode=AdviceMode.ASPECTJ) annotation on the configuration class if you are using Java based configuration.
@Configuration
...
@EnableCaching(mode=AdviceMode.ASPECTJ)
public class JpaConfig {...}
Then, specify a CacheManager provider in your configuration. There is a SpringRemoteCacheManager provided by Infinispan Spring integration.
@Override
@Bean
public CacheManager cacheManager() {

 return new SpringRemoteCacheManager(
   new RemoteCacheManager(   
    new ConfigurationBuilder()
     .addServer()
     .host("localhost")
     .build()
    )
   );
}

@Override
@Bean
public KeyGenerator keyGenerator() {
 return new SimpleKeyGenerator();
}
Do not forget to add the infinispan-spring dependency in your Maven pom.xml file.
<dependency>
 <groupId>org.infinispan</groupId>
 <artifactId>infinispan-spring</artifactId>
</dependency>
<dependency>
 <groupId>org.infinispan</groupId>
 <artifactId>infinispan-client-hotrod</artifactId>
</dependency>

Code sample

Check out the sample codes.
And explore the spring-cache-infinispan-remote folder.

Caching with Infinispan


Spring Caching with Infinispan

JBoss Infinispan is popular distributed caching solution, it is the base of RedHad Data Grid product.
In the real world projects, Infinispan can work in several ways.
  • Run as a standalone server.
  • Run as JBoss service.
  • Embedded.
Infinispan also provides Spring integration, and provides two different CacheManager implementations for embedded way and standalone servers.
This post focus on the embedded way, the next post will contain the standalone environment configuration.

Enable Caching

Add @EnableCaching(mode=AdviceMode.ASPECTJ) annotation on the configuration class if you are using Java based configuration.
@Configuration
...
@EnableCaching(mode=AdviceMode.ASPECTJ)
public class JpaConfig {...}
Then, specify a CacheManager provider in your configuration. There is a SpringEmbeddedCacheManager provided by Infinispan Spring integration.
@Override
@Bean
public CacheManager cacheManager() {

 return new SpringEmbeddedCacheManager(
   new DefaultCacheManager(
    new ConfigurationBuilder()
     .eviction()
      .maxEntries(20000)
      .strategy(EvictionStrategy.LIRS)
     .expiration()
      .wakeUpInterval(5000L)
      .maxIdle(120000L)
       .build()
    )
   );
}

@Override
@Bean
public KeyGenerator keyGenerator() {
 return new SimpleKeyGenerator();
}
Do not forget to add the infinispan-spring dependency in your Maven pom.xml file.
<dependency>
 <groupId>org.infinispan</groupId>
 <artifactId>infinispan-spring</artifactId>
</dependency>

Code sample

Check out the sample codes.
And explore the spring-cache-infinispan-embedded folder.