Simpele JSF Navigatie

 
09 november 2007

Het viel mij op dat de configuratie van Java Server Faces (JSF) snel erg groot kan worden. Een beetje web applicatie heeft al snel een configuratie bestand met 800 regels XML om zijn JSF framework aan te sturen. Dat moet uiteraard simpeler kunnen.

Na een korte periode zelf gespeeld te hebben met JSF, kreeg ik onlangs twee applicaties te zien die beide gebruikt maakte van JSF (De MyFaces implementatie). Hierin was iets wat mij opviel. Over het algemeen kom ik de volgende constructie tegen:

<navigation-rule>
	<from-view-id>/portfolio.jsp</from-view-id>
	<navigation-case>
		<from-action>newAccount</from-action>
		<to-view-id>/newAccount.jsp</to-view-id>
		<redirect/>
	</navigation-case>
	<navigation-case>
		<from-action>newClient</from-action>
		<to-view-id>/newClient.jsp</to-view-id>
		<redirect/>
	</navigation-case>
	<navigation-case>
		<from-outcome>deposit</from-outcome>
		<to-view-id>/deposit.jsp</to-view-id>
	</navigation-case>
	<navigation-case>
		<from-outcome>withdraw</from-outcome>
		<to-view-id>/withdraw.jsp</to-view-id>
	</navigation-case>
	<navigation-case>
		<from-outcome>transfer</from-outcome>
		<to-view-id>/transfer.jsp</to-view-id>
	</navigation-case>
</navigation-rule>

Zoals je ziet: zo goed als alle pagina’s hadden dezelfde naam als de outcome van de navigatie actie.

Een eigen NavigationHandler
In JSF kan je je eigen NavigationHandler bouwen en JSF instrueren om deze in plaats van zijn eigen NavigationHandler te gebruiken.
Een NavigatieHandler is ontzettend eenvoudig om te bouwen. Je hoeft maar één methode te implementeren.
public void handleNavigation(FacesContext facesContext, String fromAction, String outcome)
De argumenten zijn als volgt:

  • facesContext, de context van faces voor de huidige request
  • fromAction, de binding expression die geëvalueerd was om tot de outcome te komen, kan ook null zijn als het door iets anders berekend was
  • outcome, de logische uitkomst die geretourneerd was door een hiervoor aangeroepen actie, kan ook null zijn

JSF Configureren
Om je eigen implementatie van een NavigationHandler te gebruiken in JSF moet je het framework wel vertellen waar hij zich bevind:

<faces-config>
	...
	<application>
		<navigation-handler>
			nl.sogyo.faces.handler.SimpleNavigationHandler
		</navigation-handler>
	</application>
	...
</faces-config>

Mijn implementatie
Deze implementatie van een NavigationHandler is extreem simpel. Hij is uiteraard niet voor alle soorten applicatie geschikt maar voor huis-tuin-en-keuken applicaties is dit geen probleem.

public class SimpleNavigationHandler
		extends javax.faces.application.NavigationHandler {

	@SuppressWarnings("unchecked")
	@Override
	public void handleNavigation(FacesContext facesContext, String fromAction,
			String outcome) {

		if (outcome == null) {

			throw new FacesException("'outcome' must be filled!");
		}

		ViewHandler viewHandler = facesContext.getApplication().getViewHandler();

		UIViewRoot viewRoot = viewHandler.createView(facesContext, "/" + outcome);
		facesContext.setViewRoot(viewRoot);
		facesContext.renderResponse();
	}
}

Als je nu met deze implementatie vanuit een actie een woord terug geeft, bijvoorbeeld “help”. Dan zal deze NavigatieHandler je door sturen naar “/help.jsp”.

Meer?
Tuurlijk willen we meer! Deze versie is bijna hetzelfde maar heeft ook nog eens historie:

public class SimpleNavigationHandler
		extends javax.faces.application.NavigationHandler {

	private static String CMD_BACK = "_back";

	private static String PAGE_HISTORY = "_history";

	@SuppressWarnings("unchecked")
	@Override
	public void handleNavigation(FacesContext facesContext, String fromAction,
			String outcome) {

		if (outcome == null) {

			throw new FacesException("'outcome' must be filled!");
		}

		List history = null;

		ExternalContext externalContext = facesContext.getExternalContext();
		history = (List) externalContext.getSessionMap().get(PAGE_HISTORY);

		if (history == null) {

			history = new ArrayList();
			externalContext.getSessionMap().put(PAGE_HISTORY, history);
		}

		if (outcome.endsWith(CMD_BACK)) {

			// return to the previous page
			if (history.size() > 0) {

				outcome = history.remove(history.size() - 1);
			}
			else {

				// current page
				outcome = facesContext.getViewRoot().getViewId();
			}
		}
		else {

			history.add(facesContext.getViewRoot().getViewId());
		}		

		ViewHandler viewHandler =
			facesContext.getApplication().getViewHandler();

		if (!outcome.startsWith("/")) {

			outcome = "/" + outcome;
		}

		// create new view
		UIViewRoot viewRoot = viewHandler.createView(facesContext, outcome);
		facesContext.setViewRoot(viewRoot);
		facesContext.renderResponse();
	}
}

Ik zou zeggen: ga er lekker mee spelen!


Werken met ?
Kijk dan bij onze mogelijkheden voor starters en/of ervaren IT'ers.


Categorieën: Development

Tags: ,


Reactie

  • Tammo schreef:

    Erg fijn.
    Ik ben net een beetje begonnen met JSF te prutsen en ik zat me al erg te irriteren aan al die onformatie in faces-config.xml.
    Dit helpt een hoop!

    Geplaatst op 28 december 2007 om 19:26 Permalink