blog.smart-java.nl
Ordina J-Technologies – Java Blog

Archief ‘Web’ categorie




Oracle neemt Sun over

Door: Stephan Oudmaijer, 20 April 2009

Oracle en Sun hebben een akkoord bereikt over de overname van Sun door Oracle. Lange tijd leek het erop dat IBM een van de belangrijkste kanshebber was om Sun over te nemen. Echter konden IBM en Sun het niet eens worden over de voorwaarden waaronder de deal door zou gaan en de prijs per aandeel. Oracle betaalt de aandeelhouders van Sun $9.50 per aandeel, hetgeen neerkomt op een deal van $7.4 billion dollar. Dat is  $0.10 per aandeel meer dan IBM bereid was om neer te tellen.

De overname van Sun door Oracle levert wel een aantal interessante vraagstukken op, wat gaat Oracle bijvoorbeeld doen met Java? Op dit moment is er nogal wat onenigheid over het Java Community Proces (JCP), het standardisatie proces voor Java. Velen hopen dat Oracle de JCP nu echt open zal maken. Anderen zeggen dat er geen sprake van een inspraak proces is. Aan de andere kant, misschien is het wel niet in het belang van Oracle om Java nog opener te maken om het zodoende concurrent IBM lastiger te maken?

Verder zijn er nog NetBeans en  GlassFish? Aangezien Oracle BEA heeft overgenomen, bekend van o.a. de applicatie server WebLogic, is er bij Oracle een overschot aan applicatie servers ontstaan. Zelfde geldt voor ontwikkelomgeving NetBeans vs JDeveloper, zullen beide naast elkaar blijven bestaan of worden ze samengevoegd?

Daarnaast is Oracle plotsklaps eigenaar geworden van MySQL, door velen gezien als het open source alternatief voor Oracle. Moeten we ons nu gaan verdiepen in PostgreSQL? ;)

Voor het orginele persbericht zie: http://www.oracle.com/corporate/press/2009_april/018363.htm




Can software developers build usable GUI’s?

Door: Vincent Hartsteen, 17 April 2009

Most developers would answer this question the same way that Bob the Builder would when asked if he can fix it. “Yes I can” would be the reply. And I’m sure that Bob can. Why? Because Bob does his building based on a blueprint that tells him exactly what to build and what to use for building it.

In construction the architect is responsible for producing the blueprint. The architect talks to the customer in order to get a list of requirements for the building. For instance is the building used as an office-space or is it used as a family-home. Should it be a single or multi story building and how many rooms should it have. And of course the amount of money the customer is willing to spend. Geared with the list of requirements the architect uses his design skills and his technical knowledge to make a first sketch of the building. The architect discusses this sketch with his team of electrical, mechanical and structural engineers to find out if it feasible from a technical point-of-view. For instance is the construction mechanically strong enough, are the selected materials useable, etcetera. This should all result in a blueprint for a building that meets the customer’s requirements, is usable with respect to the needs of the customer and is aesthetically pleasing. Finally the constructionworkers take the blueprint and start building the construction.

In software development some similar process takes place. In short the information analist and the customer discuss about the requirements for the system. What functionality should the system offer, what is the expected load etcetera. The information analist works in close cooperation with the software architect. The software architect is responsible for making the technical decisions (which frameworks to use, what platform, how many nodes, etc.) such that the non-functional requirements (scalability, extensibility, manageability, etc.) can be met. The UML-artifacts created by the information analist and the software architect (e.g. use-case model, software architecture document, use-case realizations, etc.) are input for the developers.

One major difference between the blueprint for the construction workers and the UML-artifacts for the developers is that the blueprint has usability embedded. The architect has knowledge about how to make a construction usable to its users. When there is a requirement that a house must have 2 bathrooms the architect knows that to make them usable he could place one on the groundfloor and the other on the first floor. He could have fulfilled the requirement by putting both bathrooms on the attic but that would not have made them very useable. Very often a developers gets a requirement which basically comes down to: “the application must have a web-interface or a GUI and it should be user-friendly”. That’s it. So now it is up to the developers to figure out what “user-friendly” is.

Most developers have a technical mindset and find it hard to step into the role of the end-user. It is the end-user that decides if the application is user-friendly. He does so when the application supports the end-user to do his job without problems. So in order to design a user-friendly interface it must be clear what the application is used for and how it is used. Very often user-friendlyness is misconceived as “protect the end-user from making mistakes”. If end-users use the application every now and then to do some work it can be valid to pop-up a confirmation dialog. Expert users that use the application on a regular basis get annoyed by such dialogs.

There are currently lots of tools/frameworks that allow developers to build goodlooking user-interfaces: JavaFX, Flex, SAF, JSF, Wicket, and the list goes on. But goodlooking is not the same as user-friendly. With the tooling mentioned before we could build a very nice 3D, animated, gradient color dialog box that is very annoying (remember the Office paperclip?). So basically we have the tools to create cool and goodlooking user-interfaces but most of us lack the knowledge of how to build usable ones.

My point is that there should be a role in the softwaredevelopment team, just like a information analist or a software architect, that is responsible for designing the interaction with the end-user. Just like the architect in construction. I haven’t come across a person with such a role in the many projects I’ve worked on over the past years. Until that time it is up to us developers to design user-interfaces and for that we need to broaden our horizon and try to place ourselves on the end-user seat.

A few years ago I read “About Face. The Essentials of User Interface Design” (first edition) written by Alan Cooper which got me interested on this subject (3rd edition is the most recent version). The book describes the problems that occur when developers start designing user-interfaces and give advice on how to improve on that, all illustrated with entertaining examples. If you get into the situation where you as a developer are responsible for the design of the user-interface this book might help you do a better job.

Vincent Hartsteen

N.B.: Currently I’m reading “The Inmates are running the Asylum”, also by Alan Cooper. This book describes the role of “interaction designers”. I’ll write my findings when I’ve finished the book.




Facelets code completion in Eclipse Ganymede

Door: Ron Thijssen, 23 October 2008

Onlangs hebben Jan-Kees en ik de J-Tech JSF cursus flink op de schop genomen. We hebben de keuze gemaakt om JSP er definitief uit te bonjouren, en daarvoor in de plaats de Facelets te gebruiken. Facelets biedt enorm veel mogelijkheden, en is de facto standaard voor een JSF project. Het maakt het leven van een JSF ontwikkelaar op een flink aantal gebieden een stuk aangenamer.

Vorige week, tijdens de 1e dag van de JSF cursus, liepen we echter tegen het grootste struikelblok van Facelets aan, namelijk: Support in diverse IDE’s. Facelets vervangt de traditionele *.jsp files in op XML gebaseerde *.xhtml files. Deze worden standaard niet ondersteund door bijvoorbeeld Eclispe, dat wil zeggen; Geen tag completion of validatie van de gebruikte tags uit de namespaces. Hierdoor kan het voorkomen dat je nogal snel een typvoudt maakt die je pas tijdens het draaien van de applicatie tegenkomt. Ik had in een grijs verleden iets gelezen over het gebruiken van .jspx files ipv .xhtml. Tijd om eens uit te zoeken wat de opties zijn. (more…)




Spring MVC 2.5: BindingResult probleempje

Door: Jan-Kees van Andel, 9 October 2008

Zojuist liep ik tegen een vervelend probleempje aan. Ik was bezig met het bouwen van een weblog met Spring 2.5 en alles ging goed, tot het laatste schermpje. Namelijk een klein schermpje om je password te wijzigen.

Dit is de controller:

@Controller
@SessionAttributes("changePassword")
public class ProfileController extends AbstractAdminController {
 
    public static final class ChangePasswordBean {
        private String password1;
        private String password2;
        // Getters and setters
    };
 
    @Autowired private BlogService blogService;
 
    @RequestMapping(value="/admin/users/profile", method=RequestMethod.GET)
    public ModelAndView setupForm(ModelMap model, HttpSession session) {
        model.addAttribute("changePassword", new ChangePasswordBean());
        return new ModelAndView("admin/users/profile", model);
    }
 
    @RequestMapping(value="/admin/users/profile", method=RequestMethod.POST)
    public String processSubmit(
            @ModelAttribute ChangePasswordBean changePassword,
            BindingResult result, HttpSession session, SessionStatus status) {
        new PasswordValidator().validate(changePassword, result);
        if (result.hasErrors()) {
            return "admin/users/profile";
        } else {
            // Call to service layer
            return "redirect:/admin/home.html";
        }
    }
}

Dit is de JSP met het form:

  ...
  <form:form modelAttribute="changePassword">
    <form:errors />
    <table class="form">
      <tr>
        <td><form:label for="password1" path="password1">Password</form:label></td>
        <td><form:password id="password1" maxlength="20" path="password1" /></td>
        <td><form:errors path="password1" /></td>
      </tr>
      <tr>
        <td><form:label for="password2" path="password2">Retype password</form:label></td>
        <td><form:password id="password2" maxlength="20" path="password2" /></td>
        <td><form:errors path="password2" /></td>
      </tr>
      <tr>
        <td colspan="3">
          <input type="reset" value="Cancel" /> |
          <input type="submit" value="Save" />
        </td>
      </tr>
    </table>
  </form:form>
  ...

Deze opzet werkte bij alle schermen, maar om de een of andere reden niet voor dit “simpele” schermpje. Het form openen ging goed, submitten ging ook goed, zolang er geen validatiefouten waren, maar bij validatiefouten kreeg ik de volgende melding:

java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'changePassword' available as request attribute
    at org.springframework.web.servlet.support.BindStatus.<init>(BindStatus.java:141)
    at org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.getBindStatus(AbstractDataBoundFormElementTag.java:172)
    at org.springframework.web.servlet.tags.form.AbstractHtmlElementTag.resolveCssClass(AbstractHtmlElementTag.java:412)
    at org.springframework.web.servlet.tags.form.AbstractHtmlElementTag.writeOptionalAttributes(AbstractHtmlElementTag.java:388)
    ...

Toch creemd. Ik had de JSP en de controller gewoon gekopieerd van een wel werkend scherm, maar om de een of andere reden werkte het toch niet. Ligt het aan de inner class? Nee, vast niet. Hoewel, het is wel een final class. Misschien wil Spring wel een proxy aanmaken en mag de class niet final zijn ofzo. Hup, final eraf. Scheisse, nog steeds hetzelfde probleem. Wat kan het dan zijn?

Oplossing

Toen viel mijn oog op de processSubmit methode in de controller. Daar week de naam van de parameter af van de klassenaam. Class name: ChangePasswordBean, argument name: changePassword.

Hmm, dat zal wel het probleem zijn, want bij alle andere schermen waren deze gelijk. Toevallig wist ik dat in de bytecode van de klasse geen parameternamen staan en dus ook niet @runtime uit te lezen zijn. De mannen van Spring zijn waarschijnlijk tegen dezelfde beperking aan gelopen en hebben hier omheen gewerkt door standaard de type name als request attribute name te gebruiken, in plaats van de parameter naam (wat logischer is, maar dus niet kan in Java).

Dit was de oplossing:

    // Before fix (this didn't work)
    public String processSubmit(
			@ModelAttribute ChangePasswordBean changePassword,
			...) {
 
    // After fix (working solution)
    public String processSubmit(
			@ModelAttribute("changePassword") ChangePasswordBean changePassword,
			...) {

Dusss, gebruik in je JSP’s de klassenaam of geef expliciet de naam op in de @ModelAttribute annotation.




Rich web applicaties met Spring Web MVC

Door: Jethro Bakker, 5 September 2008

In de spring cursus van Ordina wordt het Spring Web MVC framework behandeld. Tijdens de cursus wordt ook gekeken naar de integratie met ajax technologie.  In 2007 is bijvoorbeeld het DWR (Direct Web Remoting) framework behandeld. Met de komst van Spring Webflow 2.0 is het DWR onderdeel komen te vervallen en behandelen we nu Spring Javascript. Het is een relatief onbekende library, die wat mij betreft meer aandacht mag krijgen.

Wat is Spring Javascript?

Je kunt Spring Javascript zien als een abstractie laag bovenop bestaande widget technologie en ajax remoting. Momenteel is er alleen nog ondersteuning voor de Dojo Toolkit maar op termijn zal dit verder uitgebreid worden. Het zal dan een generieke API zijn waarmee je andere javascript frameworks kunt gebruiken.  Spring Javascript biedt je verder een resource servlet, deze servlet handelt het CSS, JS verkeer af wat je nodig hebt voor rich widgets. Een Dojo javascript file laad je bijvoorbeeld als volgt in:

<script type=”text/javascript” src=”<c:url value=”/resources/dojo/dojo.js” />”> </script>

De servlet moet je natuurlijk registreren in de web.xml en mapt op url pattern /resources/*. De Spring Javascript JAR module bevat alle javascript en stylesheets resources die je nodig hebt voor het Dojo framework. Naast de genoemde zaken bevat Spring Javascript de volgende classes:

  • AjaxTilesView, is een Tiles view implementatie dat delen van een pagina kan renderen voor ajax requests.
  • AjaxUrlBasedViewResolver, een UrlBasedViewResolver met een specifieke implementatie voor redirect views.
  • SpringJavascriptAjaxHandler, een handler klasse die gebruikt wordt door de AjaxTilesView om ajax requests goed te kunnen afhandelen.

Voorbeeld

We starten met het registreren van twee beans, de tiles configurer en de view resolver.

<bean id="tilesConfigurer" class="org.springframework.web.servlet.view.tiles2.TilesConfigurer">
  <property name="definitions">
    <list>
	<value>/WEB-INF/defs/general.xml</value>
    </list>
  </property>
</bean>

<bean id="tilesViewResolver" class="org.springframework.js.ajax.AjaxUrlBasedViewResolver">
  <property name="viewClass" value="org.springframework.webflow.mvc.view.FlowAjaxTilesView"/>
</bean>

De ajax support in Spring Javascript werkt nauw samen met Apache Tiles. Met Apache Tiles kun je de webpagina opdelen in logische stukken. Met behulp van Spring Javascript kun je een los deel vervangen. Bijvoorbeeld alleen de body. Een normale link moet je decoreren met een stuk javascript:

<a id="newEntry" href="entry/edit.html">Nieuwe entry</a>
<script type="text/javascript">
Spring.addDecoration(new Spring.AjaxEventDecoration({
  elementId: "newEntry",
  event: "onclick",
  popup: false,
  params: {fragments: "body"}
}));

Deze link zal nu ajax gebruiken en het onderdeel ‘body’ van de pagina vervangen door nieuwe content. Je hebt ook de mogelijkheid om de link in een pop-up te openen. Naast deze ondersteuning voor ajax remoting is het ook mogelijk om widgets te gebruiken. Dat kan als volgt:

<form:input id="subjectString" path="subject" />
<script type="text/javascript">
Spring.addDecoration(new Spring.ElementDecoration( {
  elementId :"subjectString",
  widgetType :"dijit.form.ValidationTextBox",
  widgetAttrs : {
    promptMessage :"Fill in a subject for this blog entry"
  }
}));
</script>

Zoals je kunt zien wordt de Spring Javascript library aangeroepen. Deze library zal intern de Dojo toolkit gebruiken. Spring Javascript is dus een abstractie laag bovenop de bestaande widget technologie. Op dit moment biedt dat nog niet zoveel voordeel maar op het moment dat er meerdere widget technlogieen ondersteund worden kun je op een uniforme manier de widget technologie gebruiken.

Ik ben benieuwd wat jullie er van vinden.

In een volgende post zal ik ingaan op Spring Faces, deze library maakt ook onderdeel uit van de Spring Webflow distributie.




JSF 2.0 EDR: Publish/Subscribe Event System

Door: Jan-Kees van Andel, 13 July 2008

In de vorige post heb ik het over het nieuwe resource management systeem van JSF 2.0 gehad. Dit keer wil ik met een voorbeeldje laten zien wat je kunt met het nieuwe Publish/Subscribe Event System.

Publish/Subscribe

Publish/Subscribe is een algemeen toegepast mechanisme om event driven applicaties te maken. Het werkt zo: Een publisher is de zender van een bericht. Deze verzendt een bericht naar de runtime die Publish/Subscribe verzorgt, zoals middleware of een framework. Op basis van bijvoorbeeld configuratie is bekend welke ontvangers geïnteresseerd zijn in dit bericht en het bericht wordt naar al deze ontvangers verstuurd.

Eigenschappen van Publish/Subscribe zijn:

  • Ontkoppeling van zender en ontvanger; het enige dat ze verbindt, is het bericht,
  • Een zender kan een bericht naar meerdere ontvangers sturen,
  • Een ontvanger kan van meerdere zenders een bericht ontvangen.

Typische Publish/Subscribe voorbeelden zijn een radiostation en RSS feeds. Publish/subscribe in GUI’s wordt doorgaans geimplementeerd middels het Observer pattern.

Laten we eerst kijken hoe JSF 2.0 dit model wil implementeren.

JSF 1.x events

De huidige versie van JSF bevat al een event mechanisme waarmee objecten/componenten events kunnen versturen en ontvangen. Een voorbeeld hiervan is het UICommand component, waarvan <h:commandLink /> en <h:commandButton /> gebruikmaken. Dit component kan ActionEvents versturen, wat ervoor zorgt dat bijvoorbeeld een action method in een managed bean uitgevoerd wordt.

Dit hele systeem draait om de methode queueEvent, gedefinieerd in UIComponent. Met deze methode worden events verstuurd die door de lifecycle op het juiste moment naar de juiste listener verstuurd worden.

Daarnaast heb je PhaseEvents, die aan het begin en aan het einde van elke fase in de lifecycle verstuurd worden en door PhaseListeners afgevangen worden.

Ten slotte definieert de JSF 1.x spec ValueChangeEvents, die afgevuurd worden als de nieuwe waarde (submittedValue) van een UIInput anders is dan de waarde tijdens het renderen van de view.

Naast de standaard events is het mogelijk om eigen events te definiëren, bijvoorbeeld als je een AJAX component library ontwikkelt.

JSF 2.0

In JSF 2.0 wordt een nieuw soort event geïntroduceerd, namelijk het SystemEvent. Dit event wordt gebruikt om op specifieke momenten in te prikken in de JSF applicatie. Op het moment zijn er 4 soorten SystemEvents gedefinieerd, namelijk:

  • AfterAddToParentEvent
    Geeft aan dat het component (de source parameter) is toegevoegd aan de component tree.
  • BeforeRenderEvent
    Geeft aan dat het component (de source parameter) straks gerenderd wordt. Dit is het moment om last minute aanpassingen te doen.
  • ViewMapCreatedEvent
    De view scope is aangemaakt. Zie View scope.
  • ViewMapDestroyedEvent
    De view scope is verwijderd. Zie View scope.

De spec is echter nog niet definitief, dus er kunnen nog events bijkomen. Daarnaast kun je zelf events definiëren.

Deze events maken het mogelijk om tweaks te doen aan de lifecycle van JSF, zonder terug te hoeven vallen op low-level zelfbouwoplossingen, zoals eigen PhaseListeners. Wie zelf weleens een AJAX component geschreven heeft met een PhaseListener (om het AJAX request af te vangen of voor resource requests), weet hoe onhandig het werkt en hoe foutgevoelig het is.

Bovendien wordt integratie van component libraries hiermee stukken makkelijker, aangezien ze onder water allemaal van dezelfde mechanismen gebruik maken. Nu nog hopen dat het voldoende flexibiliteit biedt voor libraries zoals Trinidad en Ajax4jsf, zodat ze hun eigen (Filter/RenderKit/ContextListener) oplossingen te migreren naar de officiële manier.

Eerlijk gezegd valt het systeem me tot nu toe nog wat tegen, voornamelijk het registreren van event listeners is niet echt handig. De voorbeeldapplicatie die met de JSF 2.0 EDR meegeleverd wordt, bevat een hack in de meegeleverde Facelets versie (de methode apply in ValidateHandler is non-final gemaakt) die het mogelijk maakt om de listener te registreren. Zonder een dergelijke hack kun je niet fatsoenlijk listeners registreren. Ik hoop dat ze een soort configuratie systeem toevoegen, want nu moet je weer terugvallen op PhaseListeners. :(

View scope

In JSF 2.0 is een nieuwe scope gedefinieerd, naast de bestaande scopes (request, session, application), namelijk de View scope. De spec zegt nog niets over de view scope, maar conceptueel is de view scope al wel bekend.

https://javaserverfaces-spec-public.dev.java.net/issues/show_bug.cgi?id=290

De view scope zit tussen request en sessie in qua lifetime. De view scope begint op het moment dat een UIViewRoot instantie aangemaakt wordt en eindigt als een nieuwe UIViewRoot instantie actief wordt gemaakt via FacesContext.setViewRoot.

Concreet betekent dit dat zolang je op dezelfde pagina blijft (bijvoorbeeld een invoerscherm, waarin je – voordat je op “Save” drukt – nog wat extra acties doet), de data in de view scope in leven blijft. Op het moment dat je naar de volgende pagina (bijvoorbeeld van detail terug naar master) navigeert, wordt de view scope gereset.

Hoe werkt het?

Om dit alles mogelijk te maken zijn er een paar aanpassingen gedaan aan de API.

SystemEvent (nieuw)
Dit is de base klasse waarvan alle SystemEvents moeten overerven.

SystemEventListener (nieuw)
Dit is een nieuwe interface, die aangeeft dat de implementerende klasse als listener kan dienen voor SystemEvents. Er zijn twee methoden gedefinieerd, namelijk isListenerForSource en processEvent. processEvent spreekt voor zich, die wordt aangeroepen met als parameter de SystemEvent instantie. isListenerForSource wordt gebruikt om de calls van processEvent te beperken. Bijvoorbeeld tot events die gegooid zijn door instanties van een bepaald type. Je kunt een SystemEventListener dus configureren om alleen events van bepaalde componenten te verwerken.

ComponentSystemEventListener (nieuw)
Deze interface doet hetzelfde als SystemEventListener, maar bevat geen methode isListenerForSource, aangezien de koppeling met een bepaald type component al voor een impliciete variant van deze methode zorgt.

UIComponent (toevoegingen)
Hieraan zijn nieuwe methoden toegevoegd, namelijk subscribeToEvent, unsubscribeFromEvent en getListenersForEventClass.

public void subscribeToEvent(FacesContext context,
                             Class<? extends SystemEvent> eventClass,
                             ComponentSystemEventListener componentListener)

Met subscribeToEvent kun je componentListener aan deze component instantie koppelen die alle events afvangt die van het type eventClass zijn.

Met unsubscribeToEvent doe je precies het tegenovergestelde, namelijk de listener weer verwijderen.

getListenersForEventClass returned een lijst met alle listeners die events van het opgegeven type afvangen.

Application (toevoegingen)
Hieraan zijn ook een aantal methoden toegevoegd, namelijk subscribeToEvent, unsubscribeFromEvent en publishEvent. De eerste twee zijn volgens mij wel duidelijk, maar nu zijn de event handlers niet gekoppeld aan een specifieke component instantie. Met publishEvent kun je een event opgooien die dan door de JSF runtime verwerkt wordt.

@ListenerFor (nieuw)
Ten slotte is een annotation gemaakt, genaamd ListenerFor. Klassen die hiermee geannoteerd zijn, worden als listener geregistreerd. Dit gebeurt op dezelfde manier als hoe de subscribeToEvent methoden op Application en UIComponent werken. Dit is de verantwoordelijkheid van JSF.

Als je de annotation gebruikt heb je minder last van het feit dat er geen fatsoenlijk configuratiemechanisme is.

Voorbeeld

Iedereen (die met JSF gewerkt heeft) heeft het weleens gehad. Er treedt een validatiefout op, maar je hebt het niet door, omdat er geen <h:messages /> tag op de pagina ligt. Er vervelend. In dit voorbeeld laat ik een oplossing hiervoor zien met behulp van het beschreven event mechanisme.

We gaan door met de code uit het vorige voorbeeld. Ik beschrijf hier alleen de onderdelen die betrekking hebben op dit onderwerp.

Het idee is dat we op het moment dat we de view gaan renderen, kijken of er al een <h:messages /> tag op de pagina ligt. Als dit niet het geval is, voegen we deze programmatisch toe.

Een extra detail, dit mag alleen zichtbaar zijn tijdens development, daarom moeten we de volgende code toevoegen aan web.xml:

web.xml

<context-param>
    <param-name>javax.faces.PROJECT_STAGE</param-name>
    <param-value>Development</param-value>
</context-param>

Om te beginnen, zorgen we dat er geen <h:messages /> tag in de pagina ligt. Zo forceren we dat ons mechanisme afgaat. Zie de volgende pagina.

homepage.xhtml

<html ... >
<h:head>
  <title>JSF2 test</title>
</h:head>
<h:body>
  <p>Hello, you are logged in. </p>
  <h:form>
    <h:commandButton action="#{loginBean.addMessage}" value="Click" />
    <hr />
    <h:commandLink action="loginPage">Back to login page</h:commandLink>
  </h:form>
</h:body>
</html>

Dan maken we de event listener klasse, die ervoor zorgt dat een <h:messages /> tag aan de pagina wordt toegevoegd. In dit voorbeeld heb ik er zelfs een custom component van gemaakt, zodat je tijdens development goed kunt zien dat dit component automatisch toegevoegd is. Dat doen we in de volgende klasse.

AddDefaultMessagesListener.java

public class AddDefaultMessagesListener implements SystemEventListener {

    public boolean isListenerForSource(Object source) {
        return source instanceof UIViewRoot;
    }

    public void processEvent(SystemEvent evt) throws AbortProcessingException {
        if (!(evt instanceof BeforeRenderEvent)) {
            String klazz = (evt != null) ? evt.getClass().getName() : "null";
            throw new AssertionError("Event '"+evt+"' is not of type BeforeRenderEvent, but of type '"+klazz+"'");
        }

        FacesContext fc = FacesContext.getCurrentInstance();
        if (fc.getApplication().getProjectStage().equals(ProjectStage.Development)) {
            if (!hasMessagesComponent(fc, fc.getViewRoot())) {
                String viewId = fc.getViewRoot().getViewId();
                System.out.println("No <h:messages /> tag found in view '"+viewId+"'. ");
                addMessagesComponentToPage(fc, fc.getViewRoot());
            }
        }
    }

    private void addMessagesComponentToPage(FacesContext fc, UIViewRoot viewRoot) {
        String viewId = fc.getViewRoot().getViewId();
        UIForm form = findFirstForm(fc, viewRoot);
        if (form != null) {
            System.out.println("Adding default <h:messages /> tag to view '"+viewId+"' in the first form on the page. ");
            form.getChildren().add(0, new DummyUIMessages());
        } else {
            System.out.println("No form found while trying to add default <h:messages /> tag to view '"+viewId+"'. No <h:messages /> added. ");
        }
    }

    private boolean hasMessagesComponent(FacesContext fc, UIComponent comp) {
        if (comp instanceof UIMessages) {
            return true;
        }
        for (UIComponent child : comp.getChildren()) {
            if (hasMessagesComponent(fc, child)) {
                return true;
            }
        }
        return false;
    }

    private UIForm findFirstForm(FacesContext fc, UIComponent comp) {
        if (comp instanceof UIForm) {
            return (UIForm) comp;
        }
        for (UIComponent child : comp.getChildren()) {
            UIForm form = findFirstForm(fc, child);
            if (form != null) {
                return form;
            }
        }
        return null;
    }
}

Deze code zal vast niet optimaal zijn, al is het maar dat er naar de std out gelogged wordt. Ook zullen de zoekfuncties niet bijzonder efficiënt zijn, al weet ik zo snel geen snellere manier dan een vorm van caching en dat is weer erg fragiel aangezien de component tree nogal dynamisch is.

Deze listener wordt nog niet aangeroepen, want hij is nog niet geregistreerd. Dat doen we nu.

ConfigPhaseListener.java

public class ConfigPhaseListener implements PhaseListener {

    private static final Object LOCK = new Object();

    private static boolean initialized = false;

    //@BadCode(type="reallyBadCodeDoNotUseInProduction")
    public ConfigPhaseListener() {
        synchronized (LOCK) {
            if (!initialized) {
                AddDefaultMessagesListener listener = new AddDefaultMessagesListener();
                FacesContext fc = FacesContext.getCurrentInstance();
                fc.getApplication().subscribeToEvent(BeforeRenderEvent.class, listener);

                initialized = true;
            }
        }
    }

    public void afterPhase(PhaseEvent event) {}

    public void beforePhase(PhaseEvent event) {}

    public PhaseId getPhaseId() {
        return PhaseId.RESTORE_VIEW;
    }
}

Deze code is een beetje eng. Ten eerste wordt er in de constructor gesynchroniseerd. Dit is sowieso alleen maar mogelijk omdat het op een static variabele gebeurt, want in de constructor synchroniseren op this is gevaarlijk, aangezien this naar een algemeen beschikbaar object wijst. Ook staat er een static boolean initialized die in de constructor op true gezet wordt. De reden dat die er staat, is dat in de JSF spec niets gezegd wordt over de lifecycle van PhaseListeners.
Deze twee constructies (syncen en initalized boolean) zorgen er samen voor dat er niet meerdere instanties van de listener geregistreerd worden. Meerdere listeners zorgen namelijk voor dubbele output in de HTML pagina en dat willen we niet.

Note: Dit is absoluut niet production ready! Ik heb bijvoorbeeld geen rekening gehouden met geclusterde deployments.

We hebben nu de SystemEventListener en een PhaseListener om deze te registreren, ter volledigheid zal ik de faces-config nog even laten zien.

faces-config.xml

<lifecycle>
    <phase-listener>nl.ordina.jsf2.bean.ConfigPhaseListener</phase-listener>
</lifecycle>

Ten slotte mijn eigen UIMessages implementatie, die duidelijk laat zien dat je een fout gemaakt hebt.

public class DummyUIMessages extends UIMessages {

    public void encodeEnd(FacesContext context) throws IOException {
        ResponseWriter writer = context.getResponseWriter();
        assert(writer != null);

        writer.startElement("div", this);
        writer.writeAttribute("style", "border: 1px solid #FF0000", null);
        writer.startElement("h1", this);
        writer.writeAttribute("style", "color: #FF0000", null);
        writer.write("Automatically inserted messages tag, for development purposes only. ");
        writer.write("");
        writer.write("Add a <h:messages /> tag to remove this crap. ");
        writer.endElement("h1");

        super.encodeEnd(context);

        writer.endElement("div");
    }
}

Je kunt waarschijnlijk wel zien wat deze code doet. Er wordt een dikke vette grote rode tekst getoond met een waarschuwing. Daaronder staan de messages.

En om het te bewijzen, hier een screenshot.

DummyUIMessages screenshot

Conclusie

Het nieuwe event mechanisme staat nog in zijn kinderschoenen, maar er staat wel een vrij goede basis waarmee je op zijn minst aan de gang kunt. Ik heb momenteel maar een paar dingen aan te merken, namelijk:

  • Ik wil nog meer soorten events, bijvoorbeeld events voor Exception handling, of events die veroorzaakt worden door specifieke lifecycle events, zoals validatiefouten waardoor de lifecycle kortsluit.
  • Ook zijn de event listeners nog niet echt goed te configureren. De API is er, maar waar roep je die methoden aan? Een ServletContextListener is geen optie, want die valt buiten de JSF FacesContext. Ik heb voor bovenstaand voorbeeld een PhaseListener gebruikt, maar dat is ook niet ideaal, aangezien een PhaseListener bedoeld is om faseovergangen te herkennen en niet gemaakt is voor initialisatie van je framework. JSF biedt hier echter geen hooks voor. Bovendien geeft het overhead. Deze overhead valt op zich wel mee, aangezien de logica in de constructor staat, maar dat is ook een hack, want de spec zegt niets over de lifecycle van PhaseListeners. Dat is ook de reden van deze overdreven defensieve constructie. Het is simpelweg een hack, maar hij werkt wel en deze extra checks maken hem ook portable over JSF implementaties.
  • Bovendien zou een meer declaratieve manier van configureren prettig zijn. Er zit een annotation bij, maar ik geef de voorkeur aan een XML config file, zoals een paar extra tags in faces-config.xml. Misschien kunnen ze bij JSF ook overstappen op namespaces zoals bij Spring.

Download sources

Klik hier voor de voorbeeldcode. Er zit ook een simpel autocomplete component bij dat gebruikmaakt van het nieuwe event mechanisme om AJAX requests af te handelen.

Autocomplete component screenshot

Note: ook dit autocomplete component is niet production-ready! Ik heb geen rekening gehouden met performance, security, robuuste code, JSF implementatie portability, Java versie, cross browser issues, client side memory usage, etc. Ook zul je de Prototype.js dependency waarschijnlijk niet willen.

Bronnen

Weblog van Ryan Lubke, waar nieuwe features van JSF 2.0 beschreven worden.
http://blogs.sun.com/rlubke/entry/jsf_2_0_new_feature1

De download pagina van de JSF 2.0 specificatie.
http://jcp.org/aboutJava/communityprocess/edr/jsr314/index.html




JSF 2.0 Early Draft

Door: Jan-Kees van Andel, 30 June 2008

Eindelijk, de eerste Mojarra (JSF 2.0) release kan gedownload worden van de JSF site. Eens kijken of ze de beloftes waar kunnen maken.

Ze zeggen dat ze op dit moment de volgende features hebben geïmplementeerd:

  • Resource management
  • Nieuw (publish/subscribe) event mechanisme
  • Project stages
  • Ajax API

Ik zie nog niets over Facelets, page flow, nieuwe scopes, integratie met WebBeans of Servlet 3.0, maar goed, daar staat momenteel ook nog niets over in de spec. Ik vind het belachelijk, maar goed, daar ga ik het dit keer niet over hebben.

Laten we eens gaan experimenteren met wat we WEL hebben, te beginnen met resource management.

Resource Management

Laten we beginnen met een simpele applicatie, namelijk een standaard inlogscherm. Je voert een username+password in en je wordt ingelogd. Daarna kom je op een homepage.

Libraries
Eerst eens de binaries downloaden. Die staan hier.
https://javaserverfaces.dev.java.net/servlets/ProjectDocumentList?folderID=9437&expandFolder=9437&folderID=0

In de lib directory staan 2 JAR’s. Die moet je naar de WEB-INF/lib van je web project kopiëren.

Config
web.xml

<web-app>
  <context-param>
    <param-name>javax.faces.STATE_SAVING_METHOD</param-name>
    <param-value>client</param-value>
  </context-param>

  <context-param>
    <param-name>facelets.LIBRARIES</param-name>
    <param-value>/WEB-INF/facelet.taglib.xml</param-value>
  </context-param>

  <servlet>
    <servlet-name>Faces Servlet</servlet-name>
    <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
  </servlet>

  <servlet-mapping>
    <servlet-name>Faces Servlet</servlet-name>
    <url-pattern>/faces/*</url-pattern>
  </servlet-mapping>

</web-app>

Op het eerste gezicht weinig met een gewone web.xml in je huidige JSF project. In dit geval is er ook geen verschil, maar JSF 2 biedt extra configuratieopties die we later nog zien.

faces-config.xml

<faces-config>
  <application>
    <view-handler>com.sun.facelets.FaceletViewHandler</view-handler>
  </application>

  <navigation-rule>
    <navigation-case>
      <from-outcome>loginPage</from-outcome>
      <to-view-id>/login.xhtml</to-view-id>
    </navigation-case>
  </navigation-rule>

  <navigation-rule>
    <from-view-id>/login.xhtml</from-view-id>
    <navigation-case>
      <from-outcome>success</from-outcome>
      <to-view-id>/homepage.xhtml</to-view-id>
    </navigation-case>
  </navigation-rule>

  <managed-bean>
    <managed-bean-name>loginBean</managed-bean-name>
    <managed-bean-class>nl.ordina.jsf2.bean.LoginBean</managed-bean-class>
    <managed-bean-scope>session</managed-bean-scope>
  </managed-bean>

  <validator>
    <validator-id>jsStringValidator</validator-id>
    <validator-class>nl.ordina.jsf2.validator.JsStringValidator</validator-class>
  </validator>

</faces-config>

Ook hier geen verschil met de huidige JSF versie. Het valt wel op dat er nog niets aan Facelets integratie gedaan is. Zie bijvoorbeeld de package naam van de ViewHandler, dat mag natuurlijk nooit in een Java API.

facelet.taglib.xml

<facelet-taglib>
  <namespace>http://www.ordina.nl/facelet/taglib</namespace>
  <tag>
    <tag-name>jsStringValidator</tag-name>
    <validator>
      <validator-id>jsStringValidator</validator-id>
      <handler-class>nl.ordina.jsf2.validator.JsStringValidatorHandler</handler-class>
    </validator>
  </tag>
</facelet-taglib>

Voor de mensen die Facelets kennen ziet deze file er ook niet vreemd uit. Gewoon een standaard Facelets taglib descriptor waarin we een Validator configureren voor gebruik binnen Facelets pagina’s.

Managed Beans
LoginBean.java

public class LoginBean implements Serializable {
    private String username, password;

    public String login() {
        if ("jan-kees".equals(username)
          && "van andel".equals(password)) {
            return "success";
        } else {
            FacesContext fc = FacesContext.getCurrentInstance();
            System.out.println(fc.getCurrentPhaseId().toString());
            fc.addMessage(null, new FacesMessage("Username and/or password invalid"));
            return "failure";
        }
    }

    // Getters and setters
}

Hier zijn ook niet veel verschillen te zien tussen een ouderwetse en moderne Managed Bean. En dat is maar goed ook. Het Managed Beans model van JSF zit goed in elkaar.

Leuk detail, je kunt nu ook de lifecycle fase opvragen vanuit de FacesContext. Geen PhaseListener meer nodig.

Views
De views zien er wel iets anders uit dan we gewend zijn.

login.xhtml

<html
    xmlns="http://www.w3.org/1999/xhtml"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:ui="http://java.sun.com/jsf/facelets"
    xmlns:taglib="http://www.ordina.nl/facelet/taglib">

    <h:head>
        <title>JSF2 test</title>
    </h:head>
    <h:body>
        <img src="#{resource['images/logo.gif']}" alt="Logo Ordina" />
        <h2>Login</h2>
        <h:messages errorClass="error" />
        <h:form>
            <h:panelGrid columns="2">
                <h:outputLabel for="username" value="Username" />
                <h:inputText id="username" label="Username" required="true" value="#{loginBean.username}">
                    <taglib:jsStringValidator min="5" max="100" />
                </h:inputText>

                <h:outputLabel for="password" value="Password" />
                <h:inputSecret id="password" label="Password" required="true" value="#{loginBean.password}">
                    <taglib:jsStringValidator min="1" max="100" />
                </h:inputSecret>

                <f:facet name="footer">
                    <h:commandButton action="#{loginBean.login}" value="Login" />
                </f:facet>
            </h:panelGrid>

        </h:form>

        <h:outputStylesheet name="style/style.css" />

    </h:body>

</html>

In grote lijnen ziet het er herkenbaar uit, maar er zijn wel wat verschillen met voorheen.

Nieuwe tags
Mojarra heeft deze containers nodig voor zijn resource management mechanisme. Dit systeem maakt het mogelijk om op een willekeurige plek in de pagina een script of css te importeren, zonder dat je de ranzige HTML krijgt zoals bij de meeste frameworks, waar de <script> of <link /> tag gewoon tussen de content gegooid wordt. Los van dat het ranzig is, houden screenreaders er meestal ook niet van.Componenten kunnen ook resources nodig hebben, denk bijvoorbeeld aan een AJAX component die een stukje JavaScript nodig heeft. Die JavaScript wil je niet inline in je pagina hebben, ook wil je geen <script> tag in je body en je wilt je pagina ontwikkelaars niet lastigvallen met het feit dat jouw component een script nodig heeft.Je ziet bijvoorbeeld de tag staan. Deze staat in de body, dus het gebruik ervan is erg gemakkelijk voor een pagina ontwikkelaar, zeker wanneer je met een templating engine (zoals Facelets) werkt en je ergens in een geïnclude pagina een script wilt importeren. Met deze tag wordt de <h:outputstylesheet />
<link /> tag netjes in de head gezet.Je hebt ook nog een <h:outputscript /> tag. Die doet hetzelfde als de <h:outputstylesheet /> tag, maar dan voor een externe script file. Het verschil tussen outputScript en outputStylesheet is dat outputStylesheet standaard in de head wordt gezet en dat outputScript gewoon op dezelfde plaats blijft staan.Er is een attribuut “target” waarin de waarden “head” en “body” gezet kunnen worden. Hiermee overschrijf je de default target settings van outputScript. outputStylesheet wordt altijd in de head gerenderd.

Annotations
Het mooiste zou zijn als een component zelf zijn resources zou aangeven, zodat je in je pagina maar één tag hoeft neer te leggen, zonder het gedoe dat je scripts ofzo vergeet.In mijn testapplicatie heb ik een Validator geschreven die met JavaScript ook op de client een check doet. Deze JavaScript hoort bij het component en is dus daar ook geconfigureerd.

@ResourceDependency(name = "js/stringValidator.js")
public class JsStringValidator implements Validator, ComponentSystemEventListener {
    private int min = 5, max = 100;

    public void validate(FacesContext fc, UIComponent comp, Object value) throws ValidatorException {
        if ((fc == null) || (comp == null)) {
            throw new NullPointerException("comp or facescontext");
        }
        if (!(value instanceof String)) {
            throw new IllegalArgumentException("Argument value is no String, but a " + value);
        }
        String converted = (String) value;
        if (converted.length() < min
          || converted.length() > max) {
            String message = MessageFormat.format(
                                    "{0} is not between {1} and {2}",
                                    converted, min, max);
            FacesMessage facesMessage = new FacesMessage(FacesMessage.SEVERITY_ERROR, message, message);
            throw new ValidatorException(facesMessage);
        }
    }

    public void processEvent(ComponentSystemEvent event) throws AbortProcessingException {
        UIComponent c = (UIComponent) event.getSource();
        FacesContext fc = FacesContext.getCurrentInstance();
        String js = MessageFormat.format(
                                                "validate(\"{0}\", {1}, {2})",
                                                c.getClientId(fc), min, max);
        c.getAttributes().put("onfocus", js);
        c.getAttributes().put("onblur", js);
        c.getAttributes().put("onkeyup", js);
    }

    // Getters and setters
}

Let op de annotation bovenaan de klasse, namelijk @ResourceDependency(name = “js/stringValidator.js”). Hiermee kun je aangeven dat jou component bepaalde resources nodig heeft om zijn werk te doen. Dit component heeft dus een JavaScriptje nodig om op de client wat te doen.

Let ook op de methode processEvent, waarin de aanroep naar de volgende JavaScript function wordt gemaakt. Dit is de enige plaats waar een Validator in het rendering mechanisme kan inprikken. Hoe en waarom de processEvent methode aangeroepen wordt, komt een volgende keer, bij event handling.

function validate(id, min, max) {
    var inputFld = document.getElementById(id);
    if (inputFld.value.length < min
     || inputFld.value.length > max) {
        inputFld.style.borderColor = "#FF0000";
    } else {
        inputFld.style.borderColor = "#000000";
    }
}

Dit script doet weinig spannends, namelijk als een veld onjuist is ingevuld, het randje rood kleuren, maar je kunt natuurlijk ook geavanceerde RIA dingen op deze plaats programmeren, zoals effecten of AJAX calls.

De volgende keer gaat het over een andere nieuwe feature, namelijk het nieuwe event mechanisme. Daarin leg ik meteen uit hoe bovenstaande Validator opgenomen wordt in het renderen van de pagina, want een Validator wordt normaal gesproken niet gerenderd. Die bestaat alleen server side.

Source code
Sample application demonstrating resource management