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);
}

Samstag, 10. Oktober 2009

Set Apache Element Construction Set to pretty print

Unfortunately there is no easy way to overwrite the default settings
for the Element Construction Set to make it print pretty HTML.

After playing around with the only official way which is to set a comandline option:
For example, java -Decs.properties="my.ecs.properties"
and trying
System.setProperty("ecs.properties", "ecs.properties");

I fell back to using reflection.

This little piece of code does the trick:


try {
Field fld = ECSDefaults.class.getDeclaredField("defaults");
fld.setAccessible(true);
ECSDefaults ed = (ECSDefaults) fld.get(null);
Field pp = ECSDefaults.class.getDeclaredField("pretty_print");
pp.setAccessible(true);
pp.setBoolean(ed, Boolean.TRUE);

} catch (Exception ex) {
Logger.getLogger(Report.class.getName()).log(Level.SEVERE, null, ex);
}

IMPORTANT: Add this BEFORE you construct your document,
i.e.: Html h = new Html();
otherwise it doesn't work.

Sonntag, 24. Mai 2009

Jboss Tools & Seam

I wanted to work with JSF ever since I had used Sun's Java Studio Creator back when it was in early access.

WOW, I thought to myself, finally a WYSIWYG IDE for web applications.
I was blown away...

But soon, working on a test project, the IDE bugs haunted me, not even going away with the final and updated version of the creator...

When it was merged into NetBeans I tried it again, but something didn't feel right... Sun's enhanced JSF library ( Woodstock ) was very propriatory and the future seemed to be JSF and JPA.
But the JSF/JPA project from NetBeans was without visual help...

Also, pure JSF/JPA has a very tricky way to get paging working with very large datasets which was not drag&dropable in NetBeans. ( JSF DataModel )

Hmm, I was unhappy, what was left of Sun's Java Studio Creator's promise ?

Searching the web for JSF libraries I found ICEFaces.
They didn't solve the paging problem, but had very powerful AJAX features on top of JSF.
Later I even found a promising paging demo in the then unreleased 1.8 examples... I was getting hopeful again.

I played around with ICEFaces using the Eclipse plugins for two days just to understand the basics... quite a steap learning curve.
( Keep in mind I never wrote JSF by hand before, as Creator did all of that for me... )

Hmm that wasn't perfect either, why do I need managed beans if I have an entity bean with all the same values ?

That was when I found Seam and its way of getting rid of much config XML and those stupid managed beans. I had heard about it before so I decided to give it a try.

Cool, there are even eclipse plugins for it, the JBoss Tools !

But WOW, they are overwhelming... so many new options, so many new resources.

I needed to take a step back, so I looked for a Seam book and found the so far, very good to read Seam in Action from Manning ( http://mojavelinux.com/seaminaction )

But the book used the seam-gen command line tools... not what I wanted.
But hey, I gave it a try and it worked, but still, I wanted to work in a IDE and have tool support. ( Yes, I know I can import a seam-gen project... )

So I went back to the IDE and startd to play around. At least now I had a better understanding of the terminology. So the book helped me alot to get myself comfortable with the tools. ( BTW: http://www.jboss.org/tools )

The online documentation for the tools don't help at all with what to do after a project is generated... How to turn in it into your project.

So more playing around was necesary, and boy was it painful.
Let me give you some of the most important things I learnd so far:


1. Use old stuff !

Use the last version of: Jboss application server ( 4.2.3 not 5.X), Seam ( 2.0.X maybe 2.1 ), Java (1.5 not 1.6)

If not you will be in for a lot of pain!
So much stuff goes wrong if you want to make a WAR project if you switch out one of these things with a current version.
( EAR works better, but overwhelmes again. )

You can use the current Jboss Tools, that's ok.


2. Don't use a DB in embedded mode.

DON'T use a java DB like HyperSonic SQL in embedded mode !
The tools and JBoss App Server NEED concurrent access and you will get so much frustrating trouble if you do...

I used HSQLDB embedded first because it was easy... don't !
Read the docs how to run it in stand alone server mode and do it.


3. Customizing the reverse engineering process

You will never guess how to customize the Java classes and attribute names if you generate your entities from an existing DB with JBoss Tools:

In the Seam perspective there is a little new run button with a hibernate symbol. There is a sub option called: "Hibernate Code Generation Configurations..."
There you will find a configuration for your project if you executed "Seam Generate Entities" in the "New" Wizard.
Its this magic place where you can browse to a reveng.xml file which you create easily prior with the hibernate tools ( part of JBoss Tools ).
And then rerun and voilà new entities and xhtml files

( Now you just need to get rid of the old ones :-(
Also, this did not work with MySQL for some reason, but with HSQLDB )

Please JBoss guys, add a page in the Seam wizards for a reveng.xml file !!!


4. Schema Validation

If you generate a project from existing tables, the tools will set the hibernate.hbm2ddl.auto property to validate... boy will you get errors!
You will scramble, you will search, you will change java code and table column types. The easy fix ? Remove the word validate leaving emtpy "" quotes...


5. Restrictions ( This is the best ! )

Great now you have a nice little web application, much like phpMyAdmin.
Everyone can see everything... logged in users additionally can change everything.
But thats not what you want, many times you want users to see their own stuff only !

How can you do that with classes extending EntityQuery which provide the tabular data ?
Should you do something to that getEjbql() function maybe ?
No, add restrictions !

In the generated class you will see a RESTRICTIONS constant.
The trick is to know that you can add any restriction to that array and Seam will use and add that restriction if and only if the expression language (EL) variable in the string has data.
So in my authenticate method I simply set a variable in a new session scoped component which I use in my restriction.

WOW, I found this 3 days trying to find a solution for this.
( Yeah, I bet the book explains it later too... but I'm only in chap 3 )

I may write a little tutorial on how to make a Seam WEB project your own.
Stay tuned.