Mittwoch, 16. Juli 2014

DB2 show version in Servlet

The JDBC-Driver is provided by the J2EE-Container. So your Web-Applications doesn't really know which Version of a Driver is used. If you Google you will find following advice:
java -cp ./db2jcc.jar com.ibm.db2.jcc.DB2Jcc -version
This is a way to display the used Version on Console. e.g. if you are a Server-Admin and you what to know which driver version is installed on the system.
String[] errorSoon = {"-version"}; DB2Jcc.main(errorSoon);
The problem is, that DB2Jcc.main does not return a String which you ca pass to the Servlet. So you want to know what DB2Jcc does.
The String you want to get generated by calling a static method qp.d();
import com.ibm.db2.jcc.am.qp; qp.d() ;
Of course, this is only working if you use DB2. So it is a good idea to Write a separate Servlet - in case that your app uses a another JDBC-Driver (Another Vendor or Version of DB2) that does not use this internal method.

Donnerstag, 6. Juni 2013

jquery ui tabs tab selected callback

If you are using the tabs()-element of the jqueryui you might want to perform a custom action after the selection of the tab. The tab()-element is offering different methods.
  1. is executed before the tab switches. You have the possiblity to cancel the action.
  2. is executed after the tab has switched. You got e.g. the option to set the focus to a specific element
http://api.jqueryui.com/1.8/tabs/#event-show An other option is just to bind the tabsshow http://api.jqueryui.com/1.8/tabs/#event-select

Freitag, 8. Februar 2013

using jquery's spinner in custom component


Assumtion/prerequirements
  1. You are using jQuery
  2. You are using jQuery UI

By using jquery ui's autocomplete you get this nice spinner in your input-tag.
It is pretty easy to reuse the spinner in you own component.





$('#user_id').addClass( "ui-autocomplete-loading");





$('#user_id').removeClass( "ui-autocomplete-loading");




In my case I used an following ajax-call:

$('#user_suchen').click(function() {
  $('#user_id').addClass( "ui-autocomplete-loading" );
  var userId = $('#user_id').val();
  $.ajax({
    url : '${getUserData}',
    data : { "userId" : userId },
    dataType : "json",
    success : function(data, textStatus, jqXHR) {
      $('#name').val(data.name);
      $('#oe').val(data.oe_bezeichnung);
      $('#user_id').val(data.userId);
      $('#user_id').removeClass( "ui-autocomplete-loading" );
    },
    error : function( jqXHR, jqXHR, textStatus,errorThrown ) {
      $('#user_id').removeClass( "ui-autocomplete-loading" );
    }
  });
});

Mittwoch, 30. Januar 2013

WAS choose faces implementation

Because WAS7 ist JEE5 compiant -
and every JEE5 container need to deliver a JSF 1.2 .
WAS7 gives you the option to choose which JSF-Implementation you want to provide for the app.

The following Screenshot tells you how to do this.

By the way - Did you notice that IBM does not offer the option to choose an IBM-Implementation of JSF 1.2
That's the reason why we have to deal with the old JSF 1.1 version in our app.
Migration is very difficult.
>
I read that with JEE7 (comming mai 2013) - JSF 2.2 will be part of the spezifikation.

Dienstag, 29. Januar 2013

OpenJPA DB2 hellojpa

In order to execute the sample app, you need to create the table in your Database.
In our case the user/owner ist called TESTUSR.
The table definition works fine with the sample, and it has soem adtional features.
CREATE TABLE TESTUSR.MESSAGE (
 ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 1) PRIMARY KEY,
 CREATED DATE DEFAULT CURRENT_DATE NOT NULL,
 MESSAGE VARCHAR(2000) NOT NULL
);
INSERT INTO TESTUSR.MESSAGE (MESSAGE ) VALUES  ( 'hi2' )
don't forget the configuration of your persistence.xml.
In our case we set 4 parameters.
It is also possible to use a datasource in hosted appserverenvironment.
Because we are assuming the simple we choose to set the minimal necessary parameters.

Freitag, 25. Januar 2013

WAS7 enable debugging



In order to connect to a remote java process. The JVM must be started in debugging-mode.

Application servers > server1 > Process definition > Java Virtual Machine











Mittwoch, 9. Januar 2013

Drucken-Link

Um eine HTML-Seite zu drucken kann man einen Link in seine HTML-Seite einfügen.
window.print();
Seite-Drucken

Reparieren eines kaputten WAS7 Profiles

C:\Programme\IBM\SDP75\runtimes\base_v7\bin\ProfileManagement\pmt.bat
Sobald das Tool gestartet ist, kann man sehen, wie das aktuelle Profil heisst.


C:\Programme\IBM\SDP75\runtimes\base_v7\bin>manageprofiles -delete -profileName was70profile1 INSTCONFSUCCESS: Erfolg: Das Profil ist nicht mehr vorhanden.
C:\Programme\IBM\SDP75\runtimes\base_v7\bin>manageprofiles -create -profileName was7profile1 INSTCONFSUCCESS: Erfolg: Das Profil was7profile1 ist jetzt vorhanden. Weitere In formationen zu diesem Profil finden Sie in der Datei "C:\Programme\IBM\SDP75\run times\base_v7\profiles\was7profile1\logs\AboutThisProfile.txt".
Nach der Reparatur des defecten Profiles kann man den Server wieder wie gewohnt starten.
C:\Programme\IBM\SDP75\runtimes\base_v7\bin>startserver server1 ADMU0116I: Tool information is being logged in file C:\Programme\IBM\SDP75\runtimes\base_v7\profiles\was7profile1\logs\s rver1\startServer.log ADMU0128I: Starting tool with the was7profile1 profile ADMU3100I: Reading configuration for server: server1 ADMU3200I: Server launched. Waiting for initialization status. ADMU3000I: Server server1 open for e-business; process id is 1720
http://localhost:9060/ibm/console
  • Port der Administrationskonsole: 9060
  • Sicherer Port der Administrationskonsole: 9043
  • Port für HTTP-Transport: 9080
  • Port für HTTPS-Transport: 9443
  • Bootstrap-Port: 2809
  • SOAP-Connector-Port: 8880

Montag, 9. Juli 2012

DB2 - retrieve generated id



If you what to know the which ID was given to a record after an insert .

Record successfuly save with id: 815

e.g. insert into mytable (col1, col2, col3) values ( 'val1',  'val2', 'val3');

just calll

values identity_val_local();

it will return the last generated id.

e.g. 2

Sonntag, 4. Dezember 2011

JAVA_HOME auf dem MAC

wie setzt man die JAVA_HOME-Umgebungsvaribale auf dem MAC? Hier ist die Anwort, wie man dies z.B. in einem Script tun sollte.

/usr/libexec/java_home

Montag, 14. November 2011

Suche in DB2 mit Java

Häufig muss man mit Java in relationalen Datenbanken nach Texten suchen.
Dabei sollte die Groß- und Kleinschreibung nicht von Bedeutung sein.

Doch wie erreicht man dies?

Komplizierter wird dies noch, wenn man die deutschen Sonderzeichen beachtet.
Was passiert mit eine "ß" ? Wie wird es umgesetzt?

Die Antwort ist folgende.
Sowohl die Daten in der Datenbank, als auch die Java-Strings müssen mit
toLower() auf lower-case umgestellt werden.

Im folgenden kommt ein Real-World-Beispiel, wie es oft in unseren Programmen auftaucht. Wenn man ein "_" als searchtTerm übergibt gibt es keine Einschränkung.
Man beachte, das im DB2 mit java die substr()-funktion aufgerufen werden muss, sonst klappt das nicht.

select distinct(BERATERNAME) as beratername 
from ALMAUSR.RSB_RUECK
where lower(BERATERNAME) like :searchTerm or ('_' = substr(:searchTerm, 1, 1))



In Java erfolgt, das toLower wie folgt.
String searchTerm = "Außendienst";
searchTerm = searchTerm.toLowerCase(Locale.GERMANY);
searchTerm = searchTerm + "%";

Montag, 20. Juni 2011

JNDI - member of group - Real life example

Das Problem:

Bei der Suche im Active Directory sie die Anzahl der Ergebnisse künstlich beschränkt.
Dies ist ein Mechanismus im Active Directory um sich gegen zu große Ergebnismengen zu schützen. Dies ist verständlich, da das AD ein Herzstück der IT-Landschaft ist und auf keinen Fall überlastet werden darf. Es gibt jedoch sehr viele Organisationen die mehr als 1499 Mitarbeiter bzw. UserIds verwalten.

AD 2003 Liefert 1499 Datensätze zurück - Beim AD 2000 sind das 999.
Um die Übrigen Datensätze auszulesen muss man manuell Pagen.

Wenn man z.B. nach einer Gruppe mit mehr als 3000 Mitglieder such muss
man dies in 3 Etappen machen.

Als Antwort bekommt man nach der ersten Anfrage :"member;range=0-1499"
member;range=1500-2999
member;range=3000-*

Das Sternchen bedeutet, dass es keine weiteren Ergebnisse gibt.

Der nun folgende Code beschreibt kann als Lösung

package de.wwag.arch.portal.service.benutzer.ads;
/**
* range.java
* December 2004
* Sample JNDI application to demonstrate range retrieval
*
*/

import java.util.Hashtable;

import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import javax.naming.ldap.InitialLdapContext;
import javax.naming.ldap.LdapContext;

//if we were going to do this properly, should check that the server supports
//the range retrieval control 1.2.840.113556.1.4.802 !

public class range {
public static void main (String[] args) {

Hashtable<String,String> hashtable = new Hashtable<String,String>();

String serverName = "domainname";

String provider_url = "ldap://" + serverName + ":389";

String ldapuser = "domainname\\userid";
String ldappasswd = "passwd";


hashtable.put("java.naming.factory.initial",
"com.sun.jndi.ldap.LdapCtxFactory");
hashtable.put("java.naming.provider.url", provider_url);
hashtable.put("java.naming.ldap.version", "3");
hashtable.put("java.naming.security.authentication", "simple");
hashtable.put("java.naming.security.principal", ldapuser);
hashtable.put("java.naming.security.credentials", ldappasswd);



try {

// Create the initial directory context
LdapContext ctx = new InitialLdapContext(hashtable,null);

// Create the search controls
SearchControls searchCtls = new SearchControls();

//Specify the search scope
searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);

//specify the LDAP search filter
String searchFilter = "(&(objectClass=group)(CN=GAP_ALMA_BENUTZER_PROD))";

//Specify the Base for the search
String searchBase = "dc=ww-intern,dc=de";

//initialize counter to total the group members and range values
int totalResults = 0;
int Start = 0;
int Step = 1500;
int Finish = 1499;
boolean Finished = false;
String Range;

//loop through the query until we have all the results
while (!Finished) {

//Specify the attributes to return
Range = Start + "-" + Finish;
String returnedAtts[]={"member;Range=" + Range};
searchCtls.setReturningAttributes(returnedAtts);

//Search for objects using the filter
NamingEnumeration answer = ctx.search(searchBase, searchFilter, searchCtls);

//Loop through the search results
while (answer.hasMoreElements()) {
SearchResult sr = (SearchResult)answer.next();

System.out.println(">>>" + sr.getName());

//Print out the members

Attributes attrs = sr.getAttributes();
if (attrs != null) {

try {
for (NamingEnumeration ae = attrs.getAll();ae.hasMore();) {
Attribute attr = (Attribute)ae.next();

//check if we are finished
if (attr.getID().endsWith("*")) {
Finished=true;
}

System.out.println("Attribute: " + attr.getID());
for (NamingEnumeration e = attr.getAll();e.hasMore();totalResults++) {

System.out.println(" " + totalResults + ". " + e.next());
}
}

}
catch (NamingException e) {
System.err.println("Problem printing attributes: " + e);
}

Start = Start + Step;
Finish = Finish + Step;

}
}
}

System.out.println("Total members: " + totalResults);
ctx.close();

}
catch (NamingException e) {
System.err.println("Problem searching directory: " + e);
}
}
}

Freitag, 11. Februar 2011

Bitte warten Seite

Idee... Anzeigen einer Bitte warten Seite - Bis der eigentliche Content geladen wurde.
Dann die Seite ersetzten.- Ja - Das ist möglich....

In dieseem Beispiel wurde ein asynchroner Ajax-Aufruf gemacht.
Die Umsetzung erfolgt mittels jQuerey.
Das Ergebnis des Aufrufs ersetzt den aktuellen Inhalt.
Die 3 zentralen methoden sind:

  • document.open()

  • document.write('neuer Inhalt')

  • document.close





 <script type='text/javascript'>
$(document).ready(
function() {
$.ajax({
url: "test.php",
success: function(data, textStatus, jqXHR) { document.open();
document.write(data); document.close();
},
error: function(jqXHR, textStatus, errorThrown) {alert(textStatus);},
statusCode: {404: function() {
alert('page not found');
}
}
});
}); // Ende Ende ready()
</script>

Dienstag, 25. Januar 2011

html zentriertes Design

Moderne Anwendungen arbeiten oft mit einem zentrierten Layout.
Wie läßt sicht die Komponente in einer html-Seite zentrieren?

Man führt eine Hauptelement ein, und tut dieses zentrieren.
Das Hauptelement kann ein DIV eine TABLE oder ein sonstiges Element sein,
in dem sich unterelmenten einfügen lassen.

Das Zentrieren wird mit der 2 Style-Eingenschaften gemacht: margin-left:auto und margin-right:auto.


 <body>  
<table style="margin-left:auto; margin-right: auto;">
...
</table>
</body>


Bei divs nicht vergessen die Breite mit anzugeben.

 <body>   
<div style="margin-left:auto; margin-right: auto;width: 800px">
...
</div>
</body>

Montag, 24. Januar 2011

Einbau von Firebug-Light in den IE

Um den IE zu debuggen wünscht man sich bisweilen Zugriff auf den Firebug zu haben.
Leider ist dies nicht der Fall.
Außerdem kann man in größren Unternehmen auch kein weiteren Tools zum Debuggen in den Browser einbauen.
Die aktuelle Beschreibung zeigt wie man den Firebug-Light aktiviert.
Hierzu muß der unten beschriebene Code in die URL-Zeile des IE gepasted werden.
Nach betätigen von Enter ist der Firebug aktiviert.
Fertig.




javascript:(function(F,i,r,e,b,u,g,L,I,T,E){if(F.getElementById(b))return;E=F[i+'NS']&&F.documentElement.namespaceURI;E=E?F[i+'NS'](E,'script'):F[i]('script');E[r]('id',b);E[r]('src',I+g+T);E[r](b,u);(F[e]('head')[0]||F[e]('body')[0]).appendChild(E);E=new%20Image;E[r]('src',I+L);})(document,'createElement','setAttribute','getElementsByTagName','FirebugLite','4','firebug-lite.js','releases/lite/latest/skin/xp/sprite.png','https://getfirebug.com/','#startOpened');

Dienstag, 18. Januar 2011

jQuery Validation- und Formplugin






Die 2 jQuery-Plugins lassen sich sehr gut miteinander kombinieren.
Die Anforderung war folgende:
  • Valierung auf dem Client -> Validationplugin
  • Übermittlung des Formulars im Hintergrund (AJAX) -> Formplugin
  • So wenig Code wie möglich selber implementieren.

Beide Plugins sind für sicht genommen schon sehr genial.
Das Validationplugin gehört zu den wirklich guten plugins und läßt sich super schnell in die Seite einbauen.

Das nun Folgende Codeschnipsel wurde für Portlets in Form einer JSP konzipiert.
Eine verwendung innerhalb e
<script type="text/javascript" src="${pageContext.request.contextPath}/img/jquery.js"></script>
<script type="text/javascript" src="${pageContext.request.contextPath}/img/jquery.validate.js"></script>
<script type="text/javascript" src="${pageContext.request.contextPath}/img/jquery.form.js"></script>



function validateGutachtenForm() {
$('#gutachtenFormular').validate({
rules: {
kosteneinsparung: "required",
zeiteinsparung: "required"
}, //Ende Regeln
messages: {
},
errorPlacement: function(error, element) {
if ( element.is(":radio") || element.is(":checkbox")) {
error.appendTo( element.parent());
} else {
error.insertAfter(element);
}
},
submitHandler: function(form) {
var options = {
success: showResponse // post-submit callback
};
$(form).ajaxSubmit(options);
}
}); // Ende validate
}




// post-submit callback
function showResponse(responseText, statusText, xhr, $form) {
alert('status: ' + statusText + '\n\nresponseText: \n' + responseText +
'\n\nThe output div should have already been updated with the responseText.');
}



Es gibt verschieden Arten von Buttons in HTML.
An dieser Stelle wir explizit kein Submit-Button verwendet ()
Sondern der button-Tag. Durch betätigen dieses Buttons wird die valierungsMethode aufgerufen.

<button onclick="validateGutachtenForm()">Senden</button>



jQuery erfindet an dieser Stelle das Rad nicht neu, sondern verwendet das class-Attribut
und das title-Attribut.


<input type='radio' name='kosteneinsparung' value='ns'
class='required' title='Wählen Sie eine Option' /> Nicht schätzbar
<input type='radio' name='kosteneinsparung' value='gering' /> Gering
<input type='radio' name='kosteneinsparung' value='mittel' /> Mittel
<input type='radio' name='kosteneinsparung' value='hoch' /> Hoch






Commons Fileupload und Portlet 286

Wir konnten die die Commons-Fileupload lib von Apache nicht in unserer Anwendung verwenden, da die serverRessource-Methode des Porlet286-Standards nicht unterstüzt wird.
Wir haben die Klassen aber fast 1:1 nachgebaut, und um die relevanten Stellen umgeschrieben. Theoretisch könnten wir den Code jetzt contributen, so das Apache Commons-Fileupload dies zukünftig unterstüzten könnte.

Dienstag, 23. November 2010

Ändern von Comboboxen (SELECT-Tag) über innerHTML

In der Ajax-Welt kommt es häufig vor, dass man die Werte von Comboboxen (also die Options innerhalb eines Select-Tags) dynamisch ändern möchte. Z.B. bei einer Fahrzeugsuche. In der Combobox Marke wird VW ausgewählt. Abhängig davon soll die Combobox Typ mit Golf, Polo, Passat usw. gefüllt werden.

Eine Möglichkeit besteht in der Manipulation des DOMs. Diese Möglichkeit ist aber vor allem im Internet Explorer (IE) sehr imperformant. In den meisten Fällen ist es besser, das HTML, das das Ergebnis der DOM-Operationen sein würde, "von Hand" zu erzeugen und per innerHTML dem Element zu verpassen. Das funktioniert auch in Browsern einwandfrei. Im IE dagegen weit gefehlt.

Der IE hat beim Select-Tag einen Bug (zumindest bis zur Version 7). Wird das Innere eines Select-Tags über innerHTML gesetzt, ist der IE davon unbeeindruckt. Er zeigt weiterhin die alten Einträge in der Combobox.

Hier kommt die IE-spezifische Eigenschaft outerHTML ins Spiel. Verwendet man outerHTML, zeigt der IE die Änderungen nach dem Aufruf auch an.

outerHTML hat zwei unangenehme Nebenwirkungen:


  • Der Select-Tag selbst ist auch enthalten. D.h., er muss vollständig im manipulierten String enthalten sein.

  • Nach dem Zuweisen von outerHTML wird im DOM eine neue Instanz erzeugt. Um auf das geänderte Element nochmals zuzugreifen, muss es über document.getElementById() wieder geholt werden.



Hier ein Bsp. für die Befüllung eines Select-Tags:


var select = document.getElementById("mySelect");
var num = select.options.length + 1;
select.innerHTML = "";
var innerHTML = "";
// innerHTML erzeugen
innerHTML = "<option value=\"1\">Golf"</option>"
+ "<option value=\"2\">Polo"</option>"
+ "<option value=\"3\">Passat"</option>";

if (isIe()) {
// nur für IE

var indexOfClosingTag = select.outerHTML.toUpperCase().lastIndexOf("</select>");
var outerHTML = select.outerHTML.substring(0, indexOfClosingTag) + innerHTML + select.outerHTML.substring(indexOfClosingTag);
select.outerHTML = outerHTML;

select = document.getElementById(select.id);
} else {
// nur für Browser
select.innerHTML = innerHTML;
}
select.styles.display = "";




Der grün markierte Code setzt das erzeugte innerHTML in die vorhandenen Select-Tags ein.

Damit die Zeile select.styles.display = ""; ausgeführt werden kann, muss vorher die neu erzeugt Instanz des Select-Tags aus dem DOM zurückgeholt werden (rote Zeile).

Mittwoch, 22. September 2010

Browser Quirk - Deselect Radiobuttons





Wir haben 2 Gruppen von Radiobuttons.
Wenn die erste Gruppe auf Nein gestellt ist, so muss die zweite Gruppe deselectiert werden. Hierzu gibt es gleich mehrer Dinge zu beachten. Da sich der IE und der FF in diesem Punkt voneinander unterschiedlich verhalten

  1. Die Buttons (Gruppe 1) benötigen unbeding onclick="this.blur()" . Damit der Internetexplorer auf die Veränderung reagiert.

  2. Fehlerhafte indexierung der Buttons im IE.





Definition der steuernden Buttons.
<input type="radio" onclick="this.blur()"
onchange="de_wwag_alma_anliegen_versicherungsfallchanged(this.value)"
value="true"
name="editForm:versicherungsfallJaNeinRadio">

<input type="radio" onclick="this.blur()"
onchange="de_wwag_alma_anliegen_versicherungsfallchanged(this.value)"
value="false"
name=":editForm:versicherungsfallJaNeinRadio" >



Deselektieren der abhängigen Buttons.

document.getElementsByName("view<portlet:namespace/>:editForm:versicherungsfallAltNeuRadio")[0].checked = false;
//This is a Browser Quirk - we only have 2 option
//In FF the indexes are 0 and 1
//In IE 0 and 2
document.getElementsByName("editForm:versicherungsfallAltNeuRadio")[0].checked = false;
document.getElementsByName("editForm:versicherungsfallAltNeuRadio")[1].checked = false;
document.getElementsByName("editForm:versicherungsfallAltNeuRadio")[2].checked = false;

Donnerstag, 9. September 2010

Tomcat Authentication: Wie zeige ich den Grund für einen Authentifizierungsfehler an?

Im Tomcat bietet es sich an, die J2EE Authentifizierung für geschützte Resourcen zu verwenden. Das Verfahren ist recht simpel einzurichten und wird auf vielen Seiten im Internet erklärt, z.B. hier.

Diese Einfache Methode der Authentifizierung leitet den User auf eine Login-Seite, falls er eine geschützte Resource aufrufen möchte, aber noch nicht angemeldet ist. Auf dieser Login-Seite wird ein Formular angezeigt, in das er Username und Passwort eingeben muss.


Schlägt die Authentifizierung aus irgend einem Grund fehl, wird der User auf die in der web.xml eingestellte Error-Seite weitergeleitet. In den meisten Anwendungen ist die Error-Seite die Login-Seite mit einem Hinweistext, dass die Authentifizierung nicht möglich war. Manchmal wird noch ein Link angeboten, über den der User sein Passwort zurücksetzen kann.

Bsp:


Das Problem ist jedoch, dass es auch andere Gründe für eine fehlgeschlagene Anmeldung geben kann, außer dass Username oder Passwort falsch eingegeben wurden:

  • Passwort ist abgelaufen.

  • Passwort wurde zu oft falsch eingegeben.

  • Konto wurde wegen Inaktivität gesperrt.

  • u.s.w.


Diese Informationen können dem User jedoch bei Verwendung der Standard-Authentifizierung nicht angezeigt werden.

Abhilfe schafft hier die Implementierung eines eigenen Valves zur Form-Authentifizierung. Die Klasse muss von org.apache.catalina.authenticator.FormAuthenticator abgeleitet werden (ganz richtig, das ist eine Tomcat-spezifische Klasse!). In dieser Klasse wird die Methode forwardToErrorPage() überschrieben. Die Methode wird vom Tomcat aufgerufen, nachdem die Authentifizierung fehlschlug und der User zur Error-Seite geleitet wird.

Eine Beispiel-Implementierung:

public class CustomFormAuthenticator extends FormAuthenticator {

@Override
protected void forwardToErrorPage(Request request, Response response,
LoginConfig config) throws IOException {
Integer errorReason = checkOutErrorReason();

request.setAttribute("loginErrorReason", errorReason);
super.forwardToErrorPage(request, response, config);
}

protected void checkOutErrorReason() {
//TODO hier die Ermittlung der Ursache für die
// abgelehnte Authentifizierung implementieren
return null;
}

}

In diesem Beispiel wird ein Request-Parameter gesetzt (loginErrorReason), der dann in der Error-Page ausgewertet werden kann.
Bsp.:

Integer errorReason = (Integer) request.getAttribute("loginErrorReason");


Wichtig ist, dass der CustomFormAuthenticator in einem jar im Tomcat/lib-Verzeichnis deployt wird, damit der Tomcat die Klasse findet.

Das Valve muss dann dem Tomcat noch in der Konfiguration im Context der Web-Anwendung (in server.xml) mitgeteilt werden:

...
<Context docBase="myapp" path="" reloadable="true">
<Valve className="custom.auth.CustomFormAuthenticator" />
</Context>
...


Eine andere Möglichkeit der Konfiguration besteht in der Web-Anwendung in der Datei META-INF/context.xml:

<Context>
<Valve className="custom.auth.CustomFormAuthenticator" />
</Context>