Na sowas! Da gelingt es, via JMS einen Task zu parallelisieren, schon springt mir JAXB ins Kreuz mit einer seltsam anmutenden Fehlermeldung:
FWK005 parse may not be called while parsing
Laut java.net u.a. sieht es so aus, als wäre der XML Parser nicht so wirklich Thread-safe und damit für JMS mehr oder weniger schwierig einzusetzen.
Grund ist das folgende Stück Code für die Initialisierung und das Unmarshalling des XML-Fragments:
private static Unmarshaller unmarshaller; static { try { JAXBContext ctx = JAXBContext.newInstance(MyObj.class); unmarshaller = ctx.createUnmarshaller(); } catch (JAXBException ex) { throw new ExceptionInInitializerError(ex); } } public static MyObj unmarshal(byte[] data) throws IOException, JAXBException { ByteArrayInputStream bis = new ByteArrayInputStream(data); try { GZIPInputStream gzis = new GZIPInputStream(bis); try { return (MyObj) unmarshaller.unmarshal(gzis); } finally { gzis.close(); } } finally { bis.close(); } }
Weil die Initialisierung so teuer ist, wurde eine Art Singleton umgesetzt um den Parser zwischen allen Instanzen der Klasse zu teilen. Klappt auf dem Desktop im Standalone-Betrieb auch wunderbar. Nur wenn mehrere parallel laufende Threads gleichzeitig darauf zugreifen, wird es hektisch (siehe Fehlermeldung oben). Was nun?
Eine Möglichkeit wäre natürlich, die JMS Message-driven Bean via @ActivationConfig auf nur eine Instanz zu beschränken:
@ActivationConfigProperty(propertyName = "maxSession", propertyValue = "1")
Das funktioniert prima und die Fehlermeldung taucht zunächst mal nicht mehr auf. Der Task kann damit zwar noch asynchron ausgeführt werden, leider aber nicht mehr parallel. Die Sache hat noch einen weiteren Haken: Gewöhnliche Session Beans laufen ggf. auch parallel und wenn nun beispielsweise zwei Threads gleichzeitig auf denselben JAXB Marshaller zugreifen wollen, knallts erneut.
Abhilfe scheint hier zunächst einmal nur ein synchronize() um den Marshaller-Zugriff herum zu machen. Als Schlüssel für das Nadelöhr wurde das Marshaller-Objekt selbst benutzt. Diese Lösung läßt für den Zugriff auf das Mashaller-Objekt zu jedem Zeitpunkt nur noch einen Thread zu:
synchronize( unmarshaller ) { return (MyObj) unmarshaller.unmarshal(gzis); }
Übrigens: So richtig cool wäre hier eine Singleton-Implementierung mit einem Pool aus N Marshaller-Objekten. Damit werde ich mich zu gegebener Zeit einmal auseinandersetzen.