Jsf Service Layer

JSF Service Layer

The service layer (the business model) should be designed around the main entity (the data model). E.g. UserService for User, ProductService for Product, OrderService for Order, etc. You should absolutely not have one huge service class or so. That's extreme tight coupling.

As to the service layer API itself, Java EE 6 offers EJB 3.1 as service layer API. In the dark J2EE ages, long time ago when EJB 2.0 was terrible to develop with, Spring was more often been used as service layer API. Some still use it nowadays, but since Java EE 6 has incorporated all the nice lessons learnt from Spring, it has become superfluous. Note that EJB (and JPA) is not available in barebones servletcontainers such as Tomcat. You'd need to install for example OpenEJB on top of it (or just upgrade to TomEE).

Regardless of the service layer API choice, best would be to keep your JSF backing bean (action)listener methods as slick as possible by performing the business job entirely in the service layer. Note that the service layer should by itself not have any JSF dependencies. So any (in)direct imports of javax.faces.* in the service layer code indicates bad design. You should keep the particular code lines in the backing bean (it's usually code which adds a faces message depending on the service call result). This way the service layer is reuseable for other front ends, such as JAX-RS or even plain servlets.

You should understand that the main advantage of the service layer in a Java EE application is the availability of container managed transactions. One service method call on a @Stateless EJB counts effectively as a single DB transaction. So if an exception occurs during one of any DAO operations using @PersistenceContext EntityManager which is invoked by the service method call, then a complete rollback will be triggered. This way you end up with a clean DB state instead of a dirty DB state because for example the first DB manipulation query succeeded, but the second not.

See also:

  • Creating master-detail pages for entities, how to link them and which bean scope to choose
  • When is it necessary or convenient to use Spring or EJB3 or all of them together?
  • JSF Controller, Service and DAO

JSF Controller, Service and DAO

Is this the correct way of doing things?

Apart from performing business logic the inefficient way in a managed bean getter method, and using a too broad managed bean scope, it looks okay. If you move the service call from the getter method to a @PostConstruct method and use either @RequestScoped or @ViewScoped instead of @SessionScoped, it will look better.

See also:

  • Why JSF calls getters multiple times
  • How to choose the right bean scope?


Is my terminology right?

It's okay. As long as you're consistent with it and the code is readable in a sensible way. Only your way of naming classes and variables is somewhat awkward (illogical and/or duplication). For instance, I personally would use users instead of userList, and use var="user" instead of var="u", and use id and name instead of userId and userName. Also, a "UserListService" sounds like it can only deal with lists of users instead of users in general. I'd rather use "UserService" so you can also use it for creating, updating and deleting users.

See also:

  • JSF managed bean naming conventions


The "service" feels more like a DAO?

It isn't exactly a DAO. Basically, JPA is the real DAO here. Previously, when JPA didn't exist, everyone homegrew DAO interfaces so that the service methods can keep using them even when the underlying implementation ("plain old" JDBC, or "good old" Hibernate, etc) changes. The real task of a service method is transparently managing transactions. This isn't the responsibility of the DAO.

See also:

  • I found JPA, or alike, don't encourage DAO pattern
  • DAO and JDBC relation?
  • When is it necessary or convenient to use Spring or EJB3 or all of them together?


And the controller feels like it's doing some of the job of the service.

I can imagine that it does that in this relatively simple setup. However, the controller is in fact part of the frontend not the backend. The service is part of the backend which should be designed in such way that it's reusable across all different frontends, such as JSF, JAX-RS, "plain" JSP+Servlet, even Swing, etc. Moreover, the frontend-specific controller (also called "backing bean" or "presenter") allows you to deal in a frontend-specific way with success and/or exceptional outcomes, such as in JSF's case displaying a faces message in case of an exception thrown from a service.

See also:

  • JSF Service Layer
  • What components are MVC in JSF MVC framework?

All in all, the correct approach would be like below:

<h:dataTable value="#{userBacking.users}" var="user">
<h:column>#{user.id}</h:column>
<h:column>#{user.name}</h:column>
</h:dataTable>
@Named
@RequestScoped // Use @ViewScoped once you bring in ajax (e.g. CRUD)
public class UserBacking {

private List<User> users;

@EJB
private UserService userService;

@PostConstruct
public void init() {
users = userService.listAll();
}

public List<User> getUsers() {
return users;
}

}
@Stateless
public class UserService {

@PersistenceContext
private EntityManager em;

public List<User> listAll() {
return em.createQuery("SELECT u FROM User u", User.class).getResultList();
}

}

You can find here a real world kickoff project here utilizing the canonical Java EE / JSF / CDI / EJB / JPA practices: Java EE kickoff app.

See also:

  • Creating master-detail pages for entities, how to link them and which bean scope to choose
  • Passing a JSF2 managed pojo bean into EJB or putting what is required into a transfer object
  • Filter do not initialize EntityManager
  • javax.persistence.TransactionRequiredException in small facelet application

Contradictory explanations of MVC in JSF

I'm mostly concerned about Managed beans here. Are they M or C?

People consider them M when they look like this:

@ManagedBean
public class Bean {

private String username; // +getter+setter
private String password; // +getter+setter

@Resource
private DataSource dataSource;

public void login() {
try (
Connection connection = dataSource.getConnection();
PreparedStatement statement = connection.prepareStatement("SELECT * FROM User WHERE username = ? AND password = MD5(?)");
) {
statement.setString(1, username);
statement.setString(2, password);

try (ResultSet resultSet = statement.executeQuery()) {
if (resultSet.next()) {
// Login.
}
}
}
}

// ...
}

But people consider them C when they look like this:

@ManagedBean
public class Bean {

private User user // +getter

@EJB
private UserService userService;

public void login() {
if (userService.find(user) != null) {
// Login.
}
}

// ...
}

This is also mentioned in the very same MVC answer you found:

Note that some starters and even some —very basic— tutorials mingle/copy/flatten the entity's properties in the managed bean, which would effectively make the controller a model. Needless to say that this is poor design (i.e. not a clean MVC design).

See also:

  • Our JSF wiki page

How to isolate Spring service/persistence layer from JSF view layer

JSF is a server side technology, just do the proper mapping for the faces servlet against the xhtml files. The end user then won't be able to see the source content of any xhtml file, cause all his requests against .xhtml sources will be driven through the faces servlet and thus converted to plain HTML-CSS-javascript combination.

If JSF is your view layer framework (being it the only servlet-mapping declared) no end user will be able to access your service methods, at least not without skipping JSF itself. It will take care of security and basic request validation for you.

Anyway, I don't recommend you accessing Spring beans through xhtml. Just use a managed bean for that, for architectural matters. If you have a look at the Integrating with other web frameworks part of the documentation, you'll find how to grab a Spring bean from a JSF managed bean using the FacesContextUtils class.

Letting the presentation layer (JSF) handle business exceptions from service layer (EJB)

Create a custom service layer specific runtime exception which is annotated with @ApplicationException with rollback=true.

@ApplicationException(rollback=true)
public abstract class ServiceException extends RuntimeException {}

Create some concrete subclasses for general business exceptions, such as constraint violation, required entity, and of course optimistic lock.

public class DuplicateEntityException extends ServiceException {}
public class EntityNotFoundException extends ServiceException {}
public class EntityAlreadyModifiedException extends ServiceException {}

Some of them can be thrown directly.

public void register(User user) {
if (findByEmail(user.getEmail()) != null) {
throw new DuplicateEntityException();
}

// ...
}
public void addToOrder(OrderItem item, Long orderId) {
Order order = orderService.getById(orderId);

if (order == null) {
throw new EntityNotFoundException();
}

// ...
}

Some of them need a global interceptor.

@Interceptor
public class ExceptionInterceptor implements Serializable {

@AroundInvoke
public Object handle(InvocationContext context) throws Exception {
try {
return context.proceed();
}
catch (javax.persistence.EntityNotFoundException e) { // Can be thrown by Query#getSingleResult().
throw new EntityNotFoundException(e);
}
catch (OptimisticLockException e) {
throw new EntityAlreadyModifiedException(e);
}
}

}

Which is registered as default interceptor (on all EJBs) as below in ejb-jar.xml.

<interceptors>
<interceptor>
<interceptor-class>com.example.service.ExceptionInterceptor</interceptor-class>
</interceptor>
</interceptors>
<assembly-descriptor>
<interceptor-binding>
<ejb-name>*</ejb-name>
<interceptor-class>com.example.service.ExceptionInterceptor</interceptor-class>
</interceptor-binding>
</assembly-descriptor>

As a general hint, in JSF you can also have a global exception handler which just adds a faces message. When starting with this kickoff example, you could do something like this in YourExceptionHandler#handle() method:

if (exception instanceof EntityAlreadyModifiedException) { // Unwrap if necessary.
// Add FATAL faces message and return.
}
else {
// Continue as usual.
}

Logging logged-in user ID from JSF session in EJB service layer

When using homegrown authentication with manually putting the logged-in user in the HTTP session, you'd really have to pass it along yourself as a method argument as the service layer is supposed to be unaware of any frontend specifics such as the HTTP session (i.e., it's forbidden to import/use anything from javax.faces.*, javax.servlet.*, javax.ws.*, etc in the service layer).

When using container managed authentication via j_security_check or request.login(), it's available by EJBContext#getCallerPrincipal(). The EJBContext is in turn just injectable via @Resource. Here's an usage example in a logging interceptor.

@Resource
private EJBContext ejbContext; // You can also inject SessionContext.

@AroundInvoke
public Object log(InvocationContext invocationContext) {
String username = ejbContext.getCallerPrincipal().getName();
// ...
}

Note that it's never null and defaults to "anonymous" when non-logged-in.

Additional advantage of container managed authentication is that you can put security restriction annotations like @RolesAllowed on service methods. How to use container managed authentication is in turn however a story apart. Start here: How to handle authentication/authorization with users in a database?

Handling service layer exception in Java EE frontend method

It's because you threw a RuntimeException from an EJB.

When such RuntimeException is not annotated with @ApplicationException, then the EJB container will wrap it in an javax.ejb.EJBException and rethrow it. This is done so because runtime exceptions are usually only used to indicate bugs in code logic, i.e. programmer's mistakes and not enduser's mistakes. You know, NullPointerException, IllegalArgumentException, IndexOutOfBoundsException, NumberFormatException and friends. This allows the EJB client to have one catch-all point for such runtime exceptions, like catch (EJBException e) { There's a bug in the service layer or in the way how we are using it! }

If you had tried catch (Exception e) and inspected the actual exception, then you'd have noticed that.

Fix your BusinessException class accordingly to add that annotation, it will then be recognized as a real application exception and not be wrapped in an EJBException:

@ApplicationException(rollback=true)
public class BusinessException extends RuntimeException {
// ...
}

Do note that in case you throw an non-RuntimeException, then you still need to keep the annotation on that, explicitly with rollback=true, because by default it wouldn't perform a rollback, on the contrary to a RuntimeException without the annotation.

@ApplicationException(rollback=true)
public class BusinessException extends Exception {
// ...
}

Summarized:

  1. RuntimeException thrown from transactional EJB method will perform full rollback, but exception will be wrapped in EJBException.
  2. RuntimeException with @ApplicationException from transactional EJB method will only perform full rollback when rollback=true is explicitly set.
  3. Exception from transactional EJB method will not perform full rollback.
  4. Exception with @ApplicationException from transactional EJB method will only perform full rollback when rollback=true is explicitly set.

Note that @ApplicationException is inherited over all subclasses of the custom exception, so you don't need to repeat it over all of them. Best would be to have it as an abstract class. See also the examples in the related question linked below.

See also:

  • Letting the presentation layer (JSF) handle business exceptions from service layer (EJB)

In jsf, how should a bean communicate with a regular java class (aka the business logic)

Modern enterprise applications usually use the dependency injection pattern throughout the entire application, not just the presentation layer. So you'd have a data access layer contributing beans such as EntityManager. These are injected into business services, that form the business service layer. The business services, in turn, are injected into your JSF backing beans. What dependency injection container is best is a matter of debate, you can also mix them.

In the Java EE 6 standard (at least how I read it), EJB acts as dependency injection container for the data access and the business service layer, and CDI as dependency injection container for the presentation layer (that's why you can inject EJBs in CDI beans). Others want to replace EJB and use CDI through all layers. Yet others still smart from the hurt J2EE inflicted, and use Spring as dependency injection container.

To give a little code, you might do:

@Named
@SessionScoped
public class UserBean {

@Inject UserService userService;

User user;

public void save() {
userService.create(user);
}

}

@Stateless
public class UserService {
public void create(User user) { ... }
}

Can a Middleware class be used as a Service layer?

To be honest you can use Service/Repo Layers in PHP as well.

So what happens is

  1. Controller passes the inputs to the service and service decides what action is to be done.
  2. The Service Layer then calls the repo for receiving entries from database wherever necessary and perform all the business logic.
  3. The Repo calls the model and data from the model is returned.
  4. The Model only keeps Model specific data (like relations, appended attributes, casts array etc etc...)

To follow this approach, something like this can be done.

Controller

use App\Services\PostService;

class PostController
{
public function __construct()
{
$this->postService = new PostService;
}

public function show($id)
{
$viewData = $this->postService->getPostData($id);

return view('posts.show', $viewData);
}
}

Service Layer

use App\Repositories\PostRepository;
use App\Repositories\CommentRepository;

class PostService
{
public function __construct()
{
$this->postRepo = new PostRepository;

$this->commentRepo = new CommentRepository;
}

public function getPostData($id)
{
$post = $this->postRepo->get($id);

$recentComments = $this->commentsRepo->getRecentComments();

return collect(compact('post', 'recentComments'));
}
}

Repository Layer

use App\Models\Post;

public function PostRepository
{
public function get()
{
return Post::findOrFail($id);
}
}

Also, for your last question, I'd like to say, Middlewares are meant to be used as a per-requisite only. In other words, lets say you want to ensure a user is logged in to view that particular route, then you'll apply the auth middleware and protect your routes from other not-logged in users... According to me, using Service Layer as Middleware isn't really required. You can obviously call a service layer in a Middleware by $this->myService = new Service but making it as a middleware doesn't really sound a good practice.

Hope I answered your question well enough :)



Related Topics



Leave a reply



Submit