Sehr große Anfrage an den JBuilder

In meiner Linksammlung sitzt http://www.borland.com/jbuilder, was mit der Browser nun beantwortet mit:

 

Server Error in ‚/‘ Application.


Server Too Busy

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.Web.HttpException: Server Too Busy
Source Error:

An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.

Stack Trace:

[HttpException (0x80004005): Server Too Busy]
System.Web.HttpRuntime.RejectRequestInternal(HttpWorkerRequest wr) +148


Version Information: Microsoft .NET Framework Version:1.1.4322.2300; ASP.NET Version:1.1.4322.2300

Entweder geht es dem Produkt wahnsinnig gut und jeder will den JBuilder kaufen/laden/… oder .NET ist so schlecht, dass es mit der Last nicht umgehen kann.

Warum catch ( Exception e ) schlecht ist, und was man dagegen tun kann

Eine ganze Reihe von Java-Methoden lösen eine ganze Reihe von unterschiedlichen Ausnahmen aus:

private void setLaf()

{

try

{

UIManager.setLookAndFeel( UIManager.getSystemLookAndFeelClassName() );

}

catch ( ClassNotFoundException e )

{

handle();

}

catch ( InstantiationException e )

{

handle();

}

catch ( IllegalAccessException e )

{

handle();

}

catch ( UnsupportedLookAndFeelException e )

{

handle();

}

}


Oftmals befindet sich dann im Quellecode eine Anweisung wie catch ( Exception e ), die große Kaskaden von catch-Anweisungen verkleinern soll. (So kann man auch den gleichen Handler verwenden.) Das Problem dabei: Man fängt mehr, als man will, insbesondere wertvolle RuntimeExceptions, die man ungern als checked SQL- oder sonstwas-Exeption vernudelt haben möchte. Wenn ich mir moderne Frameworks, wie Spring oder Java EE 5 anschaue, wird dort ausschließlich mit RuntimeException gearbeitet, und die fängt eine catch ( Exception e ) ebenfalls brutal ab. Wenn zum Beispiel das Framework aufgrund einer unchecked Exception eine Transaktion abbricht, wir diesen Fehler aber schon „weggefangen“ haben, ist das ein echtes Problem.

Ich sehe für eine vernünftige Behandlung mehrere Möglichkeiten:

  • Warten auf Java 3000, da dort sicherlich so etwas wie catch ( IOException, SQLException e ) definiert wird.
  • Eine gemeinsame handle()-Funktion zu definieren, die man dann aus den catch-Handlern ansteuert.
  • Ein catch ( Exception e ) und dann ein instanceof Test auf die erwarteten Exceptions mit einer re-thow, wenn der Typ nicht bekannt ist.

Schlechter Quellcode von NeuralBuild (NeuralLimits)

Über den Artikel http://entwickler.com/itr/news/psecom,neu,1,id,39010,nodeid,82.html bin ich auf einen Generator aufmerksam geworden, der automatisch DOAs und Transfer-Objekte erzeugt. Leider ist doch der generierte Quellcode wenig überzeugend. En Besipiel:

    public Integer insertCompany(Company criteriaCompany)
throws NeuralBuildJavaSampleDAOInsertException {
Integer keyVal = new Integer(0);
con = connMgr.getConnection("neuralbuildjavasample");
int setindex = 1;
String vSQL = constructInsertPreparedStatement(criteriaCompany);
try {
PreparedStatement ps = con.prepareStatement(vSQL,
PreparedStatement.RETURN_GENERATED_KEYS);
if (criteriaCompany.CompanyID_isSet) {
ps.setInt(setindex, criteriaCompany.getCompanyID().intValue());
setindex++;
}
if (criteriaCompany.CompanyName_isSet) {
ps.setDate(setindex,
(java.sql.Date) criteriaCompany.getCompanyName());
setindex++;
}
ps.executeUpdate();
ResultSet rs = ps.getGeneratedKeys();
if (rs.next()) {
keyVal = Integer.valueOf(rs.getObject(1).toString());
}
rs.close();
rs = null;
ps.close();
} catch (Exception e) {
connMgr.freeConnection("neuralbuildjavasample", con);
throw new NeuralBuildJavaSampleDAOInsertException("Insert Failed " +
e.getMessage(), e);
}
connMgr.freeConnection("neuralbuildjavasample", con);
return keyVal;
}
  • new Integer(0) wird immer gebaut, auch wenn es später überschrieben wird.
  • finally fehlt: So ist das close() nicht garantiert. (Nicht sooo schlimm, da bei Freigabe der Connection auch ResultSet und XXXStatement freigegeben werden.)
  • Quellcode-Duplizierung bei connMgr.freeConnection(„neuralbuildjavasample“, con);. Das gehört auf jeden Fall in finally{}
  • Was soll den rs = null; bewirken? Den GC hilt das bei lokalen Variablen nun wirklich nicht.
  • catch (Exception e) muss wirklich sein? Ode doch catch (SQLException e)?
  • Wenn die Rückgabe ein Integer-Objekt ist, warum dann „Integer.valueOf(rs.getObject(1).toString());“ schreiben? Das funktioniert zwar, aber wenn getObject() schon in Integer ist, muss man nicht erst in ein String und dann wieder zurück konvertieren.
  • Sollte man wirklich einfach nur 0 zurückliefern, wenn man keinen Schlüssel bekam?

Die anderen Methoden habe auch so ihre Macken, so mit der Namensgebung.

  • CachedRowSet Rs = null; Groß-/Kleinschreibung!
  • Gemische Groß-/Kleinschreibung ist Standard, nicht so bei setindex.
  • Unterstriche sind von Sun in der Namenskonvention nur bei Konstanten vorgesehen, aber nicht etwa bei CompanyID_isSet
  • Ist die Namensgebung so gut, wenn man liest
    ps.setDate(setindex, (java.sql.Date) criteriaCompany.getCompanyName() );
    Also setDate() auf ein Company-Name?
  • Die API-Doku sollte nicht schreiben: „Delete an object from the database.“ sondern laut Suns-JavaDoc-Vorgaben: „Deletes an object from the database.“

JAXB2-Annotationen an Beispielen

Schreiben der Felder statt Nutzen der Setter/Getter: Annotiere die Klasse mit

@XmlAccessorType( XmlAccessType.FIELD )

 

@XmlAttribute

 

class Person

{

  String name;

 

  @XmlAttribute

  int id;

}

<person id=“123″>

    <name>Christian</name>

</person>

 

@XmlValue

Gibt es nur ein Element kann @XmlValue dies in den Rumpf setzten.

 

class Person

{

  int id;

}

<person>

    <id>123</id>

</person>

class Person

{

  @XmlValue int id;

}

<person>123</person>

 

@Transient

Nimmt Element aus der XML-Abbildung aus.

 

class Person

{

  @XmlTransient int id;

 

  String firstname;

  String lastname;

}

<person>

    <firstname>Christian</firstname>

    <lastname>Ullenboom</lastname>

</person>

 

@XmlList

Schreibt Elemente einer Sammlung nicht in einzelnen Elementen, sondern mit Leerzeichen getrennt.

 

class Person

{

  List<String> emails;

}

 

<person>

    <emails>muh@kuh.de</emails>

    <emails>zick@zack.com</emails>

</person>

 

class Person

{

  @XmlList

  List<String> emails;

}

<person>

    <emails>muh@kuh.de zick@zack.com</emails>

</person>

Elemente einpacken mit @XmlElementWrapper

 

class Person

{

  List<String> emails;

}

<person>

    <emails>muh@kuh.de</emails>

    <emails>zick@zack.com</emails>

</person>

class Person

{

  @XmlElementWrapper(name = „emails“)

  @XmlElement(name = „email“)

  List<String> emails;

}

<person>

    <emails>

        <email>muh@kuh.de</email>

        <email>zick@zack.com</email>

    </emails>

</person>

 

 

Reihenfolge ändern

 

class Person

{

  String lastname, firstname;

}

<person>

    <lastname>Ullenboom</lastname>

    <firstname>Christian</firstname>

</person>

@XmlType( propOrder = { „firstname“, „lastname“ } )

class Person

{

  String lastname, firstname;

}

<person>

    <firstname>Christian</firstname>

    <lastname>Ullenboom</lastname>

</person>

 

@XmlJavaTypeAdapter

Anpassen der XML-Abbildung, etwa für Datumswerte.

 

class Person

{

  Date birthday;

}

<person>

    <birthday>1973-03-12T00:00:00+01:00</birthday>

</person>

class Person

{

  @XmlJavaTypeAdapter( DateAdapter.class )

  Date birthday;

}

 

class DateAdapter extends XmlAdapter<String, Date>

{

  private final static DateFormat formatter = new SimpleDateFormat( „dd/MM/yyyy“ );

 

  public Date unmarshal( String date ) throws ParseException

  {

    return formatter.parse( date );

  }

 

  public String marshal( Date date )

  {

    return formatter.format( date );

  }

}

<person>

    <birthday>12/03/1973</birthday>

</person>

 

Der spezielle Datentyp XMLGregorianCalendar

 

Neben der Möglichkeit, Datumswerte mit einen XmlJavaTypeAdapter zu übersetzen, bietet JAXB den Datentype XMLGregorianCalendar.

 

Person p = new Person();

GregorianCalendar c = new GregorianCalendar( 1973, Calendar.MARCH, 12 );

p.birthday = DatatypeFactory.newInstance().newXMLGregorianCalendar( c );;

 
 

class Person

{

  XMLGregorianCalendar birthday;

}

<person>

    <birthday>1973-03-12T00:00:00.000+01:00</birthday>

</person>

 

Die Abbildung enthält alle notwendigen Werte. Sollen Segmente, wie die Zeitzone herausgenommen werden, so markiert man sie aus:

 

Person p = new Person();

GregorianCalendar c = new GregorianCalendar( 1973, Calendar.MARCH, 12 );

XMLGregorianCalendar gc = DatatypeFactory.newInstance().newXMLGregorianCalendar( c );

gc.setTimezone(DatatypeConstants.FIELD_UNDEFINED);

gc.setTime(DatatypeConstants.FIELD_UNDEFINED, DatatypeConstants.FIELD_UNDEFINED, DatatypeConstants.FIELD_UNDEFINED);

p.birthday = gc;
 

class Person

{

  XMLGregorianCalendar birthday;

}

<person>

    <birthday>1973-03-12</birthday>

</person>

 

Elemente einbetten mit @XmlElements

 

Person p = new Person();

EmailContact c1 = new EmailContact();

c1.address = „hoho@weihnachtsmann.de“;

PhoneContact c2 = new PhoneContact();

c2.number = „0011-123456789“;

p.contacts = Arrays.asList( c1, c2 );

 

@XmlRootElement

@XmlAccessorType( XmlAccessType.FIELD )

class Person

{

  @XmlElements(

  {

      @XmlElement( name = „email“, type = EmailContact.class ),

      @XmlElement( name = „phone“, type = PhoneContact.class )

  } )

  List<Object> contacts = new ArrayList<Object>();

}

 

@XmlAccessorType( XmlAccessType.FIELD )

class EmailContact

{

  @XmlValue

  String address;

}

 

@XmlAccessorType( XmlAccessType.FIELD )

class PhoneContact

{

  @XmlValue

  String number;

}

<person>

    <email>hoho@weihnachtsmann.de</email>

    <phone>0011-123456789</phone>

</person>

JNotify: Was tut sich im Dateisystem?

Die kleine (native) unter Linux und Windows arbeitende Bibliothek JNotify meldet Änderungen an Dateien. Ein Beispiel:

String path = "c:/";

int mask = JNotify.FILE_CREATED | JNotify.FILE_DELETED | JNotify.FILE_MODIFIED | JNotify.FILE_RENAMED;


boolean watchSubtree = true;


int watchID = JNotify.addWatch(path, mask, watchSubtree, new JNotifyListener()
{
@Override
public void fileCreated( int arg0, String arg1, String arg2 )
{
System.out.println( "created" );
}


@Override
public void fileDeleted( int arg0, String arg1, String arg2 )
{
System.out.println( "deleted" );
}

@Override
public void fileModified( int arg0, String arg1, String arg2 )
{
System.out.println( "modified" );
}

@Override
public void fileRenamed( int arg0, String arg1, String arg2, String arg3 )
{
System.out.println( "renamed" );
}
} );

Und am Ende:

JNotify.removeWatch(watchID);

jodconverter für OpenOffice Dateiformatkonvertierungen

Der zweite Schritt meiner Rechnungserstellung ist die Konvertierung in PDF. Auch dazu gibt es eine prima Bibliothek: http://www.artofsolving.com/opensource/jodconverter. (Einziger Nachteil: viele Jars.) Damit kann man Word, PowerPoint, RTF und alles andere in ein bliebiges Zielformat bringen.

private static void ensureStartedOpenOfficeService()
{
try
{
new Socket( "127.0.0.1", 8100 );
}
catch ( Exception e )
{
String path = "C:/Programme/OpenOffice.org 2.3/program/";
ProcessBuilder processBuilder = new ProcessBuilder( path+"soffice", "-headless", "-accept=\"socket,host=127.0.0.1,port=8100;urp;\"", "-nofirststartwizard" );
try
{
processBuilder.start();
}
catch ( IOException ioe )
{
throw new RuntimeException( ioe );
}
}
}
public static void convert( String source, String destination )
{
ensureStartedOpenOfficeService();

OpenOfficeConnection connection = null;
try
{
connection = new SocketOpenOfficeConnection( 8100 );
connection.connect();
DocumentConverter converter = new OpenOfficeDocumentConverter( connection );
File inputFile = new File( source );
File outputFile = new File( destination );
converter.convert( inputFile, outputFile );
}
catch ( ConnectException e )
{
throw new RuntimeException( e );
}
finally
{
connection.disconnect();
}
}

Für meine Rechnungen also:

String destination    = "S:/Private/Traida/Bills/bill1";
String destinationOds = destination + ".ods";
String destinationPdf = destination + ".pdf";
convert( destinationOds, destinationPdf );

OpenOffice als Template-Engine: Dokument einlesen, verändern, schreiben

Da ich meine Rechnungen automatisch generiert und als PDF konvertiert haben möchte, habe nach einer Lösung gesucht, wie ich die Rechnungsdaten aus einer Datenquelle in der Template einsetzten kann, und am Ende eine PDF bekomme. Klar sind Report-Generatoren dafür auf der Welt, aber meine Vorlagen möchte ich nicht für Eclipse BIRT oder Jasper schreiben, sondern in Word bzw. OpenOffice. RTF ist relativ leicht zu schreiben und für ein Template meines Erachtens ganz gut. Meine Gedanken kreisten daher einige Zeit um RTF->PDF, doch da gibt es keine freie Lösung. Auch Wege wie RTF->FO->PDF sind möglich, aber dafür gibt es ebenfalls keine leichtgewichtigen freien Lösungen.

Schon für meinen PowerPoint->PDF-Konverter habe ich mit OpenOffice gearbeitet und das klappte ganz gut. Das habe ich für meine Templates nun wieder überlegt — ein recht harter Weg für simple RTFs zwar, aber es funktioniert. Doch anstatt RTF zu nutzen, wollte ich gleich das XML-Format verwenden. Dazu muss man wissen, dass OO ein Zip-Archiv für das OO-Dokument vorsieht und dort in einer XML-Datei den Content ablegt. Mit der großartigen Open-Source-Bibliothek https://truezip.dev.java.net/ ist der Zugriff auf Archive sehr einfach.

Mit TrueZIP ist eine einfache Lösung entstanden, ein OO-Dokument mit Template-Anweisungen wie ${address} zu lesen, Ersetzungen vorzunehmen, und alles wieder zu schreiben. Diesen Text hier zu schreiben hat länger gedauert, als die 90 Zeilen Quellcode. Also los:


import java.io.*;
import java.nio.channels.FileChannel;
import de.schlichtherle.io.ArchiveDetector;

public class OpenOfficeUtils
{
public static void main( String[] args )
{
String source = "S:/Private/Traida/Bills/template.ods";
String destination = "S:/Private/Traida/Bills/bill1.ods";

copyFile( source, destination );

String content = readOpenOfficeContent( source );

content = content.replace( "${addressline1}", "Christian Ullenboom" );

writeOpenOfficeContent( destination, content );
}

public static String readOpenOfficeContent( String filename )
{
Reader is = null;

try
{
de.schlichtherle.io.File file = new de.schlichtherle.io.File( filename + "/content.xml", ArchiveDetector.ALL );
char[] fileContent = new char[ (int) file.length() ];
is = new de.schlichtherle.io.FileReader( file ); // TODO: <?xml version="1.0" encoding="UTF-8"?>
is.read( fileContent );

return new String(fileContent);
}
catch ( IOException e )
{
throw new IllegalArgumentException( e );
}
finally
{
try { is.close(); } catch ( Exception e ) { }
}
}

public static void writeOpenOfficeContent( String filename, String content )
{
Writer os = null;

try
{
de.schlichtherle.io.File file = new de.schlichtherle.io.File( filename + "/content.xml", ArchiveDetector.ALL );
os = new de.schlichtherle.io.FileWriter( file );
os.write( content );
}
catch ( IOException e )
{
throw new IllegalArgumentException( e );
}
finally
{
try { os.close(); } catch ( Exception e ) { }
}
}

public static void copyFile( String in, String out )
{
FileChannel inChannel = null;
FileChannel outChannel = null;

try
{
inChannel = new FileInputStream( new File(in) ).getChannel();
outChannel = new FileOutputStream( new File(out) ).getChannel();
inChannel.transferTo( 0, inChannel.size(), outChannel );
}
catch ( IOException e )
{
throw new IllegalArgumentException( e );
}
finally
{
try { inChannel.close(); } catch ( Exception e ) { }
try { outChannel.close(); } catch ( Exception e ) { }
}
}
}

Strg+F11: Noch so eine komische Eclipse-Änderung

Vor nicht allzulanger Zeit startete Eclipse mit Strg+F11 das letzte Programm. Dann änderte man dies und Eclipse versuchte automatisch das im Editor aktive Programm zu starten. Ich denke, dass diese Umstellung eher lästig ist und eines der ersten Sachen sein dürfte, die man abschaltet. Das geht so: In den Preferences unter Run/Debug > Launching den Schalter bei Always launch the … einstellen.

image

1 Stunde Suchen für eine doofe Eclipse-Einstellung

<Sarkasmus>Dankenswerterweise</Sarkasmus> hat Eclipse eine neue Einstellung bekommen, in dem gewissen Daten vom Kopieren in den Output-Folder ausgenommen werden können. Dass sich diese Einstellung unter Java > Compiler > Building befindet, fand ich erst nach langem Suchen hier: http://www.codecommit.com/blog/eclipse/wtps-crazy-and-undocumented-setting-change

wtp-screwup

Mein Problem: Bei der Entwicklung mit Wicket stehen die HTML-Seiten Seite an Seite mit den Java-Klassen. In den classes-Folder für die Web-Anwendung gingen aber nur die Klassen und nicht die HTML-Dateien. Da gestern auf einer anderen Eclipse-Version alles noch lief, war ich heute lange verzweifelt und kann glücklicherweise nach dieser Einstellung wieder an die Arbeit gehen.

SQL für Objekte: JoSQL (SQL for Java Objects)

JoSQL (SQL for Java Objects) unter http://josql.sourceforge.net/index.html ist eine Open-Source Biblitohek, um SQL-Anfragen an Objektgrafen zu stellen. Die Webseite gibt interessante Beispiele.

String query = "SELECT * FROM   java.io.File WHERE  name LIKE '%.mp3'";
Query q = new Query();
q.parse( query );
QueryResults results = q.execute( Arrays.asList( new File("C:/Data/Musik/").listFiles() ) );
System.out.println( results.getResults() );

Oder für Ausdrücke:

/*
* Use as a file filter.
*/
String exp = "lastModified BETWEEN toDate('10/May/2007') AND toDate('28/Jun/2007') " +
"AND " +
"length >= 10 * 1024" +
"AND " +
"path LIKE '%/subdir/%'";

ExpressionEvaluator ee = new ExpressionEvaluator (exp, File.class);

if (ee.isTrue (myfile))
{
// Process the file.
}

Buchkritik: Das Java Codebook

Marcus Wiederstein, Marco Skulschus, Mark Donnermeyer, Dirk Brodersen, Benjamin Rusch. Addison-Wesley. ISBN 3-8273-2059-3. 2003. 858 Seiten
Programmabschnitte, sog. Snippets, gibt es im Internet genug (wir wäre es alternativ mit dem Java-Almanac?) und wer eine Suchmaschine bedienen kann, wird immer, ja immer, die Bibliothek oder den Quellcode-Schnippsel finden, die er sucht. Um so erstaunlicher finde ich es, wenn Verlage Quellcode-Kochbücher veröffentlichen, ob sie nun Codebook oder Cookbook heißen. Ernsthaft: Wer eine Klasse zur Bruchrechnung sucht oder ein Beispielprogramm zum Aufbau eines Swing-Fensters, geht sicherlich nicht zum Regal, sucht im Index nach „Bruch“ und holt sich dann die Klasse von der CD. Daher die Frage – ist ein Buch wie „Das Java Codebook“ notwendig? Das Positive zuerst: Die Kapitel über reguläre Ausdrücke und Applets (Daten im Cookie speichern, Uploads, Applet-Konverter für Tags), die kleinen Beispielprogramme, in denen man etwa über ein BufferedImageOp.filter Bilder heller/dunkler macht, sowie Hinweise zur Transparenz und der Index finde ich prima. Eigentlich sind Bücher mit Quellcode gut, denn Software-Entwickler lesen im Allgemeinen zu wenig. Das Studium von Quellcode hilft jedem Entwickler, neue Idiome zu lernen und seine Fähigkeiten zu verbessern. Je nach Schwerpunkt des Buches lernt man weitere Möglichkeiten der Standardbibliothek kennen oder neue Open-Source-Bibliotheken. Soweit die Möglichkeiten guter Codebooks. Leider hat das Java Codebook dann doch die eine oder andere Schwachstelle, so dass man Software-Entwicklern vielleicht doch besser direkt zur Google-Suche raten sollte. (Immerhin gibt es das Buch als PDF.) Die Probleme im Detail: Die Klasse

public class CalcExample {
static final BigDecimal ZERO = new BigDecimal(0);
static final BigDecimal ONE = new BigDecimal(1);
static final BigDecimal FOUR = new BigDecimal(4);

deklariert drei Variablen (Sichtbarkeit könnte auch anders sein), doch vergisst, dass es ZERO, ONE, TEN schon in BigDecimal gibt. In anderen Beispielen gefällt mir die Schreibweise der Felder nicht. Einiges ist nach der Bauart String []array, also Typ, Leerzeichen, Variablenname aufgebaut. Das habe ich nach 11 Jahren Java hier zum ersten Mal gelesen. Zumal sich die Autoren hier uneins zu sein scheinen, denn sie verwenden auch String[]. Die fünf Autoren des Buchs folgen demnach keiner einheitlichen Coding-Konvention. Ein weiteres Thema betrifft die Methodennamen: Überwiegend werden englische Bezeichner (setNumber, less100, …) gebraucht, doch lassen sich innerhalb eines Beispiels Vermischungen mit deutschen Variablennamen entdecken, wie das Feld ZEHN. Einige Autoren mögen (immer noch) die Klassen Hashtable und Vector, andere nutzen doch besser List und Map – hat denn keiner der Autoren die anderen Kapitel gelesen und kommentiert? Bei den Datenstrukturen frage ich mich wirklich, was denn arrayList.add(new String(new java.util.Date().toString())); für eine Anweisung sein soll. Und warum wird ein Stack noch mal neu implementiert? OK, der Original-Stack erbt von Vector, was vom Design total verpfuscht wurde, aber dann könnte man doch immer noch den neuen Stack so implementieren, das er an den alten Stack delegiert, anstatt alle Operationen komplett mit einer ArrayList neu zu implementieren. (Und dann noch die fragwürdige Methode search() zu übernehmen.) Eine weitere Schwachstelle, die ich in der Insel weitestgehend versuche zu vermieden, sind Windows-spezifische Pfadangaben, wie etwa bei new FileOutputStream(„c:\\buecher.ser“). Bei IO-Beispielen fehlt oft das close() im finally. Vor allem bei HTTP-Servern ist das kritisch. Im JSP-Kapitel findet sich plötzlich ein JDBC-Close im finally; also hat es nicht jeder der Autoren vergessen. Frage: Was gibt dieses Programm bei Argumenten auf der Kommandozeile aus?

package javacodebook.io.stdout;
public class StdOut {
public static void main(String[] args) {
String text = "Hallo Welt";
//Wenn Text angegeben, dann diesen ausgeben
if(args.length < 1)
for(int i = 0; i < args.length; i++)
System.out.println(args[i]);
else
System.out.println(text);
}
}

Dass ist wohl eher eine Zertifizierungsfrage statt eines sinnvollen Codebeispiels, denn ist die Anzahl der Argumente kleiner 1, gibt es keine Argumente und die Schleife ist nutzlos. Genauso falsch ist der Satz „JDBC benutzt dabei die Interfaces Connection, Driver und DriverManager.“ Schade, denn DriverManager ist eine Klasse. Sollte es auch, denn aus dieser Zentrale erhält man durch die statische Methode getConnection() eine Verbindung. Im Anhang finden sich dann noch ein paar Dreher in der Groß-/Kleinschreibung. Z. B. beginnen einige Bezeichner in der Tabelle „Java Native Interface Typen“ mit Großbuchstaben. Besser wären „int“ statt „Int“ sowie „void“ statt „Void“ usw. Ähnliches gilt für JscrollPane, was zu JScrollPane werden sollte. Unter „Relationale Operatoren“ findet sich folgender Satz bei ==-Operator: „a == b ergibt true, wenn a gleich b ist. Handelt es sich bei a und b um Referenztypen, so ist der Rückgabewert dann true, wenn beide auf dasselbe Objekt zeigen.“ Wir sollten den Sonderfall a = b = NaN aber nicht Außeracht lassen, denn gerade dann ist a = b, aber das Ergebnis nicht true. In der Tabelle „Klassenobjekte für den primitiven Datentyp“ sollte Void besser nicht auftauchen, da es sich nicht um einen primitiven Datentyp handelt. Oder Void stehen lassen, aber die Überschrift anpassen. Einige Kritikpunkte betreffen die Auswahl der Klassen. Die Klasse Fraction sollte vielleicht besser einer bekannten Open-Source-Bibliothek entstammen und die Matrizen sowie der umfangreiche Quellcode nicht unbedingt abgebildet werden. Für CSV-Dateien wären populäre Standard-Klassen sinnvoller. Der Tipp 186 für Timer sollte diesen nicht selbst implementieren, sondern die zwei (!) Timer-Klassen der Java-Lib vorstellen. Da ein Codebook grundsätzlich eine unendliche Sache ist, könnten weitere Snippets eingebracht werden. Bei den Zufallszahlen wäre z. B. SecureRandom gut untergebracht. Das Beispiel FindInFile, was händisch nach einem String in einer Datei sucht, lässt sich viel effektiver mit NIO (mit Standard-Beispiel von Sun) lösen. Das NIO-Kapitel kommt mit nur zwei Beispielen daher (Datei kopieren und File-Lock). Das ist eine magere Ausbeute. Später kommt noch ein NIO-Server dazu. Der Verweis auf eine einfache NIO-Bibliothek (wie xSocket) wäre dann wünschenswert. Um ein Fenster mittig zu positionieren, muss man nicht rechnen, hier reicht eine Java-Funktion. Wenn es sich schon um ein Java 1.4 Buch handelt, dann sollte ImageIO Grafiken laden. Die Swing-Beispiele sind relativ unspektakulär. Hier wäre es wohl sinnvoller statt „wie erzeuge ich einen Button“ oder „wie etwas mehr mit den Modellen zu machen“, einen Dateibaum in ein JTree zu setzen. Und stilistisch wertvoll ist der Hinweis mit der JDBC-Verbindung in einem JSP-Tag sicher nicht. Und wo sind die DAOs? Bei JMS stimmt es zwar grundsätzlich, dass in einer Tabelle bei Programmiersprachenunabhängigkeit „Nein“ steht und bei HTTP „Ja“, doch allgemeingültig ist das nicht. Für ActiveMQ gibt es ein standardisiertes Protokollformat (OpenWire) und APIs für diverse andere Programmiersprachen. Das gleiche gilt auch für IBMs WebSphere MQ (früher MQSeries) – sicherlich eines der besten MOMs -, das heterogene Plattformen verbindet. Einen XML-Parser über die Anweisung org.apache.xerces.parsers.DOMParser parser = new org.apache.xerces.parsers.DOMParser(); statt über JAXP zu erfragen ist nicht schön sondern fast grob fahrlässig. Zudem haben auch die JAXP-Parser Features, obwohl die String-Kennungen dann immer ein wenig anders aussehen. Zu Tipp 196: Wann immer man sich mit getGraphics() einen Grafik-Kontext holt, sollte man ihn auch wieder freigeben. Dieser Hinweis fehlt leider in den Beispielen. Farbkonstanten sollten groß geschrieben sein, also Color.WHITE, statt Color.white, zumindest seitdem Sun diese Konvention in Java 1.4 eingeführt hat.

Erst wenn der letzte Programmierer eingesperrt und die letzte Idee patentiert ist, werdet ihr merken, dass Anwälte nicht programmieren können.

Shortest Java Quine with 106 chars

Because of a contest in a German Java forum I tried to formulate the shortest Java Quine:

enum _{_;{String _="enum _{_;{String _=%c%s%1$c;System.out.printf(_,34,_);}}";System.out.printf(_,34,_);}}

To compile and run under Windows (with Unix you need to redirect Exceptions to /dev/null and use diff):

>javac _.java 
>java _ 2> NUL > Quine.out
> fc Quine.out _.java
Vergleichen der Dateien Quine.out und _.JAVA
FC: Keine Unterschiede gefunden
>jrunscript -e "print(new java.io.File('_.java').length())"
106

I haven’t found a shorter solution on the net, so I suppose this is the shortest for now.

Inselupdate: JMX mit RMI-Adaptor

Für die Anbildung an den JMX-Server dient nicht nur die JConsole, sondern verschiedene andere Wege sind denkbar, etwa über HTTP oder über einen eigenen Client, der über RMI kommuniziert.

JMXConnectorServer für den Server

Zur Veröffentlichung der MBeans über RMI ist der JMXConnectorServer verantwortlich. Damit die MBeans an die RMI-Registry gebunden werden, wird eine besondere URL verwendet, die den Service genau beschreibt. Die JMXServiceURL hat den allgemeinen Aufbau:

service:jmx:<protocol>://[[[<host>]:<port>]/<path>]

Vor dem Start des Programms muss die RMI-Registry für Anmeldungen bereit sein; da unser Programm auf dem gleichen Rechner arbeitet, starten wir den RMI-Namensdienst selbst.

LocateRegistry.createRegistry( 1099 );
MBeanServer server = ManagementFactory.getPlatformMBeanServer();
String url = „service:jmx:rmi:///jndi/rmi://localhost/maze“
JMXConnectorServer connectorServer = JMXConnectorServerFactory.newJMXConnectorServer(
new JMXServiceURL( url ), null, server );
connectorServer.start();

ObjectName name = new ObjectName( „com.tutego.insel.jmx:type=Maze“ );
MazeMBean maze = new Maze();
server.registerMBean( maze, name );

JOptionPane.showMessageDialog( null, „Ende“ );
System.exit( 0 );

JConsole mit Remote-Connection

Mit der JConsole könnten wir nun Verbindung aufnehmen, in dem unter Remote Connection die URL service:jmx:rmi:///jndi/rmi://localhost/maze Einsatz findet.

JMXConnector für den Client

Auf der Clientseite gibt es mit JMXConnector den Gegenspieler zum JMXConnectorServer. Das Objekt liefert uns mit getMBeanServerConnection() ein MBeanServerConnection-Objekt, was sozusagen die Repräsentation des MBean-Servers MBeanServer auf der anderen Seite entspricht (genauso genommen gilt für die Schnittstellen: MBeanServer extends MBeanServerConnection). Über getAttribute() lassen sich dann zum Beispiel Properties erfragen.

JMXServiceURL u = new JMXServiceURL( „service:jmx:rmi:///jndi/rmi://localhost/maze“ );
JMXConnector c = JMXConnectorFactory.connect( u );
MBeanServerConnection mbsc = c.getMBeanServerConnection();
ObjectName name = new ObjectName( „com.tutego.insel.jmx:type=Maze“ );
System.out.println( mbsc.getAttribute( name, „MaxNumberOfPlayers“ ) );

Schnittstelle und Klasse für die MBean

package com.tutego.insel.jmx;

public interface MazeMBean
{
void setMaxNumberOfPlayers( int numberOfPlayers );
int getMaxNumberOfPlayers();
void dragonAlarm();
}

package com.tutego.insel.jmx;

public class Maze implements MazeMBean
{
private int numberOfPersons;

public void setMaxNumberOfPlayers( int numberOfPersons )
{
this.numberOfPersons = numberOfPersons;
}

public int getMaxNumberOfPlayers()
{
return numberOfPersons;
}

public void dragonAlarm()
{
System.out.println( „Heul. Huhuhuhu.“ );
}
}

TeX-Formulate in Webseiten mit jsMath

http://www.math.union.edu/~dpvc/jsMath/ ist eine Layout-Engine, die TeX-Formulare in Webseiten rendert. Dabei werden nicht, wie bei anderen Ansätzen, Grafiken generiert, sondern die Ausdrücke mit CSS positioniert. Damit skaliert das Ganze schön. Das Ergebnis ist beeindruckend und die Engine wird auch von einigen Wiki-Systemen verwendet. Einige Beispiele: Examples of jsMath. Im Interactive jsMath lab kann man Formen eingeben und es kommt HTML raus, was man nur noch zusammen mit dem CSS auf die eigene Webseite setzen muss. Aus x_i^2 wird etwa:

<SPAN CLASS=“typeset“><nobr><span class=“scale“><span class=“icmmi10″>x</span><span style=“position: relative; top:0.372em;“><span class=“size2″><span class=“icmmi10″>i</span></span><span class=“spacer“ style=“margin-left:0.05em“></span></span><span style=“position: relative; margin-left:-0.260em; top:-0.362em;“><span class=“size2″><span class=“icmr10″>2</span></span><span class=“spacer“ style=“margin-left:0.05em“></span></span><span class=“blank“ style=“height:1.331em;vertical-align:0.908em“></span></span></nobr></SPAN>

Jetzt fehlen nur noch jsMath.js und, falls etwa Summen-Zeichen oder sonstiges Sonderzeichen verwendet werden, im fonts-Ordner die TeX-Fonts.