package xfire;
import javax.jws.WebMethod;
import javax.jws.WebService;
@WebService( serviceName = "GreeterService" )
public class GreeterService
{
@WebMethod
public String greet( Person p )
{
return "Hello " + p.getName() + "!";
}
}
package xfire;
public class Person
{
private String name;
public Person()
{
}
public Person( String name )
{
this.name = name;
}
public String getName()
{
return name;
}
public void setName( String name )
{
this.name = name;
}
}
package xfire;
import org.codehaus.xfire.XFire;
import org.codehaus.xfire.XFireFactory;
import org.codehaus.xfire.annotations.AnnotationServiceFactory;
import org.codehaus.xfire.annotations.jsr181.Jsr181WebAnnotations;
import org.codehaus.xfire.server.http.XFireHttpServer;
import org.codehaus.xfire.service.Service;
public class ServiceServer
{
public static void main( String[] args ) throws Exception
{
XFire xfire = XFireFactory.newInstance().getXFire();
AnnotationServiceFactory factory = new AnnotationServiceFactory(
new Jsr181WebAnnotations(),
xfire.getTransportManager() );
Service service = factory.create( GreeterService.class );
xfire.getServiceRegistry().register( service );
new XFireHttpServer().start();
// http://localhost:8081/GreeterService?wsdl
}
}
package xfire;
import java.net.MalformedURLException;
import org.codehaus.xfire.client.XFireProxyFactory;
import org.codehaus.xfire.service.Service;
import org.codehaus.xfire.service.binding.ObjectServiceFactory;
public class ServiceClient
{
public static void main( String[] args ) throws MalformedURLException
{
ObjectServiceFactory serviceFactory = new ObjectServiceFactory();
Service serviceModel = serviceFactory.create( GreeterService.class );
GreeterService service = (GreeterService)
new XFireProxyFactory().create( serviceModel,
"http://localhost:8081/GreeterService" );
String s = service.greet( new Person( "Ulli" ) );
System.out.println( s );
}
}
Autor: Christian Ullenboom
Absonderliche Schreibweise bei einer Feld-Rückgabe
Da blättere ich gerade durch das „Just Java“ Buch und glaube, schon einen Fehler gefunden zu haben, da sagt mir der Compiler, dass alles in Ordnung ist:
public class T
{
public static int foo()[]
{
return new int[] { 1, 2, 3 };
}
public static void main( String[] args )
{
System.out.println( foo().length );
}
}
Diese sonderbare Rückgabe habe ich noch nie gesehen und ist mir in der Grammatik der Sprache Java auch noch nie aufgefallen.
Gerade noch in der Java EE, jetzt schon in der Java SE
Was früher noch Teil der J2EE war, ist heute Teil der Java Standard Edition.
- http://java.sun.com/j2ee/sdk_1.3/techdocs/api/ zeigt noch DOM und SAX, sowie das „JDBC 2.0 Optional Package“ was dann Teil der J2SE 1.4 wurde.
- http://java.sun.com/j2ee/1.4/docs/api/index.html zeigt javax.management „Java Management Extensions“ (JMX), und javax.xml.parsers, javax.xml.transform was fester Bestandteil von Java SE 5 wurde.
- http://java.sun.com/javaee/5/docs/api/ zeigt die JPA (Java Persistence API), was vermutlich Teil von Java 6 wird.
Viele Teile der Java Enterprise Edition werden so geläufig, dass sie in die Java Standard Edition wandern.
- Dennoch wurde es unter der Enterprise-Edition mehr: 78 Pakete in Java EE 5, 54 Pakete in J2EE 1.4, 32 in J2EE 1.3
@Service-EJBs bei JBoss — EJB 3.1 ruft
Bei JBoss ist eine Service-Bean eine besondere EJB, wovon es serverseitig nur ein Exemplar gibt.
Unterschiedliche Clients werden immer mit dem gleichen Exemplar arbeiten.
Es ist ein Singleton, ein nicht gepooltes Objekt.
Eine Service-Bean wird mit @org.jboss.annotation.ejb.Service annotiert.
@Service
public class VitalityCheckBean implements…
Über @Remote und/oder @Local wäre sich auch ganz normal von außen zugänglich.
Interessant sind Service-Bean, weil sie an einem MBean-Server angemeldet werden können und so über JMX zugänglich sind.
Dazu muss die EJB
mit @Service annotiert werden,
ein Management-Interface für die JMX-Operationen implementieren und
mit der Annotation @org.jboss.annotation.ejb.Management das Management-Interface benennen.
Das Management-Interface ist eine ganz normale Java-Schnittstelle und muss keiner Namenskonvention folgen.
Alle dort genannten Methoden sind über eine JMX-Konsole sichtbar.
interface MyManagementInterface
{
void doIt();
String getValue();
void setValue( String string );
}
import org.jboss.annotation.ejb.*;
@Service
@Management( MyManagementInterface.class )
public class SimpleServiceBean implements MyManagementInterface
{
private String string = „(default)“
public void doIt() {
System.out.println( „Ausgabe“ );
}
public void setValue( String string ) {
this.string = string;
}
public String getValue() {
return string;
}
}
In der JMX-Konsole unter http://localhost:8080/jmx-console/ suchen nach SimpleServiceBean mit ManagementInterface.
Der JMX-ObjectName lässt sich über das Attribut objectName der Annotation @Service ändern:
@Service(objectName=“javatutor:service=SimpleServiceBean“)
@Management(MyManagementInterface.class)
public class SimpleServiceBean implements …
Weiteres, auch zum Lebenszkylus unter
JBoss, JMX und JConsole
Mit JBoss und den proprietären Annotationen ist es einfach eine JMX zu schreiben. JBoss bringt ein Beispiel gleich mit:
package trail.jmx;
public interface Rechner {
// Attribute
public void setGrowthrate (double g);
public double getGrowthrate ();
// The management method
public double calculate (int start, int end, double saving);
// Life cycle method
public void create () throws Exception;
public void destroy () throws Exception;
}
package trail.jmx;
import java.lang.management.ManagementFactory;
import javax.annotation.PostConstruct;
import org.jboss.annotation.ejb.Management;
import org.jboss.annotation.ejb.Service;
@Service (objectName=“trail:service=calculator“)
@Management(Rechner.class)
public class RechnerMBean implements Rechner {
double growthrate;
public void setGrowthrate (double growthrate) {
this.growthrate = growthrate;
}
public double getGrowthrate () {
return growthrate;
}
public double calculate (int start, int end, double saving) {
double tmp = Math.pow(1. + growthrate / 12., 12. * (end – start) + 1);
return saving * 12. * (tmp – 1) / growthrate;
}
// Lifecycle methods
public void create() throws Exception {
growthrate = 0.08;
System.out.println(„Calculator – Creating“);
}
public void destroy() {
System.out.println(„Calculator – Destroying“);
}
}
In der JMX-Console taucht die Klasse dann auf.
Wenn man das ganze nun mit der JConsole hinbekommen möchte, hat man ein Problem, denn JBoss meldet die Beans nicht am Standard-Container für JMX Beans an, sondern an einem eigenen. Daher benötigt JBoss einen Hinweis, in den Sun-Container zu schreiben. Dazu ist ein Schalter beim Starten (run.bat oder in Eclipse bei der Startkonfiguration) mit anzugeben:
-Djboss.platform.mbeanserver -Djavax.management.builder.initial=org.jboss.system.server.jmx.MBeanServerBuilderImpl
Unter Java 5 muss natürlich noch -Dcom.sun.management.jmxremote dazu.
Jetzt ist der „Rechner“ auch in der JConsole.
Einige Gründe, warum man keinen Second Level Cache (etwa bei Hibernate) verwenden soll
- Greift Hibernate nicht allein auf die Datenquelle zu, sind die Daten im Cache unter Umständen nicht mehr aktuell.
- Wenn Daten einmal in den Cache gesetzt werden, danach aber nie wieder benötigt werden, war die Arbeit umsonst.
- Bei sehr vielen Daten quillt der Cache über und muss Dinge entfernen – nach Murphys Law sind die dabei, die wir als nächstes wieder lesen.
- Es sollen/müssen alle Anfragen an die Datenbank gehen, weil etwa jede Anfrage Logging-Informationen mitgespeichert. (Durch ein SELECT ist ein Trigger verbunden.)
- Wenn es nur eine Sitzung gibt, ist ein Second Level Cache unnötig. Er dient nur dazu, Daten zwischen mehreren Sitzungen „lebendig“ zu halten.
Eclipse-Plugin JSEclipse für JavaScript
JSEclipse: Ein JavaScript Editor aus den Adobe-Labors. Der bietet farbliche Hervorhebung, Templates, Tastaturvervollständigung, Fehler- und Hinweismeldungen, Outline und mehr. Das Plugin lässt sich manuell, oder über den Update-Manger mit der Adresse http://download.macromedia.com/pub/labs/jseclipse/autoinstall installieren. In einem Forum finden Probleme und Lösungen zusammen. Die Doku wird im Eclipse Hilfe-Manager eingebunden. (Bild 1, Bild 2)
Java 2D unter 3D JOGL
Das Programm mit zu starten ist ein MUSS!
Mehr zur Vorgehensweise, wie Java 2D mit JOGL auf Open GL kommt, gibt es unter http://weblogs.java.net/blog/campbell/archive/2007/01/java_2d_and_jog.html.
Jettison – JSON-Dokumente nach dem StAX-Modell schreiben
Viele JSON-Implementierungen nehmen ein direktes Mapping der Java-Objekte nach JSON vor. http://jettison.codehaus.org/ geht da einen anderen Weg. Mit den aus StAX bekannten Methoden zum Schreiben/Lesen von Elementen wird nicht in ein XML-Format „gemarshalled“, sondern in das JSON-Format. Ein Testfall für MappedXMLStreamWriterTest macht deutlich, was hier vor sich geht:
StringWriter strWriter = new StringWriter(); MappedNamespaceConvention con = new MappedNamespaceConvention(); AbstractXMLStreamWriter w = new MappedXMLStreamWriter(con, strWriter); w.writeStartDocument();w.writeStartElement("root"); w.writeStartElement("child1");w.writeStartElement("subchild1");w.writeCharacters("test");w.writeEndElement(); w.writeStartElement("subchild2");w.writeCharacters("test");w.writeEndElement(); w.writeEndElement(); w.writeStartElement("child2");w.writeStartElement("subchild");w.writeCharacters("test");w.writeEndElement();w.writeEndElement(); w.writeEndElement();w.writeEndDocument(); w.close();strWriter.close(); System.out.println(strWriter.toString()); assertEquals("{\"root\":{" +"\"child1\":{\"subchild1\":\"test\",\"subchild2\":\"test\"}," +"\"child2\":{\"subchild\":\"test\"}}}", strWriter.toString());
Ein Servlet/JSP würde statt in den StringWriter in den response Strom schreiben.
TaskExecutor und java.util.concurrent.Executor in Spring 2
Seit Java 5 gibt es mit dem Executor (JSR 166) eine Schnittstelle für „Ausführer“ von Runnable-Objekten.
Spring bietet mit dem TaskExecutor die gleiche Schnittstelle.
– So steht die Funktionalität auch für Java 1.4 und Java 1.3 und auch für Java EE Umgebungen zur Verfügung.
org.springframework.core.task.TaskExecutor:
void execute( Runnable task )
Die TaskExecutor im Überblick
SimpleThreadPoolTaskExecutor
– Thread-Pool durch Quartz’s SimpleThreadPool.
SimpleAsyncTaskExecutor
– Erzeugt pro Anfrage einen neuer Thread. Es gibt eine optionale maximale Größe.
SyncTaskExecutor
– Führt Runnable im aktuellen Thread aus. Erzeugt also keinen neuen Thread zur Nebenläufigkeit.
ConcurrentTaskExecutor
– Ein Wrapper zum java.util.concurrent.Executor von Java 5.
ThreadPoolTaskExecutor
– Geht direkt auf den java.util.concurrent.ThreadPoolExecutor von Java 5. Ermögicht Anpassung der Properties „corePoolSize“, „maxPoolSize“, „keepAliveSeconds“, „queueCapacity“.
TimerTaskExecutor
– Nutzt einen java.util.Timer zur Ausführung. Der Runnable wird als TimerTask hintereinander durch den Timer-Thread ausgeführt.
WorkManagerTaskExecutor
– Nutzt CommonJ WorkManager als Implementierung. Ermöglicht Ausführung auf Applikationsservern von BEA und IBM.
Beispiel für TaskExecutor
public class DoItExecutor
{
private TaskExecutor executor;
public DoItExecutor( TaskExecutor executor )
{
this.executor = executor;
}
public void doIt()
{
Runnable run = new Runnable() {
public void run() {
System.out.println( Thread.currentThread() );
}
};
executor.execute( run );
executor.execute( run );
}
}
Injizierung vom TaskExecutor
<bean id=“taskExecutor“ class=“org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor“>
<property name=“corePoolSize“ value=“5″ />
<property name=“maxPoolSize“ value=“10″ />
<property name=“queueCapacity“ value=“25″ />
</bean>
<bean id=“doItExecutor“ class=“DoItExecutor“>
<constructor-arg ref=“taskExecutor“ />
</bean>
„Fehlende“ Typen aus Java 5
Spring bietet unterschiedliche Implementierungen für den TaskExecutor, aber Java 5 bietet außer execute() im Executor noch mehr.
Der java.util.concurrent.ThreadPoolExecutor implementiert mehr als nur Executor, nämlich ExecutorService. Das bietet
– awaitTermination(), invokeAll(), invokeAny(), isShutdown(), isTerminated(), shutdown(), shutdownNow(), submit()
– mit weiteren Typen wie Future, Callable.
Spring bietet dafür bisher keine Abstraktion!
SimpleJdbcTemplate aus Spring 2.0
Das org.springframework.jdbc.core.JdbcTemplate compiliert unter Java 1.4 und nutzt keine Eigenschaften von Java 5 wie Generics, Autoboxing und Varags.
Seit Spring 2.0 gibt es mit org.springframework.jdbc.core.simple.SimpleJdbcTemplate eine neue Klasse mit eine überschaubaren Anzahl Methoden für Java 5.
– Es ist eine Java 5-Fassade vor dem JdbcTemplate.
Der Konstruktor von SimpleJdbcTemplate nimmt wie JdbcTemplate eine DataSource an.
Methoden vom SimpleJdbcTemplate
int queryForInt(String sql, Object… args)
long queryForLong(String sql, Object… args)
Erfragt ein int/long über einen SQL-String und Argumenten.
List<Map<String,Object>> queryForList(String sql, Object… args)
Map<String,Object> queryForMap(String sql, Object… args)
Führt die SQL-Anfrage mit optionalen Argumenten aus.
<T> T queryForObject(String sql, ParameterizedRowMapper<T> rm, Object… args)
Die SQL-Anfrage liefert ein Ergebnis, das der Mapper auf ein Objekt überträgt.
<T> T queryForObject(String sql, Class<T> requiredType, Object… args)
Führt eine SQL-Anfrage aus und überträgt die Eigenschaften auf ein Objekt.
<T> List<T> query(String sql, ParameterizedRowMapper<T> rm, Object… args)
Der ParameterizedRowMapper überträgt Ergebnisse auf Objekte.
int update(String sql, Object… args)
Führt ein SQL-Update mit optionalen Argumenten durch.
SimpleJdbcDaoSupport
Die DAO-Klassen von Spring erlauben Zugriff auf die Template-Objekte.
Während JdbcDaoSupport ein JdbcTemplate liefert, gibt SimpleJdbcDaoSupport ein SimpleJdbcTemplate.
SimpleJdbcDaoSupport ist eine Unterklasse von JdbcDaoSupport:
org.springframework.jdbc.core.support.JdbcDaoSupport
extended by org.springframework.jdbc.core.simple.SimpleJdbcDaoSupport
AJAX in 50 simple lines: Requesting HTML and Producing JSP
First we need a simple JSP as a service.
http://localhost:8080/jsp/request.jsp?name=Chris
<%
String name = request.getParameter("name");
if ( name != null )
out.print( name.length() );
%>
The JSP simply returns the length of the string. The format is simple text, no XML.
Then we need a HTML page. It shows two text fields. One with an attached onkeyup() listener and one which displays the result, the length of the string in the first text field. The AJAX-request is inside a function called call() by the listener.
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Der Längenmann</title>
<script type="text/javascript">
var xmlHttp = false;
/*@cc_on @*/
/*@if (@_jscript_version >= 5)
try {
xmlHttp = new ActiveXObject("Msxml2.XMLHTTP");
} catch (e) {
try {
xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
} catch (e2) {
xmlHttp = false;
}
}
@end @*/
if (!xmlHttp && typeof XMLHttpRequest != 'undefined') {
xmlHttp = new XMLHttpRequest();
}
function call()
{
var value = document.getElementById("name").value;
if ((value == null) || (value == "")) return;
var url = "/jsp/request.jsp?name=" + escape(value);
xmlHttp.open("GET", url, true);
xmlHttp.onreadystatechange = update;
xmlHttp.send(null);
}
function update()
{
if ( xmlHttp.readyState == 4)
{
var response = xmlHttp.responseText;
document.getElementById("length").value = response;
}
}
</script>
</head>
<body>
Nutzung online: <b>http://localhost:8080/jsp/request.jsp?name=Chris</b>.
<form>
Name: <input type="text" name="name" id="name" onKeyUp="call();" />
<br>
Length: <input type="text" name="length" id="length" />
</form>
</body>
</html>
The best solution is to use an AJAX framework like prototype, but this is only a very simple example „how this stuff works“ (is there a copyright on the sentence?)
Restart a died Thread
If a Thread dies the system can inform you via a Thread.UncaughtExceptionHandler. You can use it to restart a Thread:
import java.awt.Frame;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.Thread.UncaughtExceptionHandler;
import javax.swing.JDialog;
import javax.swing.JTextArea;
public class Main
{
public static void main( String[] args )
{
ThreadHandler.startThread();
}
}
class ThreadHandler
{
static void startThread()
{
Thread t = new Thread( new MyCommand() );
UncaughtExceptionHandler ueh = new MyUncaughtExceptionHandler();
t.setUncaughtExceptionHandler( ueh );
t.start();
}
static class MyCommand implements Runnable
{
public void run()
{
int i = 0;
System.out.println( 12 / i ); // Provoke a RuntimeException
}
}
}
class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler
{
public void uncaughtException( Thread thread, Throwable throwable )
{
StringWriter stringWriter = new StringWriter();
PrintWriter w = new PrintWriter( stringWriter );
throwable.printStackTrace( w );
JDialog d = new JDialog( new Frame(), true );
d.add( new JTextArea( stringWriter.toString() ) );
d.setTitle( thread.getName() + " --- " + throwable.getMessage() );
d.pack();
d.setVisible( true );
ThreadHandler.startThread(); // Restart Thread
}
}
Buchkritik "Java 6 Plattform Revealed"
Ein frühes Buch (Juni 2006) zu Java 6, aber es zeigt alle wesentlichen Eigenschaften der neuen Version. Sehr gut gefallen hat mit die Beschreibung von CookieHandler, CookieManager & Co. KG, etwas, was ich noch nie ordentlich erklärt gefunden habe. Der einzige Fehler, der mir aufgefallen ist, befindet sich auf Seite 111, wo eine Variable statt rs (für ResultSet) fälschlicherweise st heißt. Eine präzisere Beschreibung für Web Services und JAXB liefert das Buch leider nicht — nur ein paar Standard-Beispiele und eine schlaffe Aufzählung der Annotationen — aber dafür gibt es ja andere Bücher aus der Ecke der Java EE Literatur. Auch StAX ist nicht sonderlich präzise und späteren Neuerungen, wie Java DB oder GroupLayout finden keine Erwähnung. Das gleiche gilt für neue Funktionen copySign(), getExponent(), nextAfter(), nextUp(), scalb() aus Math/StrictMath. Eigentlich wäre jetzt Zeit für ein Buch-Update.
John Zukowski. APress. ISBN 1590596609. 220 Seiten
Smartest/shortest way to write a string to a file?
Without exception handling:
new FileWriter(„c:/test.txt“).append(„Bo“).close();
ROME: Lesen/Schreiben von Atom/RSS-Feeds
Rome ist eine Bibliothek zum Lesen/Schreiben/Konvertieren von Atom/RSS-Feeds in den Versionen
- RSS 0.90
- RSS 0.91 Netscape
- RSS 0.91 Userland
- RSS 0.92/RSS 0.93/RSS 0.94
- RSS 1.0/RSS 2.0
- Atom 0.3/Atom 1.0
Ein Beispiel ist schnell programmiert und die API (Doku hier) ist relativ einfach.
package com.sun.syndication.samples;
import java.io.FileWriter;
import java.net.URL;
import java.util.List;
import com.sun.syndication.feed.synd.SyndContent;
import com.sun.syndication.feed.synd.SyndEntry;
import com.sun.syndication.feed.synd.SyndFeed;
import com.sun.syndication.io.SyndFeedInput;
import com.sun.syndication.io.XmlReader;
public class FeedReader
{
@SuppressWarnings("unchecked")
public static void main( String[] args )
{
try
{
URL feedUrl = new URL( "http://javainsel.blogspot.com/atom.xml" );
SyndFeedInput input = new SyndFeedInput();
SyndFeed feed = input.build( new XmlReader( feedUrl ) );
StringBuilder sb = new StringBuilder( 1024 );
for ( SyndEntry syndFeed : (List<SyndEntry> )feed.getEntries() )
{
sb.append( "<b>" + syndFeed.getTitle() + "</b>" );
sb.append( "<blockquote>" + ((SyndContent)syndFeed.getContents().get(0)).getValue() + "</blockquote>" );
}
// JFrame f = new JFrame( "Einfache Feed Anzeige" );
// f.setDefaultCloseOperation( WindowConstants.EXIT_ON_CLOSE );
// JEditorPane editor = new JEditorPane( "text/html", sb.toString() );
// editor.setEditable( false );
// f.add( new JScrollPane(editor) );
// f.setSize( 600, 400 );
// f.setVisible( true );
FileWriter w = new FileWriter( "c:/out.html" );
w.write( sb.toString() );
w.close();
}
catch ( Exception e )
{
e.printStackTrace();
}
}
}
Wenn die JEditorPane besser wäre, könnte man auch (schnell) was sehen. Daher schreibt das Programm eine selbst generierte HTML-Seite ins Dateisystem.
Buchkritik zu "Pro Apache Geronimo" von Kishore Kumar
Pro Apache Geronimo. Open Source Lightwave J2EE Container
Kishore Kumar. apress. ISBN 1590596420. April 2006. 252 Seiten
Ich habe zwar Apache Geronimo nicht im Einsatz (und kann mir auch nicht vorstellen, den Server in der nahen Zukunft zu nutzen, da die Verbreitung zu gering ist), aber dennoch wollte ich etwas über die Architektur kennen lernen. Kumar vermittelt zunächst die Basis vom Geronimo Application-Server 1.x und stellt den Kern, einen IOC-Container, der GBeans verwaltet, vor. Er geht kurz über die üblichen Bean-Typen und zeigt, wie sie deployed werden und welche spezifischen Deployment-Deskriptoren es gibt. Gut gefallen hat mir ein Kapitel zur Web-Service Fassade für eine Session-Bean, und wie J2EE Connectoren programmiert und eingebunden werden. Dass das Buch jedoch etwas zu Java EE 5 enthält — so steht es auf dem Buchrücken „Includes some Java EE 5“ — ist eine glatte Lüge. Januar 2007
Character „Chunking“ bei SAX
Bei langen Zeichenketten ist es nicht zwingend, dass sie in einem Rutsch der characters() Methode übergeben werden.
<TSeq_sequence>
TAACCCTAACCCTAACCCTAACCCTAAC
</TSeq_sequence>
Denkbar ist hier ein (skizzierter) Aufruf von
characters(„T“);
characters(„AACCCTAAC“);
characters(„CCTAACCCTAACCCTAAC“);
Die Anwendung muss mit Character „Chunking“ rechnen und damit umgehen. Man kann es nicht abstellen! Viele Parser teilen die CDATA-Sektion auf, so dass nach jedem Carriage Returns einmal characters() aufgerufen wird.
Eine Lösung ist, die Zeichen zwischenzuspeichern, etwa in einem StringBuilder/StringBuffer.
public void characters(char[] ch, int start, int length)
throws SAXException {
currentText.append( ch, start, length );
}
Wenn endElement() aufgerufen wird, kann man den Puffer auswerten.
Eine interessante Idee ist, einen Delegate zu bauen, der sich für den SAX-Parser wie ein ContentHandler verhält, aber characters() abfängt und die Zeichen intern speichert. Alle anderen Methoden wie endDocument(), … gehen zum Original.
Zuvor jedoch werden die Methoden „flushen“, also ein Aufruf von characters() auf dem Original durchführen und alle Zeichen übergeben.
Anschauen kann man eine solche unter http://koders.com/java/fid607C6038FB8FD26D12EED5B32042D3EF48038AA7.aspx. Die Implementierung steht unter der Apache-Lizenz.
/*
* Copyright 2004 Outerthought bvba and Schaubroeck nv
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.outerj.daisy.htmlcleaner;import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
import org.xml.sax.Locator;
import org.xml.sax.Attributes;class MergeCharacterEventsHandler implements ContentHandler {
private ContentHandler consumer;
private char[] ch;
private int start = 0;
private int length = 0;public MergeCharacterEventsHandler(ContentHandler consumer) {
this.consumer = consumer;
}public void characters(char ch[], int start, int length) throws SAXException {
char[] newCh = new char[this.length + length];
if (this.ch != null)
System.arraycopy(this.ch, this.start, newCh, 0, this.length);
System.arraycopy(ch, start, newCh, this.length, length);
this.start = 0;
this.length = newCh.length;
this.ch = newCh;
}private void flushCharacters() throws SAXException {
if (ch != null) {
consumer.characters(ch, start, length);
ch = null;
start = 0;
length = 0;
}
}public void endDocument() throws SAXException {
flushCharacters();
consumer.endDocument();
}public void startDocument() throws SAXException {
flushCharacters();
consumer.startDocument();
}public void ignorableWhitespace(char ch[], int start, int length) throws SAXException {
flushCharacters();
consumer.ignorableWhitespace(ch, start, length);
}public void endPrefixMapping(String prefix) throws SAXException {
flushCharacters();
consumer.endPrefixMapping(prefix);
}public void skippedEntity(String name) throws SAXException {
flushCharacters();
consumer.skippedEntity(name);
}public void setDocumentLocator(Locator locator) {
consumer.setDocumentLocator(locator);
}public void processingInstruction(String target, String data) throws SAXException {
flushCharacters();
consumer.processingInstruction(target, data);
}public void startPrefixMapping(String prefix, String uri) throws SAXException {
flushCharacters();
consumer.startPrefixMapping(prefix, uri);
}public void endElement(String namespaceURI, String localName, String qName) throws SAXException {
flushCharacters();
consumer.endElement(namespaceURI, localName, qName);
}public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException {
flushCharacters();
consumer.startElement(namespaceURI, localName, qName, atts);
}
}
Prefuse: Immer wieder schöne Graphen
Das „prefuse visualization toolkit“ ist eine feine API zur Informationsvisualisierung. In der visualization gallery gibt es etwa folgendes zu sehen und auszuprobieren:
Small-World Networks by Stephen Frowe Ingram
Vizster by Jeffrey Heer and danah boyd
Flow Map Layout by Doantam Phan
NameVoyager by Martin Wattenberg, rebuilt by Jeffrey Heer
zipdecode by Ben Fry, rebuilt by Jeffrey Heer
RadialGraphView Demo by Jeffrey Heer
GraphView Demo by Jeffrey Heer
FisheyeMenu Demo by Jeffrey Heer
Nice Office Access: Leichter OpenOffice programmieren
Unter http://ubion.ion.ag/solutions/004niceofficeaccess gibt’s eine einfachere API unter der LGPL, um OpenOffice-Aufgaben zu erledigen. Java UNO kann zwar alles, ist aber nicht gerade einfach zu nutzen. Mit Nice Office Access (NOA) soll das ganze einfacher gehen. Es bietet einen leichtgewichtigen Wrapper für viele UNO Schnittstellen.
Da ich das noch nicht genutzt habe, sind Kommentare willkommen.