6

Let us take a basic example. In a servlet app I would have those classes:

  • app.domain.User: domain or model POJO, would contain fields and getters/setters
  • app.service.UserService: would contain methods that operate on User instances, such as Register(User). Would call the DAO.
  • app.dao.UserDao: would be called by service to actually, say, insert a User in the DB.
  • app.servlets.RegisterController: a servlet intercepting requests to www.app/registration and calling the methods in app.service.UserService. It would then redirect to WEB-INF\JSPs\registration.jsp which would take care of the view.

All this made perfect sense to me and clearly separated the concerns.

I have tried since to wrap my head around the JSF/Facelets way of thinking. I have went through many excellent resources here in SO and otherplaces but there is not any place that addresses this simple question - namely how does the dao/service/controller/view pattern translate into JSF.

To continue my example - I have set up a JSF project in eclipse, have translated my DB schema to "entities" and now I am left wondering - what kind of package, with what king of classes should I create to handle user Registration ? I understand I have to create xhtml pages for the view but where is the controller ?

Please do not point me to tutorials - I am aware of many, amongst which the @BalusC one - my problem is not to make this work but to understand the pattern. I have seen "session" packages containing "managed beans", "abstract facade" patterns etc etc but nothing that makes as clear sense as the good old servlets pattern.

So - among the "entities" created by the DB there is a User.java class that looks like the model to me. The view will be an xhtml. The controller ?

NB: this is not asking as so many duplicates here the differences between those technologies - I am asking about a clear translation of the very clear and intuitive controller/dao/service pattern to the JSF framework - or a clear statement that there is not such a translation.

See also:

Community
  • 1
  • 1
Mr_and_Mrs_D
  • 32,208
  • 39
  • 178
  • 361

3 Answers3

3

There are two levels of MVC in JSF 2:

  • Model = @ManagedBean, View = Facelets/xhtml, Controller = FacesServlet if you look only at jsf
  • Model = Entities + Services, Controller = @ManagedBean, View = xhtml if you look at your application at a whole.

The typical package layout in a JSF application is the same:

  • com.foo.domain.featurex.service <- model
  • com.foo.domain.featurex.model <- model
  • com.foo.presentation.featurex <- controller
  • xhtml files <- view

Not that big of a difference here except that your controller is now the FacesServlet (the heart of JSF) together with your @ManagedBean/@Named which would reside in a class like com.foo.presentation.featurex.Controller/Handler or something like that.

Example for a simple registration:

// we talk about the big picture here, not about how jsf is built 
// but how your app is built


// model
package com.foo.domain.registration.entity;

@Entity
public class User { 
    // fields
}

package com.foo.domain.registration.service;

// also model
@Stateless
public class RegistrationService {

    @PersistenceContext 
    EntityManager em;

    public void register(User u) { 
        em.persist(u);
    } 
}

package com.foo.presentation

// controller
@Named
@ViewScoped
public class RegistrationController { 

    @Inject
    RegistrationService rs;

    User current = new User();

    public void register() { 
        rs.register(u);
    }

    // get set for current user
}

// view
// quite a lot boilerplate omitted
// form omitted which displays all the necessary field of your current user
// via value="#{registrationContoller.current.name}"
<h:commandButton value="submit" type="submit" action="#{registrationController.register}" />
atamanroman
  • 11,607
  • 7
  • 57
  • 81
  • see also: https://stackoverflow.com/questions/5104094/what-components-are-mvc-in-jsf-mvc-framework and and http://www.adam-bien.com/roller/abien/entry/bureaucratic_design_with_java_ee – atamanroman Feb 01 '14 at 16:31
  • Personally I consider com.foo.domain.featurex.service and com.foo.presentation.featurex a huge antipattern. Why not name your packages in the logic of your application? com.foo.featurex.presentation and com.foo.featurex.service – Leon Feb 01 '14 at 16:33
  • Well dividing your projects in domain/business and presentation is something people are used to. I wouldn't insist on this separation however. Much more important is to divide the components in features and not only in `service/dao/entity/controller`, because this just scales better. – atamanroman Feb 01 '14 at 16:39
  • Hmm - I still do not get it. So Instead of "UserService.register(User)" - what ? (please do not answer here - edit your answer) – Mr_and_Mrs_D Feb 01 '14 at 16:39
  • Also - you say `Controller = @ManagedBean` and then `your controller is now the FacesServlet` - see all this is confusing contradictory sttements and it's not you - they are _all over the place_. Or `Model = @ManagedBean` and then `Controller = @ManagedBean` (asker dies of confusion) – Mr_and_Mrs_D Feb 01 '14 at 16:46
  • I understand your confusion. There are just two different views here on a JSF app. Have a look at my linked question (first comment) which is answered by BalusC. – atamanroman Feb 01 '14 at 16:48
  • I am sure it is clear to you but to me still my comment applies - please clarify when each of the 2 views applies and how exactly both the "managed beans" (whatever they are from an MVC point of view) and the FacesServlet play the role of controller. Also are you sure (as `adam-bien.com` says) that I need to have so many packages ? Like com.app.registation, com.app.login, com.app.order, etc etc ? Why not good old entities, services, controllers and fin (a;though this is a separate question) ? Good try anyway +1 – Mr_and_Mrs_D Feb 01 '14 at 17:27
  • Packages per component/feature just scale better in the long term. Otherwise you will have a `service` package with hundreds of services in the future. Where will you put classes which are related to a certain feature if there is no package for it? That's the reasoning behind it. Regarding when which MVC perspective applies: JSF is a *view* technology implemented MVC like. BalusC spoke about M(MVC)C, where jsf is the MVC in your V. That quite fits it. The same should be true for Spring MVC, although I had only a few short looks on it yet. – atamanroman Feb 01 '14 at 17:44
1

It does not. They are conceptual different. MVC uses a MVC model, and jsf uses a handler based model.

The face of you page is done with the XHTML file. The handler handles the use input like submitting form. A binding exists between the form elements and the handler, normally using getters and setters. So if the user submits a form an method of you handler is called and all the submitted data is already in place for use to process (thank to the load of event phases).

So your XHTML file gives you username and password. The handler method is called. You do the lookup of the credentials and return a string, forwarding the user to a new page.

As said before, you cannot map the two technologies. It's like sending an email or sending a written letter. Yes, they transport information, and yes it is a relay messeging system. But the way how it is done is totally different. It is the same clash of paradigm when writing jsf and then switching to wicket.

Hannes
  • 2,018
  • 25
  • 32
1

Here's what I ended up doing - Service, plays the DAO:

package app.service;

import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import app.entities.User;

@Stateless
public class UserService {

    @PersistenceContext
    private EntityManager em;

    public User register(User u) {
        // TODO : transactions, sql injection
        // https://stackoverflow.com/q/21640369/281545
        em.persist(u);
        // em.flush(); // not needed
        return u;
    }

    public boolean isUsernameUnique(String username) {
        Query query = em
            .createNativeQuery("SELECT r1_check_unique_username(?)");
        short i = 0;
        query.setParameter(++i, username);
        return (boolean) query.getSingleResult();
    }
}

Controller - accessed by the form

package app.controllers;

import javax.annotation.PostConstruct;
import javax.ejb.EJB;
import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;
import javax.faces.bean.ViewScoped;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.validator.Validator;
import javax.faces.validator.ValidatorException;
import javax.persistence.NoResultException;

import app.entities.User;
import app.service.UserService;

@ManagedBean
@ViewScoped
public class UserController {

    // https://stackoverflow.com/a/10691832/281545
    private User user;
    @EJB // do not inject stateful beans (?)
    // @Inject
    private UserService service;

    public User getUser() {
        return user;
    }

    @PostConstruct
    void init() {
        // https://stackoverflow.com/questions/3406555/why-use-postconstruct
        user = new User();
    }

    public String login() { return null; }

    public String register() {
        FacesContext context = FacesContext.getCurrentInstance();
        user = service.register(user);
        if (user.getIduser() == 0) {
            context.addMessage(null, new FacesMessage(
                FacesMessage.SEVERITY_ERROR, "Registration failed", null));
            return null;
        }
        context.getExternalContext().getSessionMap().put("user", user);
        return "/index.xhtml?faces-redirect=true";
    }

    public String logout() { return null; }

    @ManagedBean
    @RequestScoped
    public static class UniqueUsernameValidator implements Validator {

        @EJB
        private UserService service;

        @Override
        public void validate(FacesContext context, UIComponent component,
                Object value) throws ValidatorException {
            if (value == null) return; // Let required="true" handle, if any.
            try {
                if (!service.isUsernameUnique((String) value)) {
                    throw new ValidatorException(new FacesMessage(
                        FacesMessage.SEVERITY_ERROR,
                        "Username is already in use.", null));
                }
            } catch (Exception e) {}
        }
}

Notice the packages and that I use service as DAO. Not clear on transactions and EJB vs CDI and all that but it works™

Community
  • 1
  • 1
Mr_and_Mrs_D
  • 32,208
  • 39
  • 178
  • 361