users@javaserverfaces-spec-public.java.net

[jsr344-experts mirror] Re: JSF MVC?

From: Leonardo Uribe <lu4242_at_gmail.com>
Date: Sat, 5 Apr 2014 23:21:23 +0200

Hi

2014-04-05 17:52 GMT+02:00 Frank Caputo <frank_at_frankcaputo.de>:
> Hi all,
>
> I've been thinking much about this MVC thing. From my point of view, we
> should not focus on JSF only, instead take a look at some more specs like
> JAX-RS, Servlet/JSP and JSF.
>

+1

> I think many of the samples given by Adrian can be implemented in Java EE by
> simply forwarding from JAX-RS to the Faces Servlet.
>
> Use a JAX-RS resource. Define the model with @Model (which is actually
> @Named @RequestScoped), inject it in your JAX-RS bean and simply use it from
> JSF.
>
> Then you can do something like this (the code isn't tested):
>
> @Path("/exemple/clients")
> public class ClientResource {
>
> @Inject
> private ClientModel model; // ClientModel is annotated with @Model
>
> @Inject
> private ClientManager clientManager;
>
> @GET
> @Path("{id}")
> public void show(@Context HttpServletRequest request, @Context
> HttpServletResponse response, @PathParam("id") String id) throws Exception {
> Client lClient = clientManager.findById(id);
> model.setClient(lClient);
> model.setItemId(id);
>
> request.getRequestDispatcher("/exemple/clients/show.faces").forward(request,
> response);
> }
>
> }
>

In JSF there is ExternalContext as an abstraction of the underlying environment.
I would prefer use JSF existing stuff in that part, and refer to the parameters
as something not coupled with servlet spec. The only flaw I can see is using
ExternalContext there is no way to know when the request is a GET, a POST
and so on, which is important at the time to define the endpoint.

> We should simply collect SpringMVC examples and port them to Java EE. I'm
> willing to do so, but I don't have experience with SpringMVC. So examples
> should be provided by others.
>

I ported some time ago a simple booking app to SpringMVC in the performance
comparison done some time ago. See:

https://github.com/lu4242/performance-comparison-java-web-frameworks

There you can find the same app written in different web frameworks, including
JSF 2 of course, but it is quite helpful. It could be interesting to check how
some typical use cases are solved by different frameworks, as a way to get
some inspiration.

I consider JSF approach is all about define an abstraction so you can build
complex applications and reuse components easily. One thing that JSF solve
pretty well is the id problem. In other words, generate ids for the components
available in the page. In an action source framework there is not such concept
but that makes things difficult to reuse, because each time you need to use
something in other place, you usually need to rename the ids.

Now, it could be useful to have a "action source framework" component. That
means, an special component that work in a way that everything inside it works
just like any action source framework, but everything outside the box still work
under JSF rules.

In that sense, allow a syntax to define "endpoints" in managed beans is a part
of the problem we know that it could be useful. But there are other parts where
people don't really want to solve everything in the same way as an action
source framework is solving, because in those parts JSF just do a better job.

Does somebody out there already provided something that we can take as a
source of inspiration? Just look for the competition.

- Type on google "wicket rest", and you will find a couple of articles that
aims to do something similar.
- Type on google "tapestry rest", and you will find something like tynamo.

So, other people has already thought the same as we are trying to do and
have been doing (JSF 2.2 viewAction and JSF 2.0 viewParam), there are
some cases where an action oriented approach with a component oriented
framework can coexist. In that sense, the ability to define REST services
inside CDI managed beans through Faces Servlet seems to be important.
For what? It is not hard to find cases. For example, to expose information
to some other app or component or service that consume it as a REST
service. If you have a JSF webapp nothing can be simpler as define the
method right on the managed bean.

> Maybe we can add them to https://github.com/javaee-samples/javaee7-samples
> or create a dedicated github-repo for it.
>

+1 for use github.

I have a proposal to discuss different use cases and ideas I have on my sleeve,
that still need some review from my side, but I hope to send it to the
list in some
few days.

regards,

Leonardo

> Ciao Frank
>
> Am 01.03.2014 um 21:57 schrieb Adrian Gonzalez <adr_gonzalez_at_yahoo.fr>:
>
> [1] An action-based controller with Spring MVC
> @Controller
> @RequestMapping(value = "/exemple/clients")
> public class ClientController {
> @Inject
> private ClientManager clientManager;
>
> @RequestMapping(value = "/{id}", method = RequestMethod.GET)
> public String show(@PathVariable Long id, Model uiModel) {
> Client lClient = clientManager.findById(id);
> uiModel.addAttribute(lClient);
> uiModel.addAttribute("itemId", id);
> return "exemple/client/show";
> }
>
> @RequestMapping(method = RequestMethod.POST)
> public String create(@Valid Client client, BindingResult bindingResult,
> Model uiModel) {
> String inputView = "exemple/client/edit";
> if (bindingResult.hasErrors()) {
> return inputView;
> }
> client.setDateContact(new Date());
> try {
> clientManager.create(client);
> } catch (SomeBusinessException e) {
> bindingResult.reject("error.global", e.getMessage());
> return inputView;
> }
> uiModel.asMap().clear();
> return "redirect:/exemple/clients/" + client.getId();
> }
>
> @RequestMapping(params = "form", method = RequestMethod.GET)
> public String createForm(Model uiModel, SitePreference sitePreference) {
> uiModel.addAttribute(newClient());
> return "exemple/client/edit";
> }
> @RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
> public String delete(@PathVariable Long id, Model uiModel, SitePreference
> sitePreference) {
> Client client = clientManager.findById(id);
> clientManager.delete(client);
> uiModel.asMap().clear();
> return "redirect:/exemple/clients" + (sitePreference.isMobile() ? "-mobile"
> : "");
> }
> @RequestMapping(method = RequestMethod.PUT)
> public String update( @Valid Client client, BindingResult
> bindingResult,Model uiModel) {
> String inputView = "exemple/client/edit";
> if (bindingResult.hasErrors()) {
> return inputView;
> }
> try {
> clientManager.update(client);
> } catch (MatriculeExistantException e) {
> bindingResult.reject("error.global", e.getMessage());
> return inputView;
> }
> uiModel.asMap().clear();
> return "redirect:/exemple/clients/" + client.getId();
> }
>
> @RequestMapping(value = "/{id}", params = "form", method =
> RequestMethod.GET)
> public String updateForm(@PathVariable Long id, Model uiModel) {
> show(id, uiModel);
> return "exemple/client/edit";
> }
>
> @RequestMapping(method = RequestMethod.GET)
> public String list(@Valid ClientCriteriaForm clientCriteriaForm,
> BindingResult aBindingResult,
> Model uiModel) {
> if (aBindingResult.hasErrors()) {
> return "exemple/client/list";
> }
> ...call clientManager...
> uiModel.addAttribute("clients", clients);
> return "exemple/client/list";
> }
>
> @RequestMapping(params = "list", method = RequestMethod.GET)
> public String listForm(ClientCriteriaForm clientCriteriaForm, Model uiModel)
> {
> List<Client> clients = new ArrayList<Client>();
> uiModel.addAttribute("clients", clients);
> return "exemple/client/list";
> }
> @RequestMapping(params="cancel")
> public String cancel() {
> return "redirect:/exemple/clients";
> }
> }
>
> [2] A REST backend controller with Spring MVC
> @Controller
> @RequestMapping(value = "/exemple/rest/clients")
> public class RestClientController {
>
> @Inject
> private ClientManager clientManager;
>
>
> @RequestMapping(method = RequestMethod.GET)
> @ResponseBody
> public Map<String, Object> list(@Valid ClientCriteriaForm
> clientCriteriaForm) {
> List<Client> clients = clientManager.findByName(
> clientCriteriaForm.getNom(), clientCriteriaForm.getPage()
> * clientCriteriaForm.getSize(),
> clientCriteriaForm.getSize() + 1);
> Map<String, Object> lResults = new HashMap<String, Object>();
> if (clients.size() > clientCriteriaForm.getSize()) {
> lResults.put("hasNextPage", true);
> lResults.put("page", clientCriteriaForm.getPage());
> clients.Remove(clientCriteriaForm.getSize().intValue());
> }
> lResults.put("clients", clients);
> return lResults;
> }
>
> @RequestMapping(value = "/{id}", method = RequestMethod.GET,
> produces="application/json")
> @ResponseBody
> public Client show(@PathVariable Long id) {
> return clientManager.findById(id);
> }
> @RequestMapping( method = RequestMethod.POST, produces="application/json")
> @ResponseBody
> public Client create(@RequestBody @Valid Client client) throws
> SomeBusinessException {
> return clientManager.create(client);
> }
> @RequestMapping(value = "/{id}", method = RequestMethod.DELETE,
> produces="application/json")
> @ResponseStatus(HttpStatus.NO_CONTENT)
> public void delete(@PathVariable Long id) {
> Client client = clientManager.findById(id);
> clientManager.delete(client);
> }
>
> @RequestMapping(method = RequestMethod.PUT, produces="application/json")
> @ResponseBody
> public Client update(@RequestBody @Valid Client client) throws
> SomeBusinessException {
> return clientManager.update(client);
> }
> @ExceptionHandler(RequestBodyNotValidException.class)
> @ResponseBody
> public Map<String,Object> handleException(RequestBodyNotValidException
> exception, HttpServletResponse response) throws IOException {
> return RestUtils.convertAndSetStatus(exception, response);
> }
> @ExceptionHandler(Exception.class)
> @ResponseBody
> public Map<String,Object> handleException(Exception exception,
> HttpServletResponse response) throws IOException {
> return RestUtils.convertAndSetStatus(aException, aResponse);
> }
> }
>
>
>