@ManagedResource in Spring is really cool

First, write a class and annotate it:

package com.javatutor.spring.jmx;

import java.util.HashMap;

import java.util.Map;

import org.springframework.jmx.export.annotation.ManagedAttribute;

import org.springframework.jmx.export.annotation.ManagedOperation;

import org.springframework.jmx.export.annotation.ManagedResource;

@ManagedResource( objectName = "com.javatutor.spring.jmx:name=StringMap" )

public class StringMapImpl

{

private Map<String, String> map = new HashMap<String, String>();

@ManagedAttribute

public int getSize()

{

return map.size();

}

@ManagedOperation

public String get( String key )

{

return map.get( key );

}

@ManagedOperation

public String put( String key, String value )

{

return map.put( key, value );

}

public void remove( String key )

{

map.remove( key );

}

}

Next, write a spring config file:

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"

"http://www.springframework.org/dtd/spring-beans.dtd">

<beans>

<bean id="StringMap" class="com.javatutor.spring.jmx.StringMapImpl" />

<bean id="MBeanExporter"

class="org.springframework.jmx.export.MBeanExporter">

<property name="assembler" ref="MBeanInfoAssembler" />

<property name="autodetect" value="true" />

<property name="namingStrategy" ref="NamingStrategy" />

</bean>

<bean id="AnnotationJmxAttributeSource"

class="org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource"

/>

<bean id="NamingStrategy"

class="org.springframework.jmx.export.naming.MetadataNamingStrategy">

<property name="attributeSource" ref="AnnotationJmxAttributeSource" />

</bean>

<bean id="MBeanInfoAssembler"

class="org.springframework.jmx.export.assembler.MetadataMBeanInfoAssembler">

<property name="attributeSource" ref="AnnotationJmxAttributeSource" />

</bean>

</beans>

Now the client:

package com.javatutor;

import javax.swing.JOptionPane;

import org.springframework.context.ApplicationContext;

import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.javatutor.spring.jmx.StringMapImpl;

public class SpringWithJmxExample

{

public static void main( String[] args )

{

ApplicationContext beFac = new ClassPathXmlApplicationContext( "springJmx.xml"

);

StringMapImpl cacheBean = (StringMapImpl) beFac.getBean( "StringMap" );

cacheBean.put( "Key", "Value" );

System.out.println( cacheBean.get("Key") );

JOptionPane.showMessageDialog( null, "End" );

}

}

Start the program. Do not forgett to start with -Dcom.sun.management.jmxremote.
Start JConsole and enjoy.

 

mime4j für MS-Web-Archive MHT

Während Word und Excel beim Export einzelnde (mehr oder wenig schöne) XML-Dateien erzeugen (können), macht PPT das nicht: Es generiert entweder eine Sammlung von XML/HTML/GIF/.. Dateien, oder packt alle Dateien in ein MHT-Datei. Möchte man diese Datei auseinandernehmen, kann man gut http://mime4j.sourceforge.net/apidocs/ nutzen, denn MS bündelt die Dokumente wie eine MIME-EMail. Zunächst benötigt man einen Handler:

package TEST;

import java.io.IOException;
import java.io.InputStream;

import org.mime4j.BodyDescriptor;
import org.mime4j.SimpleContentHandler;
import org.mime4j.message.Header;

public class MyHandler extends SimpleContentHandler
{
@Override
public void headers( Header header )
{
System.out.println( header );
}

@Override
public void bodyDecoded( BodyDescriptor bd, InputStream is ) throws IOException
{
System.out.println( bd );
}
}

Der macht jetzt nicht viel, man sieht aber das Prinzip.

Der Quellcode des Parsers ist auch kurz:

package TEST;

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;

import org.mime4j.ContentHandler;
import org.mime4j.MimeStreamParser;

public class Mime4J
{
public static void main( String[] args ) throws IOException
{
ContentHandler handler = new MyHandler();
MimeStreamParser parser = new MimeStreamParser();
parser.setContentHandler( handler );
parser.parse( new BufferedInputStream( new FileInputStream( „c:/a.mht“ ) ) );
}
}

Auf der Konsole folgen dann Ausgaben wie

MIME-Version: 1.0
Content-Type: multipart/related; boundary=“—-=_NextPart_01C617A0.DCEFFF40″

Content-Location: file:///C:/EC2C4D01/a.htm
Content-Transfer-Encoding: quoted-printable
Content-Type: text/html; charset=“us-ascii“

text/html
Content-Location: file:///C:/EC2C4D01/a-Dateien/master29.htm
Content-Transfer-Encoding: quoted-printable
Content-Type: text/html; charset=“us-ascii“

text/html
Content-Location: file:///C:/EC2C4D01/a-Dateien/master29.xml
Content-Transfer-Encoding: quoted-printable
Content-Type: text/xml; charset=“utf-8″

Ein Java-Programm (nicht quelloffen), was das schon alles macht — und für Mac/Linux ganz spannend ist, ist unmhtml von http://www.joecheng.com/code/.

Spring, Hibernate und HSQLDB

Nach dem das erste Beispiel das Zusammenspiel von Spring und HSQLDB zeigte, soll das zweite Beispiel statt dem JdbcTemplate das HibernateTemplate nutzen, damit Hibernate die OR-Abbildung übernimmt. Zur Vorbereitung (und Erweiterung) des ersten Beispiels wird zunächst log4j.properties um eine Zeile erweitert:

log4j.rootCategory=ERROR, A1
log4j.logger.net.sf.ehcache=ERROR, A1

log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%m%n

Für Hibernate sind ebenso weitere Jar-Dateien nötig, so dass sich insgesamt
im Pfad befinden müssen:

  • hsqldb.jar
  • log4j-*.jar
  • spring.jar
  • common-logging.jar
  • hibernate3.jar
  • dom4j-*.jar
  • jta.jar
  • common-collections-*.jar
  • ehcache-*.jar
  • cglib-*.jar
  • asm.jar
  • antlr-*.jar

Als erstes zeigt die DAO-Implementierung die Verwendung von HibernateTemplate.

package com.javatutor.dao.hibernate;

import java.util.Collection;

import org.springframework.orm.hibernate3.HibernateTemplate;

import com.javatutor.dao.CustomerDao;
import com.javatutor.domain.Customer;

public class CustomerDaoHibernateImpl extends HibernateTemplate implements CustomerDao
{
@SuppressWarnings("unchecked")
public Collection<Customer> getCustomers()
{
return loadAll( Customer.class );
}

public Customer findCustomerById( int id )
{
return (Customer) load( Customer.class, id );
}

public void save( Customer customer )
{
saveOrUpdate( customer );
}
}

Zum Mappen der POJOs auf die Datenbank ist eine Mapping-Datei nötig, es sei denn, man arbeitet mit Java 5 Annotationen. In den Klassenpfad kommt die Datei Customer.hbm.xml.

<?xml version="1.0"?>

<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping package="com.javatutor.domain">

<class name="Customer" table="Customers" lazy="false">

<id name="id" column="Id">
<generator class="native" />
</id>

<property name="name" column="Name" />

</class>

</hibernate-mapping>

In der Spring XML-Datei müssen wir den CustomerDaoHibernateImpl anmelden und
mit einer SessionFactory versorgen.

<bean id="CustomerDaoHibernate" class="com.javatutor.dao.hibernate.CustomerDaoHibernateImpl">
<property name="sessionFactory">
<ref local="SessionFactory" />
</property>
</bean>

Die SessionFactory ist für Hibernate nötig und enthält den Hinweis auf die OR-Mappings und die DataSource. Die DataSource haben wir schon verwendet, doch damit alles persistent bleibt, soll HSQLDB diesmal ins Dateisystem schreiben.

<bean id="DataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName">
<value>org.hsqldb.jdbcDriver</value>
</property>
<property name="url">
<value>jdbc:hsqldb:file:/c/:Customers;shutdown=true</value>
</property>
<property name="username">
<value>sa</value>
</property>
<property name="password">
<value></value>
</property>
</bean>

Es ist sehr wichtig, den Parameter ;shutdown=true an die Verbindungs-URL zu setzen. Die LocalSessionFactoryBean ist der Produzent für unsere SessionFactory:

<bean id="SessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource">
<ref local="DataSource" />
</property>
<property name="mappingResources">
<list>
<value>Customer.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">
org.hibernate.dialect.HSQLDialect
</prop>
<!-- <prop key="hibernate.show_sql">true</prop>-->
<prop key="hibernate.hbm2ddl.auto">create</prop>
</props>
</property>
</bean>

Mit dem hibernate.hbm2ddl.auto ist dann auch in der Applikation das CREATE TABLE unnötig. Und das war’s dann auch schon.

JSmooth mit Ant Build Skript

JSmooth nutze ich schon eine ganze Zeit, um unter Windows ausführbare EXE zu
erzeugen. Statt der Gui lässt sich JSmooth aber auch über Ant steuern.

Zunächst muss ein neuer Ant-Task definiert werden:

<property name="jsmooth.dir" value="C:/Programme/JSmooth 0.9.7">
<taskdef name="jsmoothgen" classname="net.charabia.jsmoothgen.ant.JSmoothGen"
         classpath="${jsmooth.dir}/lib/jsmoothgen-ant.jar">

Jetzt kann man den neuen Task jsmoothgen nutzen:

<target name="build-exe-createmetadata">
  <jsmoothgen project="createmetadata.jsmooth"
              skeletonroot="${jsmooth.dir}/skeletons" />
</target>

Mit Spring und dem JdbcTemplate auf eine Hibernate-Datenbank

Das folgende Beispiel soll zeigen, wie man mit Spring arbeitet und Daten in einer relationalen Datenbank persistent macht. Um das Bespiel zum Laufen zu bringen müssen im Klassenpfad sein: hsqldb.jar, spring.jar, log4j-1.2.9.jar und common-logging.jar. Für die Log-Meldungen setzen wir in den Klassenpfad die Datei log4j.properties:

log4j.rootCategory=WARN, A1

log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%m%n

Beginnen wir mit unserem Geschäftsobjekt, einem Kunden:

package com.javatutor.domain;

public class Customer
{
private int id;

private String name;

public int getId()
{
return id;
}

public void setId( int id )
{
this.id = id;
}

public String getName()
{
return name;
}

public void setName( String name )
{
this.name = name;
}

@Override
public String toString()
{
return String.format( "Customer[id=%d, name=%s]", id, name );
}
}

Für diesen Kunden definieren wir eine DAO-Schnittstelle, die uns später Exemplare der Geschäftsobjekte gibt und die Speicherung ermöglichen.

package com.javatutor.dao;

import java.util.Collection;

import com.javatutor.domain.Customer;

public interface CustomerDao
{
Collection<Customer> getCustomers();

Customer findCustomerById( int id );

void save( Customer customer );
}

Zum Testen der Applikation beginnen wir mit einer einfach DAO-Implementierung, die Kunden in einer HashMap speichert.


package com.javatutor.dao.map;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import com.javatutor.dao.CustomerDao;
import com.javatutor.domain.Customer;

public class CustomerDaoMapImpl implements CustomerDao
{
private Map<Integer, Customer> map = new HashMap<Integer, Customer>();

public Collection<Customer> getCustomers()
{
return map.values();
}

public Customer findCustomerById( int id )
{
return map.get( id );
}

public void save( Customer customer )
{
map.put( customer.getId(), customer );
}
}

Eine Applikation wird über die Spring-Konfigurationsdatei später mit einem konkreten CustomerDao gespritzt. Die Methode haveFun() legt einige Business-Objekte an und speichert sie über das DAO.


package com.javatutor;

import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;

import com.javatutor.dao.CustomerDao;
import com.javatutor.domain.Customer;

public class Application
{
private CustomerDao customerDao;

public void setCustomerDao( CustomerDao customerDao )
{
this.customerDao = customerDao;
}

private void haveFun()
{
System.out.println( customerDao.getCustomers() );

Customer c1 = new Customer();
c1.setId( 0 );
c1.setName( "Christian Ullenboom" );
customerDao.save( c1 );

System.out.println( customerDao.findCustomerById( 0 ) );

System.out.println( customerDao.getCustomers() );

Customer c2 = new Customer();
c2.setId( 1 );
c2.setName( "Tantiana Roujitcher" );
customerDao.save( c2 );

System.out.println( customerDao.getCustomers() );
}

//

public static void main( String[] args )
{
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext( "spring.xml" );
Application bean = (Application) context.getBean( "Application" );
bean.haveFun();
}
}

Jetzt fehlt nur noch die XML-Datei für Spring:


<?xml version="1.0"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">

<beans>
<bean id="Application" class="com.javatutor.Application">
<property name="customerDao">
<ref local="CustomerDao" />
</property>
</bean>
<bean id="CustomerDao" class="com.javatutor.dao.map.CustomerDaoMapImpl" />
</beans>

Startet man das Programm, ist die Ausgabe


[]
Customer[id=0, name=Christian Ullenboom]
[Customer[id=0, name=Christian Ullenboom]]
[Customer[id=1, name=Tantiana Roujitcher], Customer[id=0, name=Christian Ullenboom]]

Prima. Es klappt. Jetzt kommt eine DAO-Implementierung für JDBC. Zunächst die Klasse.


package com.javatutor.dao.jdbc;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collection;

import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;

import com.javatutor.dao.CustomerDao;
import com.javatutor.domain.Customer;

public class CustomerDaoJdbcImpl extends JdbcTemplate implements CustomerDao
{
@SuppressWarnings("unchecked")
public Collection<Customer> getCustomers()
{
return query( "SELECT Id, Name FROM Customers", new CustomerRowMapper() );
}

public Customer findCustomerById( int id )
{
String sql = "SELECT Id, Name FROM Customers WHERE Id = ?";

try
{
return (Customer) queryForObject( sql,
new Object[] { id },
new CustomerRowMapper());
}
catch ( IncorrectResultSizeDataAccessException e ) { }

return null;
}

public void save( Customer customer )
{
if ( findCustomerById( customer.getId() ) == null )
{
Object[] args = { customer.getId(), customer.getName() };
update( "INSERT INTO Customers (Id, Name) VALUES (?,?)", args );
}
else
{
Object[] args = { customer.getName(), customer.getId() };
update( "UPDATE Customers SET Name = ? WHERE Id = ?", args );
}
}
}

class CustomerRowMapper implements RowMapper
{
public Object mapRow( ResultSet rs, int rowNum ) throws SQLException
{
Customer c = new Customer();
c.setId( rs.getInt( "Id" ) );
c.setName( rs.getString( "Name" ) );
return c;
}
}

Und in der XML-Datei ergänzen wir für den JDBC-DAO:

<bean id="CustomerDaoJdbc" class="com.javatutor.dao.jdbc.CustomerDaoJdbcImpl">
<property name="dataSource"><ref local="DataSource" /></property>
</bean>

<bean id="DataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName">
<value>org.hsqldb.jdbcDriver</value>
</property>
<property name="url">
<value>jdbc:hsqldb:mem:customers</value>
</property>
<property name="username">
<value>sa</value>
</property>
<property name="password">
<value></value>
</property>
</bean>

Damit unsere Applikation mit dem neuen JDBC-DAO gespritzt wird setzt man.

 <bean id="Application" class="com.javatutor.Application">
<property name="customerDao">
<ref local="CustomerDaoJdbc" />
</property>
<property name="dataSource">
<ref local="DataSource" />
</property>
</bean>

Gleichzeitig geben wir der Applikation eine DataSource, damit sie die Tabelle für die Datenbank anlegen kann.


public void setDataSource( DataSource dataSource )
{
new JdbcTemplate( dataSource ).execute( "CREATE TABLE Customers( Id INTEGER, Name VARCHAR(128) ) " );
}

Das gestartet Programm gibt nun die gleiche Ausgabe.