Showing posts with label jheadstart. Show all posts
Showing posts with label jheadstart. Show all posts

Nov 11, 2011

Locale switching στο JHeadstart και στο ADF

Μια συνηθισμένη απαίτηση έχει να κάνει με τη δυνατότητα επιλογής γλωσσικού περιβάλλοντος (locale) σε μια ADF εφαρμογή. Μια τέτοια ενέργεια θα έχει ως αποτέλεσμα την κατάλληλη διαμόρφωση των λεκτικών στοιχείων (labels), ημερομηνιών, αριθμών και ενδεχομένως και δεδομένων (πχ λίστες τιμών) στο περιβάλλον επιλογής του χρήστη. Μου αρέσει πολύ η υποστήριξη αυτής της δυνατότητας από το JHeadstart δίχως να χρειαστεί να γράψουμε γραμμή κώδικα και πρόκειται να την παρουσιάσω καθώς είναι εξαιρετικά χρήσιμη και σε καθαρά ADF projects, αν απλά ανατρέξουμε στον πηγαίο κώδικα της υλοποίησης.

Πρώτα από όλα, την δυνατότητα επιλογής γλώσσας θα τη δίνουμε στους χρήστες μας κατά τη διάρκεια του login. Διαφορετικά μπλέκουμε σε καταστάσεις cancel edit και requery objects αν αυτοί βρίσκονται ήδη σε μια σελίδα με αλλαγές ή που παρουσιάζονται δεδομένα από φιλτραρισμένες λίστες με βάση το locale. Στη login.jspx, αντιγράφουμε κάτω από το πεδία username και password τον παρακάτω κώδικα από το templates/default/misc/file/menuGlobal.vm ώστε να προκύψει και ένα choice list για την επιλογή της γλώσσας. Διορθώνουμε μόνο την ιδιότητα labelAndAccessKey ώστε να ανταποκρίνεται στο λεκτικό της επιλογής μας:

<af:selectOneChoice id="localeSwitcher" immediate="true" autoSubmit="true"
labelAndAccessKey="${JHS.nls('Language','LANGUAGE_SELECTOR_LABEL')}"
valueChangeListener="#{jhsLocaleManager.changeLocale}"
value="#{jhsLocaleManager.currentLocale}">
<af:forEach items="#{jhsLocaleManager.supportedLocales}" var="row">
<af:selectItem value="#{row.locale}" label="#{row.description}"/>
</af:forEach>
</af:selectOneChoice>

Τώρα, ας ορίσουμε τις υποστηριζόμενες γλώσσες. Στο definition file του JHeadstart υπάρχουν οι ανάλογες ρυθμίσεις για το default locale, τα υπόλοιπα υποστηριζόμενα καθώς και τον τρόπο επιλογής του αρχικού.

Παράγοντας τις σελίδες, θα εμφανιστεί στο login και ο επιλογέας γλώσσας με εξ ορισμού τιμή αυτήν του browser μας. Ανάλογα με την επιλογή της γλώσσας, από εδώ και στο εξής στην πλοήγηση μας στην εφαρμογή θα εμφανίζονται τα αντίστοιχα μεταφρασμένα μηνύματα.

Αν θέλουμε επιπλέον να μεταφράσουμε και δεδομένα όπως λίστες τιμών που φιλτράρονται σύμφωνα με τη γλώσσα επιλογής, τότε θα χρειαστεί να δούμε λίγο τα ενδότερα του JHeadstart. Πιο συγκεκριμένα, στοιχεία του συνδεδεμένου χρήστη αποθηκεύονται σε μια session μεταβλητή ονόματι jhsUser (και τύπου: oracle.jheadstart.model.JhsUser) που περιλαμβάνει το πεδίο locale που παίρνει τιμές κατά το login μας (στο παράδειγμα μας 'en' ή 'el') Άρα μπορούμε να το χρησιμοποιήσουμε ως bind variable για φιλτράρισμα με expression της μορφής: adf.userSession.userData.jhsUser.locale.

Nov 3, 2011

Πώς να κάνουμε αναζήτηση σε πεδίο πολλαπλών επιλογών στο JHeadstart 11g

Το παράδειγμα που πρόκειται να περιγράψω αφορά τη ρύθμιση ενός πεδίου ώστε να γίνεται σε αυτό προχωρημένη αναζήτηση (advanced search) ενώ ο χρήστης έχει ορίσει σε αυτό πολλαπλές τιμές (multiple values) ως κριτήρια. Τα βήματα αφορούν την τελευταία παραγωγική έκδοση του JHeadstart (11.1.1.3) ενώ πληροφορίες για κάτι ανάλογο στην έκδοση 10g θα βρείτε εδώ. Τα πράγματα μάλιστα περιπλέκονται λίγο παραπάνω καθώς το πεδίο προς αναζήτηση δεν θα αποτελεί attribute του view object αλλά θα χρησιμοποιείται απλά ως bind variable.

Η προσέγγιση που θα ακολουθήσουμε αφορά την κατασκευή ενός κατάλληλου where clause στο View Object μας, ώστε το bind variable μας να περιλαμβάνει πολλαπλές τιμές. Γι' αυτό το λόγο, θα χρησιμοποιήσουμε την συνάρτηση regexpr_like της Oracle (αντί ενδεχόμενα του τελεστή IN) ώστε να κάνουμε σύγκριση με τις πολλαπλές καταχωρήσεις του χρήστη. Φτιάχνουμε λοιπόν μια έκφραση του στυλ:

WHERE regexpr_like(table_column, :bind_variable)

όπου ο τύπος της bind variable είναι java.lang.String

Για να υποστηριχθεί αυτή η έκφραση από τον εσωτερικό μηχανισμό αναζήτησης του JHeadstart, χρειάζεται να κάνουμε override το JhsApplicationModuleImpl και ειδικά να ξαναγράψουμε (copy-paste) τη συνάρτηση advancedSearch() ώστε στο κομμάτι που γίνεται ο υπολογισμός των bind variables, να περάσουμε τη λογική του regular expression.Πιο συγκεριμένα, ακολουθούν τονισμένες οι γραμμές της αλλαγής, που διαχειρίζονται τις πολλαπλές τιμές:

if (isBindParam) {
// work around for bug 4714529
if (!value.startsWith("[")) { // Array container as parameter.
vo.ensureVariableManager().setVariableValue(attribute, value);
vo.ensureVariableManager().setVariableValue(attribute, value);
vo.setNamedWhereClauseParam(attribute, value);
sLog.info("Search item matches query bind param " +
attribute + ", value set to " + value);
} else { // Array container.
sLog.info("=Array container for search found...." + value);
String attrValue = "^".concat(value.substring(1, value.length() - 1).replaceAll(",", "\\$\\|\\^").replaceAll(" ", "") + "$");
vo.ensureVariableManager().setVariableValue(attribute, attrValue);
vo.ensureVariableManager().setVariableValue(attribute, attrValue);
vo.setNamedWhereClauseParam(attribute, attrValue);
}
Πρακτικά αυτό που συμβαίνει είναι πως το array container της μορφής: "[10, 46]" που περιγράφει τα κλειδιά των στοιχείων που έχει επιλέξει ο χρήστης μετασχηματίζεται σε regular expression ως εξής: "^10$|^46$" ώστε να περάσει ως τιμή στην bind variable μας. Δεν ξεχνάμε να ορίσουμε πως το Application Module μας κληρονομεί από την κλάση που μόλις φτιάξαμε.

Τώρα, στο επίπεδο του JHeadstart, ορίζουμε ένα unbounded πεδίο με Java type String, τύπο εμφάνισης list και φυσικά το αντίστοιχο Domain όπως περιγράφεται και στο JHS Developer's Guide (7.1.7 Using Query Bind Variables in JHeadstart Quick or Advanced Search)

To template του display type "list" του JHeadstart είναι βασισμένο στο af:selectOneListbox component, οπότε το αλλάζουμε σε selectManyListbox για το πεδίο του ενδιαφέροντός μας.

Έχουμε σχεδόν τελειώσει μιας και μπορούμε να κάνουμε αναζήτηση με πολλαπλές επιλογές. Αυτές μεταφέρονται ως java.lang.String array format στο ADF BC και εμείς εφαρμόζουμε τον regular expression μετασχηματισμό μας.

Κάτι τελευταίο έχει να κάνει με τη συμπεριφορά του Reset query button. Για να πάνε όλα σωστά, θα πρέπει να φτιάξουμε το δικό μας JHS Search bean (κληρονομώντας από το JhsSearchBean) με μια μόνη μέθοδο που θα καθαρίζει τα κριτήρια μας και ειδικά τη bind variable μας, πχ:

@Override
public void clearSearchCriteria(ActionEvent event) {
getIterBinding().getViewObject().setNamedWhereClauseParam("SalsSndId", null);
super.clearSearchCriteria(event);
}

Nov 1, 2011

Πώς να συμμαζέψουμε (clean up) ένα JHeadstart project

Αυτό το χρονικό διάστημα αισθάνομαι πολύ τυχερός γιατί ένα καινούργιο project ξεκίνησε βασισμένο στον JHeadstart 11g. Είχα αναφερθεί και στο παρελθόν για την έκδοση JHS 10g ως απαραίτητο συμπλήρωμα ανάπτυξης στο ADF. Στην τελευταία έκδοση (11g) τα πράγματα είναι πιο μπλεγμένα: το ADF έχει γίνει πιο παραγωγικό πλαίσιο παρέχοντας με ευκολία βασικές αλλά και προηγμένες δυνατότητες στο web development. Από την άλλη, υπάρχει ακόμα περιθώριο για αυτοματισμούς και πειθαρχία όπως επιβάλλει το code generation του JHeadstart. Ίσως η συμβολή του να μην είναι τόσο καθοριστική όπως στην έκδοση 10g, αλλά εξακολουθεί να δίνει μεγάλη ευελιξία στις ομάδες ανάπτυξης, ιδίως σε αυτές που κάνουν τα πρώτα τους βήματα σε ADF.

Ένα από τα "παράπονα" που είχα και από την έκδοση 10g είχε να κάνει με τα κατάλοιπα του code generation που πια δεν χρησιμοποιούνται. Για παράδειγμα, στην εξέλιξη του project μας φτιάχνουμε ένα JHeadstart Lov, που έπειτα το εγκαταλείπουμε. Ή φτιάχνουμε μερικές σελίδες πάνω σε κάποια view objects που τελικά δεν χρησιμοποιούμε. Πώς μπορούμε να εντοπίσουμε τέτοια άχρηστα; Πώς μπορούμε να τακτοποιήσουμε τα generated artifacts (pages, task flows) του JHeadstart που είναι ανενεργά;

Μια προσέγγιση είναι να μην κρατάμε τα αρχεία που παράγονται από το JHeadstart σε ένα versioning σύστημα, ώστε να μπορούμε ανά πάσα ώρα και στιγμή να κάνουμε ένα project checkout και ένα καθαρό code generation ολόκληρης της εφαρμογής.Υπάρχουν μειονεκτήματα σε αυτό, καθώς είναι ενάντιο στα best practices. Συχνά επίσης κρατάμε σελίδες που έτσι και αλλιώς έχουμε "παγώσει" από το generation. Αυτόματο build και deployment δεν είναι εφικτό. Έχουμε να κάνουμε με μεγάλο αριθμό αρχείων που δεν γίνονται version και αυτό μπορεί να προκαλέσει σύγχυση. Μια εναλλακτική, είναι να προκαλέσουμε ένα code generation, και να δούμε το timestamp των αρχείων που δεν έχουν μεταβληθεί στο αμέσως προηγούμενο χρονικό διάστημα. Αυτό θα μας οδηγήσει στην ανίχνευση παλαιών σελίδων, task flows ή κώδικα που δεν είναι πια απαραίτητος και δεν παράγεται εκ νέου.

Πηγαίνοντας λοιπόν από command line στο ViewController project μας (που λειτουργεί ο JHS) υπάρχουν δυο βασικοί κατάλογοι που γράφει το JHeadstart: ο public_html και ο adfmsrc (για τα page definitions): Για τον μεν πρώτο, μπορούμε να βρούμε τα αρχεία που δεν έχουν αλλάξει τα τελευταία πέντε λεπτά:

cd public_html && find . ! -mmin -5 | grep -v "jheadstart/"

ενώ για τον δεύτερο η εντολή είναι:

cd adfmsrc && find . ! -mmin -30

Έτσι μπορούμε να δούμε τα αρχεία που δεν έχουν μεταβληθεί, να τα εξετάσουμε και ενδεχόμενα να τα διαγράψουμε αν δεν μας είναι πια απαραίτητα.

Jun 4, 2009

Μια πρόταση υλοποίησης σχετιζόμενων lovs σε οθόνες αναζήτησης (search cascade lovs) του JHeadstart

Μια απαίτηση που προέκυψε σε ένα έργο με JHeadstart 10g, είναι να υπάρχει συσχέτιση μεταξύ δυο list of values (lovs) πεδίων στις οθόνες της προχωρημένης αναζήτησης (advanced search) και άρα η επιλεγόμενη τιμή του πρώτου, να κατευθύνει τον περιορισμό των τιμών του δεύτερου. Αν και μια τέτοια συμπεριφορά είναι σχετικά εύκολο να επιτευχθεί για οθόνες καταχώρησης, όπου τα bindings μας απευθύνοντας ευθέως σε ένα ADF view object, οπότε αλλάζοντας την τιμή ενός πεδίου, μπορούμε να επηρεάσουμε τον κώδικα του setter του ώστε να θέτει μια bind variable σε ένα άλλο view object, κάτι τέτοιο δεν συμβαίνει με τις οθόνες αναζήτησης: ο λόγος είναι πως ο JHeadstart δημιουργεί ένα Java bean για τα πεδία της αναζήτησης και άρα δεν επικοινωνούμε απευθείας με το view object μας. Επομένως, αν ανατρέξουμε στον κώδικα μιας JSP σελίδας θα δούμε κάτι ως το ακόλουθο για το value binding:

value="#{searchDeptAndEmployees.criteria.DeptAndEmployeesLocationId}"

όπου το πρώτο τονισμένο (bold) μέρος είναι το όνομα του JHS search bean, ενώ το δεύτερο (criteria) είναι το όνομα της δομής που κρατά σε μορφή java.util.Map τα κριτήρια μας.

To παράδειγμα μας στηρίζεται στο σχήμα HR, όπου έχουμε φτιάξει ένα συνδυαστικό view object (DeptAndEmployees) από τα entities Departments και Employees, στο οποίο γίνεται η αναζήτηση. Επιπλέον υπάρχουν δυο view objects που υλοποιούν τα list of values μας. To πρώτο είναι το DepartmentsLookup και το δεύτερο το EmployeesByDeptLOV που αναμένει την παράμετρο του department id για να εκτελεστεί. Έτσι έχουμε σχεδιάσει δυο εξαρτώμενα view objects.

Αφού το ξεκαθαρίσαμε αυτό, παρατηρώντας πιο κοντά ένα list of value σε επίπεδο JSP σελίδας, βλέπουμε πως γίνεται η επιστροφή της τιμής που επιλέξαμε από το JSF:

<af:selectInputText id="SearchDeptAndEmployeesEmployeeId" value="#{searchDeptAndEmployees.criteria.DeptAndEmployeesEmployeeId}"
action="dialog:EmployeesByDeptLOV" windowHeight="200" windowWidth="600" ...
returnListener="#{DeptAndEmployeesEmployeeIdLovItemInAdvancedSearch.returnedFromLov}">
</af:selectInputText>

Η πρόκληση που έχουμε να αντιμετωπίσουμε είναι να μεταβάλλουμε τον τρόπο λειτουργίας του returnListener, ώστε ταυτόχρονα με την ενημέρωση της τιμής στο parent window, να προκαλεί και την εκτέλεση άλλων view objects που αναμένουν την ενημερωμένη τιμή ως bind variable. Όλα αυτά τα στοιχεία θα πρέπει να είναι παραμετρικά. Γι' αυτό το λόγο, θα κάνουμε χρήση ενός γνωρίσματος του JHeadstart που μας επιτρέπει να ορίζουμε metadata ανά πεδίο, όπως φαίνεται στην επόμενη εικόνα. Συνεπώς ορίζουμε μια λίστα τιμών που περιλαμβάνει το όνομα του data control (δηλαδή του application module), του view object που θα ενημερωθεί και την bind variable που απαιτείται.

Θα χρειαστεί τώρα να φτιάξουμε ένα δικό μας lovitembean που θα λαμβάνει υπόψη του αυτές τις παραμέτρους. Άρα, ορίζουμε μια νέα κλάση που κληρονομεί από τη oracle.jheadstart.controller.jsf.bean.LovItemBean και βασικά επανά-ορίζει τον κώδικα της μεθόδου returnedFromLov:

public void returnedFromLov(ReturnEvent returnEvent) {
// Execute parent event.
super.returnedFromLov(returnEvent);
// Retrieve returned value.
String returnedValue =
((ValueHolder)returnEvent.getComponent()).getValue().toString();

// Iterate over custom properties of the field.
Set> entrySet = customProperties.entrySet();
for (Map.Entry entry: entrySet) {
// Custom property found.
if (entry.getKey() != null && entry.getValue() != null) {
// param1: Datacontrol name.
// param2: view object name.
// param3: bind variable name.
String[] params = entry.getValue().split(",");
assert params.length == 3;
// Apply bind variable to dependent view object.
ApplicationModule module =
(ApplicationModule)JsfUtils.getExpressionValue("#{data." +
params[0] +
".dataProvider}");
ViewObject vo = module.findViewObject(params[1]);
vo.setNamedWhereClauseParam(params[2], returnedValue);
vo.executeQuery();
}
}
}


Η δήλωση του δικού μας Java bean πρέπει να επηρεάσει την διαδικασία του generation, άρα ορίζουμε το δικό μας lovItemInAdvancedSearchBean.vm στο επίπεδο του master lov, όπου γίνεται το πέρασμα των παραμέτρων των metadata, δημιουργώντας ένα νέο managed property.


Κάνουμε generate και επιλέγουμε μια τιμή από το αρχικό (parent) lov, έστω του IT Department.

Κάνουμε click τώρα στο δεύτερο lov, το οποίο έχει ενημερωθεί και εμφανίζει τα φιλτραρισμένα αποτελέσματα.

Mar 22, 2009

Κάνοντας include velocity macros στο JHeadstart

Η τροποποίηση του layout μιας web εφαρμογής αντιμετωπίζεται με μεθοδικό τρόπο στο JHeadstart, βασισμένο στην αρθρωτή (modular) αρχιτεκτονική του. Η δημιουργία μιας σελίδας είναι αποτέλεσμα της συνάθροισης διάφορων στοιχείων (headers, button bars, forms, κλπ) που το προφίλ τους αποθηκεύεται σε αρχεία μακροεντολών της γλώσσας Velocity (http://velocity.apache.org/). Αν χρειαστεί να μεταβάλλουμε αυτό το αποτέλεσμα, απλά ορίζουμε το δικό μας αρχείο μακροεντολών στον generator. Στο δικό μας εξειδικευμένο αρχείο vm, είναι δυνατόν να ενσωματώσουμε κάποιο άλλο αρχείο με την εντολή parse, όπως για παράδειγμα:

## Include standard JHeadstart velocity file tableGroupButtons.vm
#parse ("default/pageComponent/tableGroupButtons.vm")

Aug 3, 2008

Κάνοντας trace το generation του JHeadstart

Κάτι χρήσιμο, είναι το tracing του generation του JHeadstart, ιδίως όταν το generation αυτό καθ' αυτό παρουσιάζει προβλήματα. Σε αυτήν την περίπτωση, αρκεί να ξεκινήσουμε το JDeveloper από command line εκτελώντας το αρχείο $JDEV_HOME/jdev/bin/jdev και ένα πλήθος πληροφοριών θα παρουσιαστεί με το generation.

May 11, 2008

Αποτελεσματικότερη εξαγωγή (export) στο excel από το JHeadstart

Υπάρχουν διάφορα αξιόλογα άρθρα που περιγράφουν το πώς είναι δυνατόν να εξάγουμε ένα ADF πίνακα σε excel (όπως http://kuba.zilp.pl/?id=421, http://blogs.ittoolbox.com/oracle/jjflash/archives/exporting-from-adf-faces-to-csv-excel-23168) με τη βοήθεια του Apache POI (http://poi.apache.org/ ) Σε όλες τις προσεγγίσεις, είναι απαραίτητο να χρησιμοποιήσουμε μια μέθοδο σε ένα backing bean στην οποία ορίζεται το όνομα του ADF iterator (που κρατά τα δεδομένα), προχωρά στη δημιουργία του excel αρχείου και ενεργοποιείται με το πάτημα ενός κουμπιού.

Θέλοντας να εκμεταλλευτούμε τις templating δυνατότητες του JHeadstart, το όνομα του iterator μπορεί να γίνει παράμετρος ώστε να κατασκευάσουμε ένα επαναχρησιμοποιήσιμο κουμπί. Υπάρχει λοιπόν μια ειδική μεταβλητή ονόματι ITERATOR που μπορούμε να χρησιμοποιήσουμε στα vm's μας ώστε να αναφερθούμε στα τρέχοντα δεδομένα. Έτσι, η κατασκευή ενός γενικού κουμπιού εξαγωγής στο excel μπορεί να μοιάζει ως εξής:
<af:commandButton text="#{nls['button.exporttoexcel']}" id="ExportToExcelButton" action="#{navBean.onExportToExcelClick}" rendered="#{#ITERATOR().estimatedRowCount > 0}" >

<af:setActionListener from="#{#ITERATOR().name}" to="#{requestScope.iteratorName}" />

</af:commandButton >



Κατά αυτόν τον τρόπο, παιρνάμε σαν παράμετρο το όνομα του iterator, το οποίο μπορούμε μετέπειτα να χρησιμοποιήσουμε για την παραγωγή του excel αρχείου.

Apr 20, 2008

Χρησιμοποιώντας Javascript στον JHeadstart

Δεν αποτελεί σπάνια περίπτωση η απαίτηση να επηρεάσουμε την συμπεριφορά κάποιου UI component των ADF Faces με την χρήση Javascript. Όταν μάλιστα χειριζόμαστε το JHeadstart (http://www.oracle.com/technology/products/jheadstart) προκειμένου να επιταχύνουμε την ανάπτυξη σύμφωνα με το ADF, τότε η εφαρμογή παράγεται από το generator. Για να ενσωματώσουμε ένα Javascript αρχείο στις σελίδες μας, αρκεί να το δηλώσουμε στο commons/regions/pageConfig.jspx, στην ενότητα scripts.

Κατά αυτόν τον τρόπο, ο κώδικας του δικού μας αρχείου της Javascript είναι προσβάσιμος από όλες τις σελίδες τις εφαρμογής (και τα αντίστοιχα velocity scripts), και, κυρίως, δεν σβήνεται όταν θα ξανατρέξουμε τον generator.

Sep 12, 2007

Έλεγχοι (validations) κατά την εκτέλεση ενός wizard στο JHeadstart

Μια από τις πιο ισχυρές ευκολίες που παρέχει το JHeadstart για την ανάπτυξη εφαρμογών ADF, είναι η εύκολη δημιουργία σελίδων για την ολοκλήρωση μιας σύνθετης συναλλαγής που αποτελείται από διάφορα βήματα, που διαμοιράζονται σε πολλαπλές σελίδες. Ο χρήστης ειδοποιείται στο πάνω μέρος της σελίδας για το στάδιο στο οποίο βρίσκεται σχετικά με την περάτωση της συνολικής διαδικασίας. Το ADF Faces component που χρησιμοποιείται ονομάζεται processTrain.

Ένα από τα ιδιαίτερα χαρακτηριστικά του wizard που δημιουργείται είναι πως οι validations κανόνες που έχουν οριστεί σε επίπεδο Entities, δεν ενεργοποιούνται παρά μόνο στο τελευταίο βήμα της όλης διαδικασίας. Παρόλα αυτά, είναι συχνά επιθυμητό, να πραγματοποιούνται οι έλεγχοι κατά την μετάβαση από το ένα βήμα στο επόμενο του wizard, να παρουσιάζονται εκείνη τη στιγμή τα μηνύματα λάθους και να παραμένει ο χρήστης στην ίδια οθόνη για να διορθώσει τις παρατηρήσεις.

Για να επιτευχθεί αυτό, είναι απαραίτητη η επέμβαση στον τρόπο που διαχειρίζεται το JHeadstart το wizard. Αρχικά, ανατρέχουμε στο faces-config αρχείο που κρατά τις ρυθμίσεις των beans για το wizard μας. Από αυτό το αρχείο δανειζόμαστε την κλάση του JHeadstart που διαχειρίζεται τα βήματα του wizard.

Έπειτα, δημιουργούμε μια νέα, δικιά μας κλάση, που κληρονομεί από αυτή του wizard του JHeadstart που βρήκαμε μόλις παραπάνω. Η μοναδική μέθοδος που χρειάζεται να κάνουμε override είναι η getNextAction() που μπορεί να έχει την ακόλουθη δομή:

public String getNextAction() {
// Check of an attribute, of those an end-user has to submit in the first
// page of the wizard.
String value =
(String)JsfUtils.getExpressionValue("#{bindings.MasterDetailWizardGroupOrderId.inputValue}");
// If the user has submitted value.
if (value != null && value.equals("111")) {
// Validate the user input, by invoking the appropriate method
// probably of the Application Module.

// In case of error, stay in the current page.
JsfUtils.getInstance().addError("Value 111 is not allowed !");
return null;
}
checkFocusRowIndex();
return super.getNextAction();
}


Όπως διαφαίνεται, αρκεί να επιλέξουμε ένα attribute το οποίο ξέρουμε πως θα έχει τιμή (π.χ. ύστερα από την αναγκαστική συμπλήρωση του στο κάποιο βήμα του wizard) και με βάση αυτό να εκτελέσουμε το validation κώδικα μας (π.χ. καλώντας μια μέθοδο στο Application Module) Εάν το αποτέλεσμα δεν είναι θετικό, παραμένουμε στην ίδια σελίδα (return null) διαφορετικά καλούμε τη συμπεριφορά που θα έπρεπε να έχει το framework (checkFocusRowIndex(); return super.getNextAction();)

Το τελευταίο κομμάτι που μας απομένει, είναι να ξαναγυρίσουμε στο faces-config των beans μας και να ορίσουμε σαν κλάση του wizard, τη δικιά μας.

Χωρίς καμία άλλη αλλαγή, και χωρίς JHeadstart generation μπορούμε να ελέγξουμε τη συμπεριφορά του συστήματος. Για παράδειγμα έχω ορίσει σαν validation κανόνα πως όταν η συμπλήρωση σε ένα πεδίο είναι η τιμή "111" να εμφανίζεται μήνυμα λάθους, διαφορετικά συνεχίζουμε στο επόμενο βήμα.

Για να οριστικοποιήσουμε τις αλλαγές μας, θα πρέπει να τροποποιήσουμε το κατάλληλο vm αρχείο του Group μας για να περίεχει από εδώ και στο εξής της δηλώσεις στο faces-config bean αρχείο μας.