Freitag, 27. November 2009

Turn JBoss AS into a HSQLDB server

Developing with Seam its nice to have easy access to a test DB.
Hypersonic SQL fits the need nicely.
Best of all - its distributed with JBoss AS already.

Before now, I used the supplied hsqldb-ds.xml and modified it to my needs,
inlcuding enabling the hsqldb over tcp mbean.

But it was painful to create a new dummy Seam project with the JBoss Tools
just to switch the datasource file later.

Then it occured to me:

Why not just copy the mbean part into a new service.xml file ?

Then I can use a persisting hsqldb server in tcp mode and create new
Seam projects with the real datasource from the start.

So without further ado here is the contents of my new hsqldb-service.xml file:

<?xml version="1.0" encoding="UTF-8"?>
<server>
<mbean code="org.jboss.jdbc.HypersonicDatabase" name="jboss:service=HypersonicDB">
<attribute name="Port">9001</attribute>
<attribute name="BindAddress">${jboss.bind.address} </attribute>
<attribute name="Silent">true</attribute>
<attribute name="Database">default</attribute>
<attribute name="Trace">false</attribute>
<attribute name="No_system_exit">true</attribute>
</mbean>
</server>



Update:
Use 127.0.0.1 instead of ${jboss.bind.address}.
Makes it easier to create ds.xml files for and makes it more secure.


<?xml version="1.0" encoding="UTF-8"?>
<server>
<mbean code="org.jboss.jdbc.HypersonicDatabase" name="jboss:service=HypersonicDB">
<attribute name="Port">9001</attribute>
<attribute name="BindAddress">127.0.0.1</attribute>
<attribute name="Silent">true</attribute>
<attribute name="Database">default</attribute>
<attribute name="Trace">false</attribute>
<attribute name="No_system_exit">true</attribute>
</mbean>
</server>

Donnerstag, 19. November 2009

Good source code is the best documentation

There, I said it... but before you hunt me out of the village, let me elaborate.

The way picture editing software sharpens images, is by generating a blurred
or smoothed version of the picture and then calculating all the differences
to the original picture pixels. It then multiplies the differences by a constant
factor based on your settings. The product is then added to the blurred picture
pixels, resulting in a sharper image than the original.

Now, if you take a normal piece of source code and run it through an obfuscator
you kinda get a blurred version of the source code.

My thesis is, that if you look at the differences of the original and the obfuscated code
and enhance the differences, you get self explanatory code.

What does an obfuscator do with f.book(p) ?
=> a.b(c)

So the sharper version of the code would be:
flight.book(passenger);

My rules of thumb are:

1. Make short functions ( one task per function )
2. give function and parameter names explanatory names
3. don't be afraid to refactor if you find a limitation in your code
4. Read Java Concurrency in Practice

Sonntag, 8. November 2009

Hack Java 6 to let SOAP headers for web services be set

Java 6 (and NetBeans) make it extremely hard to let people set SOAP headers.

( UPDATE AT END )

How to normally do it is outlined in the metro guide:
https://metro.dev.java.net/guide/SOAP_headers.html

and looks like this:
WSBindingProvider bp = (WSBindingProvider)port;
bp.setOutboundHeader( Headers.create(new QName("simpleHeader"),"stringValue") );

( Hm, in Java 6 there is only bp.setOutboundHeaders with an "s" at the end... )
Problem is the WSBindingProvider is in an internal package in Java 6.
So javac or NetBeans won't let you compile the code.
( Eclipse lets you btw. if you allow internal class usage in the preferences. )

So what can we do ?

1. Maybe this: http://devplace.wordpress.com/2007/09/24/adding-soap-header-in-java/
in combination with:

BindingProvider bp = (BindingProvider) port;
bp.getBinding():
...

2. Fight the authority and use reflection once again:

public static void setHeader(MyPortType port, String session) throws Exception {
Method[] methods = port.getClass().getMethods();
for (Method method : methods) {
if (method.getName().equals("setOutboundHeaders")) {
Class<?>[] parameterTypes = method.getParameterTypes();
for (Class<?> class1 : parameterTypes) {
if (class1.getName().endsWith(".List")) {
Object h = getHeader(session);
List l = new ArrayList();
l.add(h);
method.invoke(port, l);
return;
}
}
}
}
}


public static Object getHeader(String session) throws Exception {
Class<?> header = Class.forName("com.sun.xml.internal.ws.api.message.Headers");
Method[] methods = header.getDeclaredMethods();
for (Method method : methods) {
Class<?>[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length == 2 && parameterTypes[0].getName().endsWith("QName")) {
return method.invoke(null, new QName("http://schemas.domain.com/2005/01/Product/types", "session"),
session);
}
}
return null;
}
The method getHeader acquires an instance of forbidden Headers class and invokes
the static method "create" to return a new Header object.
(In my code it is only called from the other method "setHeader".)

The method setHeader searches for a method called "setOutboundHeaders"
in the port object which accepts a List object as its parameter.
Then it acquires a new Header object, stuffes it in an ArrayList
and invokes the setOutboundHeaders method.
Voilà.

( Yeah I know I could make better use of the parameter method search, but hey... )

UPDATE:
Here are the refined, clean, loopless methods:


public static Object getHeader(String session) throws Exception {
Class header = Class.forName("com.sun.xml.internal.ws.api.message.Headers");
Method method = header.getDeclaredMethod( "create", QName.class, String.class);
return method.invoke(null, new QName("http://schemas.domain.com/2005/01/product/types", "session"), session);
}

public static void setHeader(MyPortType port, String session) throws Exception {
port.getClass().getMethod("setOutboundHeaders", List.class).invoke(port, Arrays.asList(getHeader(session)));
}


public static void setURL(MyPortType port, String url) {
BindingProvider bp = (BindingProvider) port;
Map rc = bp.getRequestContext();
rc.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, url);
}