innoQ

CAPSLOCKMarcel’s (CAPS)Weblog


Artikel

Come Together - Java und SQL Server 2005;Marcel Tilly; SQL Server Spezial; 2006

Sandesha: WS-ReliableMessaging mit Apache Axis;Marcel Tilly; Java Magazin; 4.2006

Lose Kopplung mit Web-Services einfach gemacht; Stefan Tilkov, Marcel Tilly und Hartmut Wilms, Java Spektrum; 05/2005

Das XML Object Model; Marcel Tilly, Java Spektrum; 03/2005

Java und .NET: Interoperabilität jenseits von Theorie und Spezifikation; Christian Weyer & Marcel Tilly, Enterprise Programming; dot.Net Sonderheft 2005

WSDL2.0 - Neuerungen der Spezifikation; Marcel Tilly; XML & Web Service Magazin; Ausgabe 1.2005

Modellgetriebene Entwicklung mit einer serviceorientierten Architektur zur Host-Integration; Frank Bruch, Marcel Tilly; OBJEKTspektrum MDD-Online-Ausgabe 09/04

Entspannung pur — Schemasprache Relax NG; Marcel Tilly, Stefan Tilkov; iX, 10/2004, S. 124.

Die Skriptsprache Groovy; Marcel Tilly; JavaSPEKTRUM 05/2004

Windows on a Mac

18.03.06

I cannot unnderstand why it is such a hype to install Windows on a Mac. The only reason for me: The mac user might hate their Mac OS. They might have waited such a long time for the chance to install another OS ;-)

Zwei neue Artikel

18.03.06

In diesem Jahr sind zwei neue Artikel von mir erschienen. Der eine beschäftigt sich mit WS-ReliableMessaging und der Umsetzung der Spezifikation in Apache Axis. Der andere Artikel behandelt den Zugriff auf den SQL Server 2005 von Java aus.

Einfach einfach

16.10.05

Mein fast zweijähriger Sohn erzählte mir heute folgendes:

Wurst - Wech - Mund - Alle - Ein - Ma - Auch!

   so ganz nebenbei bemerkt, sein erster kompletter Satz, wenn man ihn denn so bezeichnen würde und das macht einen Vater schon richtig stolz :-)

Mit welch verblüssender Klarheit er mir deutlich machen wollte, dass er seine erste Scheibe Wurst gegessen hatte und nun im Begriff war auch die nächste zu verputzen.

Wozu benötigen wir eigentlich Adjektive, Adverben, Prepositionen und den ganzen Mist. Es geht offensichtlich auch einfacher!

SEHR COOL - Identity2.0 - OSCON Presentation

07.10.05

Diese Präsentation ist alles andere als langweilig. Werde ich mir merken und versuche davon zu lernen.
(von Stefan)

Eclipse 3.1.1 Maintenance Release ist raus

07.10.05

Eclipse 3.1.1 maintenance release is now available for download:
For detail see the release notes.

Gleich runtergeladen und läuft! Hoffe, dass meine Probleme (NullPointerException und OutOfMemory beim kopieren) auch behoben sind!

Ergänzung zum Artikel "Lose Kopplung mit Web-Services einfach gemacht"

24.09.05 | Comments (4) | TrackBack (4)

Eine kleine Ergänzung zum Artikel möchte ich noch anführen. Den Original-Code zum Artikel findet man auf der Seite bei Java-Spektrum. Die Ergänzungen, die ich hier anführe, können direkt mit dem Artikel-Beispiel verwendet werden.

Die Frage, die sich stellt ist: Bietet Axis nicht auch die Möglichkeit, mit diesen Non-Breaking-Changes umzugehen? Von Hause aus ist Axis da sehr rigoros, wie wir im Artikel gezeigt haben, was meiner Meinung nach auch ein Problem ist. Sichelrlich, kann man mit Axis sehr schnell einfache Web Services basteln, aber Eingriffe in den Gesamtprozes sind doch eher schmerzhaft und nervig. Warum also nicht einfach XML verschicken. Aber warum sollte man dann noch Axis benutzen, da doch das gesamte Know-How in der Serialisierung und Deserialisierung steckt. Das Verschicken und Empfangen von SOAP-Nachrichten ist selbst keine Kunst. Nun gut, ich schweife ab :-)
Ok, wenn man schon Axis verwendet, soll man gefälligst auch die wenigen Vorteile genießen. Es gibt nämlich durchaus eine Möglichkeit Axis dazu zubewegen, ebenfalls bei Non-Breaking-Changes keinen Fehler zu verursachen.
Bei dem Artikel gab es einen Service, bei dem Tickets für die WM bestellt werden konnten. In der ersten Version hat dieser Service für das bestellte Ticket nur Nummer des Tickets und den Block zurückgibt. Eine Bestellbestätigung sah also wie folgt aus:


public class Confirmation  implements java.io.Serializable {
    private java.lang.String block;
    private long number;
    ...
}

In der zweiten Version gab es zusätzlich auch Informationen über den Sitzplatz (Seat). Hier sah die Bestätigung folgendermaßen aus:


public class Confirmation  implements java.io.Serializable {
    private java.lang.String block;
    private long number;
    private java.lang.String seat;
    ...
}

Wurden nach der Änderung des Services keine neuen Proxy-Klassen erzeugt, meldete der Client (Consumer) den folgenden Fehler:

org.xml.sax.SAXException: Invalid element in com.innoq.ws.client1.Confirmation - seat

Obwohl ich es wesentliche "Service-orientierter" finde mit der Nachricht - also dem puren XML - zu arbeiten, gibt es bei der Verwendung von WSDL2Java und der daraus resultierenden Proxy-Klassen auch Vorteile. Auf jeden Fall ist die Entwicklung machmal einfacher - aber auch fehleranfälliger. Axis bietet nun die Möglichkeit eigene Serialisierer und Deserialisierer zu verwenden. Diese können einem nun aus diesem Problem (SAXException siehe oben) zu befreien. Damit ein CustomDeserializer aber auch verwendet wird, muss er in der getDeserializer()-Methode der JavaBean zurückgegeben werden:


public class Confirmation  implements java.io.Serializable {
    private java.lang.String block;
    private long number;

    public Confirmation() {
    }

    ....

    /**
     * Get Custom Deserializer
     */
    public static org.apache.axis.encoding.Deserializer getDeserializer(
           java.lang.String mechType, 
           java.lang.Class _javaType,  
           javax.xml.namespace.QName _xmlType) {
        return 
         // urspünglich
         //new  org.apache.axis.encoding.ser.BeanDeserializer(
                _javaType, _xmlType, typeDesc);
          // mein eigener Deserialisierer
         new  CustomDeserializer(_javaType, _xmlType, typeDesc);
    }

}


Ein CustomerDeserialiser, der überflüssige Elemente in einem SOAP-Request ignoriert, sieht folgendermaßen aus - hierzu musste ich die onStartChild()-Methode des BeanDeserializer modifizieren:


package com.innoq.ws.client1;

import java.util.Map;

import javax.xml.namespace.QName;
import org.apache.axis.encoding.DeserializationContext;

import org.apache.axis.Constants;
import org.apache.axis.description.ElementDesc;
import org.apache.axis.description.FieldDesc;
import org.apache.axis.description.TypeDesc;
import org.apache.axis.encoding.ConstructorTarget;
import org.apache.axis.encoding.Deserializer;
import org.apache.axis.encoding.ser.ArrayDeserializer;
import org.apache.axis.encoding.ser.BeanDeserializer;
import org.apache.axis.encoding.ser.BeanDeserializerFactory;
import org.apache.axis.encoding.ser.BeanPropertyTarget;
import org.apache.axis.message.MessageElement;
import org.apache.axis.message.SOAPHandler;
import org.apache.axis.soap.SOAPConstants;
import org.apache.axis.utils.BeanPropertyDescriptor;
import org.apache.axis.utils.Messages;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;

public class CustomDeserializer extends BeanDeserializer {

  private Class javaType;

  // Construct BeanSerializer for the indicated class/qname
  public CustomDeserializer(Class javaType, QName xmlType) {
    this(javaType, xmlType, TypeDesc.getTypeDescForClass(javaType));
  }

  // Construct BeanDeserializer for the indicated class/qname and meta Data
  public CustomDeserializer(Class javaType, QName xmlType, TypeDesc typeDesc) {
    this(javaType, xmlType, typeDesc, BeanDeserializerFactory
        .getProperties(javaType, typeDesc));
  }

  // Construct BeanDeserializer for the indicated class/qname and meta Data
  public CustomDeserializer(Class javaType, QName xmlType, TypeDesc typeDesc,
      Map propertyMap) {
    super(javaType, xmlType, typeDesc, propertyMap);
    this.javaType = javaType;
  }

  /**
   * Deserializer interface called on each child element encountered in the
   * XML stream.
   */
  public SOAPHandler onStartChild(String namespace, String localName,
      String prefix, Attributes attributes, DeserializationContext context)
      throws SAXException {
    handleMixedContent();

    BeanPropertyDescriptor propDesc = null;
    FieldDesc fieldDesc = null;

    SOAPConstants soapConstants = context.getSOAPConstants();
    String encodingStyle = context.getEncodingStyle();
    boolean isEncoded = Constants.isSOAP_ENC(encodingStyle);

    QName elemQName = new QName(namespace, localName);
    // The collectionIndex needs to be reset for Beans with multiple arrays
    if ((prevQName == null) || (!prevQName.equals(elemQName))) {
      collectionIndex = -1;
    }
    prevQName = elemQName;

    boolean isArray = false;
    QName itemQName = null;
    if (typeDesc != null) {
      // Lookup the name appropriately (assuming an unqualified
      // name for SOAP encoding, using the namespace otherwise)
      String fieldName = typeDesc.getFieldNameForElement(elemQName,
          isEncoded);
      propDesc = (BeanPropertyDescriptor) propertyMap.get(fieldName);
      fieldDesc = typeDesc.getFieldByName(fieldName);

      if (fieldDesc != null) {
        ElementDesc element = (ElementDesc) fieldDesc;
        isArray = element.isMaxOccursUnbounded();
        itemQName = element.getItemQName();
      }
    }

    if (propDesc == null) {
      // look for a field by this name.
      propDesc = (BeanPropertyDescriptor) propertyMap.get(localName);
    }

    // try and see if this is an xsd:any namespace="##any" element before
    // reporting a problem
    if (propDesc == null) {
      // try to put unknown elements into a SOAPElement property, if
      // appropriate
      propDesc = getAnyPropertyDesc();
      if (propDesc != null) {
        try {
          MessageElement[] curElements = (MessageElement[]) propDesc
              .get(value);
          int length = 0;
          if (curElements != null) {
            length = curElements.length;
          }
          MessageElement[] newElements = new MessageElement[length + 1];
          if (curElements != null) {
            System
                .arraycopy(curElements, 0, newElements, 0,
                    length);
          }
          MessageElement thisEl = context.getCurElement();

          newElements[length] = thisEl;
          propDesc.set(value, newElements);
          // if this is the first pass through the MessageContexts
          // make sure that the correct any element is set,
          // that is the child of the current MessageElement, however
          // on the first pass this child has not been set yet, so
          // defer it to the child SOAPHandler
          if (!localName.equals(thisEl.getName())) {
            return new SOAPHandler(newElements, length);
          }
          return new SOAPHandler();
        } catch (Exception e) {
          throw new SAXException(e);
        }
      }
    }

    if (propDesc == null) {
      // todo: Hier wird auf ein fehlendes Attribute-Mapping reagiert!!!!!

      System.out.println("Attribute " + localName + "[" + namespace + "] is unknown!");

      // todo: vorher wurde hier eine Exception geworfen!
      // No such field
      // throw new SAXException(Messages.getMessage("badElem00",
      // javaType.getName(),localName));
      return null;
    } else {

      // Get the child's xsi:type if available
      QName childXMLType = context.getTypeFromAttributes(namespace,
          localName, attributes);

      String href = attributes.getValue(soapConstants.getAttrHref());
      Class fieldType = propDesc.getType();

      // If no xsi:type or href, check the meta-data for the field
      if (childXMLType == null && fieldDesc != null && href == null) {
        childXMLType = fieldDesc.getXmlType();
        if (itemQName != null) {
          // This is actually a wrapped literal array and should be
          // deserialized with the ArrayDeserializer
          childXMLType = Constants.SOAP_ARRAY;
          fieldType = propDesc.getActualType();
        } else {
          childXMLType = fieldDesc.getXmlType();
        }
      }

      // Get Deserializer for child, default to using DeserializerImpl
      Deserializer dSer = getDeserializer(childXMLType, fieldType, href,
          context);

      // It is an error if the dSer is not found - the only case where we
      // wouldn't have a deserializer at this point is when we're trying
      // to deserialize something we have no clue about (no good xsi:type,
      // no good metadata).
      if (dSer == null) {
        dSer = context.getDeserializerForClass(propDesc.getType());
      }

      // Fastpath nil checks...
      if (context.isNil(attributes)) {
        if (propDesc != null && (propDesc.isIndexed() || isArray)) {
          if (!((dSer != null) && (dSer instanceof ArrayDeserializer))) {
            collectionIndex++;
            dSer.registerValueTarget(new BeanPropertyTarget(value,
                propDesc, collectionIndex));
            addChildDeserializer(dSer);
            return (SOAPHandler) dSer;
          }
        }
        return null;
      }

      if (dSer == null) {
        throw new SAXException(Messages.getMessage("noDeser00",
            childXMLType.toString()));
      }

      if (constructorToUse != null) {
        if (constructorTarget == null) {
          constructorTarget = new ConstructorTarget(constructorToUse,
              this);
        }
        dSer.registerValueTarget(constructorTarget);
      } else if (propDesc.isWriteable()) {
        // If this is an indexed property, and the deserializer we found
        // was NOT the ArrayDeserializer, this is a non-SOAP array:
        // 
        // value1
        // value2
        // ...
        // In this case, we want to use the collectionIndex and make
        // sure
        // the deserialized value for the child element goes into the
        // right place in the collection.

        // Register value target
        if ((itemQName != null || propDesc.isIndexed() || isArray)
            && !(dSer instanceof ArrayDeserializer)) {
          collectionIndex++;
          dSer.registerValueTarget(new BeanPropertyTarget(value,
              propDesc, collectionIndex));
        } else {
          // If we're here, the element maps to a single field value,
          // whether that be a "basic" type or an array, so use the
          // normal (non-indexed) BeanPropertyTarget form.
          collectionIndex = -1;
          dSer.registerValueTarget(new BeanPropertyTarget(value,
              propDesc));
        }
      }

      // Let the framework know that we need this deserializer to complete
      // for the bean to complete.
      addChildDeserializer(dSer);

      return (SOAPHandler) dSer;
    }
  }

}

Artikel "Lose Kopplung mit Web-Services einfach gemacht"

24.09.05

Gerade ist im Java Spektrum ein Aritkel zu lose gekoppelten Web Services von Stefan, Hartmut und mir erschienen.

Die PDF-Version findet man hier.

Interview mit Groovy's Guillaume Laforge

07.09.05

Bei Vanvard Technologies kann man ein Interview mit dem Projekt-Lead vom Groovy-Projekt, Guillaume Laforge, lesen. Es lohnt definitiv einen Blick, auch wenn man bisher noch nicht viel mit Groovy gemacht hat.

Außerdem hat hat Andrew Glover, CTO von Vanvard Technologies, dieses Interview geführt. Andrew hat einen guten Artikel bei developerWorks zu Groovy geschrieben hat: "Practically Groovy".

Einen deutschsprachigen Artikel zu Groovy findet man hier.

Groovy JSR-03 released

07.09.05

Groovy JSR-03 released:
The Groovy team is pleased to announce the release of Groovy JSR-03.

As we're getting closer to a final 1.0 release, we've been mainly focusing on bug fixing and closing JIRA issues.
You can browse all the bugs fixed and new improvements on JIRA: http://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&styleName=Html&version=11518

Weiter so!

Wir warten auf das Final-Release.

Validierung mit Schematron und Jing

25.05.05

Für einen Artikel wollte ich ein XML Dokument mit Schematron validieren. Dabei wollte ich aus meinem Javacode heraus die Validierung anstossen. Mehrere Ansätze und APIs hätten sich angeboten. Eine Liste von APIs findet sich hier.

Ich habe mich für Jing entschieden. Ein Java-API von James Clark, dass er zur Validierung mit Relax NG geschrieben hat. Es ist allerdings auch eine Schematron Validierung damit möglich. Die Dokumentation dazu habe ich allerdings nciht gefunden, musste mich also durch den Sourcecode quälen, um zu sehen, wie ich denn nun das XML Dokument mit Schematron und Jing validiert bekomme.

Damit ich nicht noch mal Stunden damit verbinge, wollte ich ein paar Punkte festhalten.

  1. zunächst muss eine SchematronSchemaReaderFactory erzeugt werden; hier die XalanSchemaReaderFactory; alternativ dazu gibt es noch die SaxonSchemaReaderFactory
  2. als nächstes wird ein ValidationDriver erzeugt. Dabei hilft die Factory; hier ist zu beachten, dass der createSchemaReader()-Methode die korrekte Schemtron-Uri (http://www.ascc.net/xml/schematron) übergeben wird, sonst erzeugt die Factory nix
  3. dann darf man nicht vergessen ein Schemtron-Schema über die loadSchema()-Methode zu laden
  4. zum Schluss kann dann eine InputSource validiert werden

Hier nun der Code: import java.io.FileReader; import java.io.StringReader; import org.xml.sax.InputSource; import com.thaiopensource.validate.ValidationDriver; import com.thaiopensource.validate.schematron.SchematronSchemaReaderFactory; import com.thaiopensource.validate.schematron.XalanSchemaReaderFactory;

/**
 * How to validate with Schematron and Jing. 
 */
public class SchematronValidation {
  // Schematron URI
  private static final String SCHEMATRON_URI = "http://www.ascc.net/xml/schematron";
  /**
   * Validates the input with the given xslt.
   * @param input
   */
   public static boolean validate(org.w3c.dom.Node content, String xslt) {
      try {
          InputSource message = new InputSource(new StringReader(
              content.toString())); 
          SchematronSchemaReaderFactory readerFactory = new XalanSchemaReaderFactory();
          ValidationDriver validDriver = new ValidationDriver(
              readerFactory.createSchemaReader(SCHEMATRON_URI));
          validDriver.loadSchema(new InputSource(
              new FileReader(xslt)));
          return validDriver.validate(message);
       } catch (Exception e) {
          System.out.println(e.getMessage());
       }
       return false;
    }
}

Die XSLT-Datei könnte wie folgt aussehen:

<schema xmlns="http://www.ascc.net/xml/schematron">
  <ns prefix="ws" uri="http://ws.innoq.com"/>
  <pattern name="OrderReturn">
    <rule context="ws:orderReturn">
      <assert test="ws:number">Schema contains number.</assert>
      <assert test="ws:block">Schema contains block.</assert>
    </rule>
  </pattern>
</schema>

Artikel zu XOM

25.05.05

Mal wieder ein Artikel von mir! Diesmal zum Thema "XML" - offensichtlich wandert XML immermehr in meinen Hauptfokus. Nachdem ich zunächst XML nicht allzusehr beachtet habe, beschäftige ich mich in letzter Zeit sehr viel mit XML.

Deshalb ist es wohl zwangläufig, dass ich auf XOM - Dank an Stefan - gestoßen bin. Also mal geschwind ein paar Dinge ausprobiert, für gut befunden und einen Artikel dazugeschrieben.

Vóila, da isser!

P.S.: Der Sourcecode zu den Beispielen kann ebenfalls von dort heruntergeladen werden.

Bucherfolg

25.05.05

Mein Buch ist nun seit mehr als einem halben Jahr auf dem Markt. Ich messe derzeit den Erfolg an dem Verkaufsrang bei Amazon - da fände ich es super, wenn man zu einem Buch eine Grafik bekommen würde, die den Verkaufsrang über die Zeit anzeigen würde. Die beste Platzierung, die das Buch dort hatte, war ungefähr 4.900. Ich werte das als Erfolg, zumal nur ein Buch zu Eclipse besser platziert ist (und das ist nicht von Erich Gamma, sondern ebenfalls von Berthold Daum). Nachdem es so ab Februar 2005 steig bergab ging und sich das Buch bereits auf Verkaufsrang 11.000 befand, dachte ich schon, dass wir uns langsam damit abfinden müssten, dass wir den Zenit hinter uns haben, aber...

...überraschenderweise bekam das Buch den "zweiten Wind" und steigt auf Rang 4.838! Rekord!

Also wer noch keines hat sollte schnellstens zuschlagen, irgendwann sind die ausverkauft ;-)

Apache Axis 1.2final

06.05.05

Obwohl Apache Axis 1.2 nun final ist, ist es immernoch nicht auf der Download-Seite verfügbar. Nun ja, aber hier findet man es dann doch!

Viel Spass damit! Ich bin gespannt, ob die Fehler aus dem RC3 nun wirklich alle raus sind. Also heißt es: Testen, testen, testen!

Build.xml to Freemind via Groovy

04.04.05

Ich finde, dass Ant-Skripte die Eigenschaft haben, dass sie sehr schnell unleserlich und unpflegbar werden. Nun hat Javanicus eine wirklich coole Möglichkeit gefunden, diese zu visualisieren.

Man nehme einfach ein Groovy-Skript und rufe es über

groovy antmap.g > build.mm

auf und schon hat man eine Freemind Ausgabe seiner build.xml.

Artikel zu WS-Interop zwischen Java und .NET

04.04.05

Ich habe meinen ersten Artikel in einem .NET-Magazin veröffentlicht. Danke an Christian Weyer!

In dem Artikel beleuchten wir die Interoperabilitätsproblem von Web Services zwischen Java un .NET etwas näher. Dabei verwenden wir neben WSE2.0 auch Apache Axis und Systinet Server.

Coole Web-Application-Frameworks in Ruby oder Perl

07.03.05

Durch Stefan bin ich bei Ruby Rails gelandet und die Intros kann man sich wirklich antun. Das ist wirklich sehr einfach und Open Source.

Gibt es auf Java/Jsp/Servlet-Basis eigentlich ähnliche Frameworks? Ich kenne von Innopract das W4T-Tool, das aber einen größeren Fokus auf WYSIWYG legt. Ein anderes Framework findet man bei Maypole und kommt den Perl-Hackern mehr entgegen.

Groovy-1.0-beta-10

02.03.05

In der letzten Zeit gab es ziemlich viel Kritik am Groovy Projekt. Dennoch hat sich dort einiges getan:

Außerdem finde ich das COM Scripting ziemlich cool:


import org.codehaus.groovy.scriptom.ActiveXProxy

// create a proxy for Excel
xls = new ActiveXProxy("Excel.Application")
xls.Visible = true

Thread.sleep(1000)

// get the workbooks object
workbooks = xls.Workbooks
// add a new workbook
workbook  = workbooks.Add()

// select the active sheet
sheet = workbook.ActiveSheet

// get a handle on two cells
a1 = sheet.Range('A1')
a2 = sheet.Range('A2')

// sets a value for A1
a1.Value   = 123.456
// defines a formula in A2
a2.Formula = '=A1*2'

println "a1: ${a1.Value.value}"
println "a2: ${a2.Value.getValue()}"

// close the workbook without asking for saving the file
workbook.Close(false, null, false)
// quits excel
xls.Quit()

Faltbares Foliendisplay

02.03.05

Einigen von meinen Bekannten hatte ich mal von meiner Idee erzählt, dass man ein Foliendisplay machen sollte, dass man auf die Größen falten kann, die man zur Verfügung hat. So könnte man z. B. in der Bahn ein A6 großes Display an den Vordersitz heften oder ein A4 großes an die Seitenscheibe. Wie man will!

Meistens habe ich dafür nur Spott und Hohn geerntet. Aber offensichtlich habe ich die falschen Bekannten! Unkreativ und nicht visionär.

Sir Stefan

09.01.05

Mir scheint, als wenn mein Chef, Stefan, nun in den Adelsstand erhoben wurde.

Von nun an muss ich ihn wohl Sir Stefan nennen.

Verdammter Mist ;-)

Eigentlich müsste ich ja aauch noch meinen “Senf dazugeben”, aber da fällt mir wirklich nichts mehr ein. Sorry!

Effektives WSDL Design

Da hat Christian wirklich recht; diese Präsentation lohnt einen Blick:

Using XML schemas effectively in WSDL design - A conference presentation from SD West 2004 HTML PDF