Java EE kickoff app

As a basis for many different kinds of web applications, the JDevelopment team has released an open source Java EE kickoff app.

This app is a skeleton application that demonstrates a couple of technologies:

  • JSF 2.1 based views
  • CDI based backing beans
  • JASPIC based authentication module
  • EJB based services
  • Bean Validation constraints
  • JPA based models
  • Java EE 6 standard data source
  • Embedded H2 database

The concrete functionality that’s implemented:

  • Logging in with username/password
  • Remember-me for login
  • Login-to-continue for protected pages
  • Basic user registration with password validation
  • Authentication via app controlled Service and domain entity
  • Extra app controlled layer above roles: groups
  • Admin CRUD page for editing and deleting users

Authentication is done via a reusable JASPIC SAM (JSR 196), that’s being developed in a separate project: OmniSecurity which is a sub-project of OmniFaces, which is also demonstrated in the kickoff app (e.g. for HTML5 placeholders).

The Java code

A n overview of the code and the main packages is given below:

org.example.kickoff.auth

This package contains the KickoffAuthenticator class, which loads a user from the embedded database. This class implements the UsernamePasswordAuthenticator interface and is auto-discovered (via CDI) by the JASPIC authentication module.

An excerpt of this class:

@SessionScoped
public class KickoffAuthenticator implements UsernamePasswordAuthenticator {
 
    @EJB
    private UserService userService;
 
    private User user;
    private List applicationRoles;
 
    @Override
    public boolean authenticate(String name, String password) {
        try {
            user = userService.getUserByCredentials(name, password);
        }
        catch (InvalidCredentialsException e) {
            return false;
        }
        applicationRoles = user.getRoles();
 
        return true;
    }
}

org.example.kickoff.business

This package contains the Services and associated exception types. It now mainly contains class UserService, which is an EJB bean that uses JPA to handle Users.

An excerpt of this class:

@Stateless
public class UserService {
 
    @PersistenceContext
    private EntityManager entityManager;
 
    public void registerUser(User user, String password) {
        if (getByEmail(user.getEmail()) != null) {
            throw new ValidationException("Email address is already registered");
        }
 
        setCredentials(user, password);
 
        if (!user.getGroups().contains(USERS)) {
            user.getGroups().add(USERS);
        }
 
        entityManager.persist(user);
    }
}

org.example.kickoff.model

This package contains all domain models (entities) that the application uses. Currently this is mainly contains the User entity, and several security related types like Group, Role and Credentials. There’s also a BaseEntity, which provides a universal hashCode and equals implementation, which are based on the unique persistent Id that all persisted JPA entities have. An individual entity can of course provide a more specific implementation whenever that’s needed.

The base entity looks as follows:

public abstract class BaseEntity {
 
    public abstract T getId();
 
    public abstract void setId(T id);
 
    @Override
    public int hashCode() {
        return (getId() != null)
            ? (getClass().hashCode() + getId().hashCode())
            : super.hashCode();
    }
 
    @Override
    public boolean equals(Object other) {
        return (other != null && getClass() == other.getClass() && getId() != null)
            ? getId().equals(((BaseEntity) other).getId())
            : (other == this);
    }
}

An excerpt of the User entity:

@Entity
public class User extends BaseEntity {
 
    @Id
    @GeneratedValue(strategy = IDENTITY)
    private Long id;
 
    @NotNull
    @Email
    @Column(nullable = false, unique = true)
    private String email;
 
    @OneToOne(mappedBy = "user", fetch = LAZY, cascade = ALL)
    private Credentials credentials;
 
    private String loginToken; // TODO: make collection later
 
    @ElementCollection(targetClass = Group.class, fetch = EAGER)
    @Enumerated(STRING)
    @CollectionTable(name = "user_group")
    @Column(name = "group_name")
    private List groups = new ArrayList();
}

org.example.kickoff.validator

This package contains validation logic, mostly intended to be used with Bean Validation. It currently contains a basic email validator, for the @Email annotation that was shown above:

public class EmailValidator implements ConstraintValidator {
 
    @Override
    public void initialize(Email constraintAnnotation) {
    }
 
    @Override
    public boolean isValid(String email, ConstraintValidatorContext context) {
        try {
            new InternetAddress(email).validate();
        }
        catch (AddressException e) {
            return false;
        }
        return true;
    }
}

org.example.kickoff.view

This package contains the view specific artifacts, which are mostly backing beans (which back a single view, or dialog on a view) and more general managed beans (which can be used from multiple views).

An excerpt from a bean called LoginBean that’s used by the index and the “login-to-continue” views to login:

@Named
@RequestScoped
public class LoginBean {
 
    private String loginUserName;
    private String loginPassword;
    private boolean rememberMe;
 
    public void login() throws IOException, ServletException {
 
        boolean authenticated = Jaspic.authenticate(loginUserName, loginPassword, rememberMe);
 
        if (!authenticated) {
            addGlobalError("Login failed");
        }
    }
 
    public void logout() throws ServletException {
        Jaspic.logout();
    }
}

The Facelets views

The Facelets views (pages) are located in the src/main/webapp folder. Public views are directly put into the root, while other pages are organized per role. There is a single page for users with role admin now, which is thus located in /admin.

Includes, useful for breaking up bigger pages into smaller chunks, have been put into WEB-INF/includes. Folders within this includes folder correspond to the top-level views. E.g. WEB-INF/includes/index contains all includes for the index page.

The main index page looks as follows:

<ui:composition template="/WEB-INF/templates/layout.xhtml"
    xmlns="http://www.w3.org/1999/xhtml"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:ui="http://java.sun.com/jsf/facelets"
>
    <ui:define name="title">Java EE kickoff app</ui:define>
 
    <ui:define name="content">
 
        <h:panelGroup rendered="#{empty request.userPrincipal}">
            <ui:include src="/WEB-INF/includes/index/public.xhtml"/>
        </h:panelGroup>
 
        <h:panelGroup rendered="#{!empty request.userPrincipal}">
            <ui:include src="/WEB-INF/includes/index/loggedin.xhtml"/>
        </h:panelGroup>
 
    </ui:define>
 
</ui:composition>

What’s next?

We intend to periodically update the project with common things that we found to be useful for a kickoff app. Some things that are on the road map is i18n support, a mechanism for system (app) settings and something for storing user preferences.

Download and Demo

The project itself is hosted on GitHub: github.com/javaeekickoff/java-ee-kickoff-app.

There is a live demo at OpenShift: javaee6-kickoffapp.rhcloud.com

See also

css.php best counter