Tuesday, March 27, 2012

Apache Wicket: Stateless to Stateful

For a brand new Wicket application, it starts as Stateless.
After you add your first page to it, you can see that it is Stateless (the URL does not have any
?0, ?1, etc. in it.)
There are other posts about cookies and ;jsessionid, how Tomcat always start by passing both, url rewriting with Tuckey urlrewrite, etc.

However, most page components are Stateful so the moment you add any of these components to your page, the whole page turns Stateful and you start to see ?0, ?1, etc. in your URL.

To keep the page Stateless, you cannot use anything that records state: Ajax, Modal window, etc. can no longer be used. Instead of Form, you use StatelessForm.
But if you add any Stateful component to the StatelessForm, it turns Stateful.
Wicket-extensions add components such as StatelessAjaxFallbackLink, etc. to help but not all components could be made Stateless.

If your page is Stateless, the whole session created by Wicket is marked as temporary:
getSession().isTemporary() will return true.

To record custom session information, the standard way is to extend your own Session:

public class CustomSession extends WebSession {

private UserInfo user;
public CustomSession(Request request) {
super(request);
}

// Java 1.5 Covariance
public static CustomSession get() {
return (CustomSession) Session.get();
}

... getter and setter for user
}

In your WebApplication, you override newSession()

@Override
public Session newSession(Request request, Response response) {
return new CustomSession(request);
}

and in your code, you do

CustomeSession mySession = CustomSession.get();

Stateless page is useful.
For example, it makes sense that a Login page should be Stateless.

But if the page is Stateless, since the session will be marked temporary,
a new session is always re-created whenever you go to the next page.
You will lose your custom information with a Stateless page.

You want to switch from Stateless to Stateful before you navigate to the next page.

In your code, you can do this:

// User authenticated
if (getSession().isTemporary()) {
getSession().bind();
}
and then you start passing custom information around:
CustomSession.get().setUser(user);

Monday, May 10, 2010

WBI - Getting XML content

One problem with WBI is that the XML messages must follow its format (with its own header elements) or it will not be recognized properly and the content is just dumped to a BLOB.
But we have other programs that simply write their XML messages to our queues so we need to get the contents out.

Since WBI always passes you the root MbElement, the content can be retrieved this way:

MbElement data = (MbElement) ((List)root.evaluateXPath("BLOB/BLOB")).get(0);
String dataStr = new String((byte[])data.getValue());

Monday, November 23, 2009

JBoss setup for Solaris

Old style, before SMF

/etc/init.d/jboss
====================================
#!/sbin/sh
JBOSS_HOME=/u05/app/jboss-5.0.1.GA
JAVA_HOME=/u05/app/java6/jre1.6.0_02
case "$1" in
start)
cd $JBOSS_HOME/bin
echo "Staring jboss daemon: "
nohup $JBOSS_HOME/bin/run.sh &
;;
stop)
cd $JBOSS_HOME/bin
echo "Stopping jboss daemon: "
$JBOSS_HOME/bin/shutdown.sh -S
;;
restart)
cd $JBOSS_HOME/bin
echo "Restarting jboss daemon: "
$0 stop
sleep 2
$0 start
;;
*)
echo "Usage $0 {startstoprestart}"
exit 1
;;
esac
exit 0
===================================
/etc/rc3.d/S99jboss -> /etc/init.d/jboss
/etc/rcS.d/K07jboss -> /etc/init.d/jboss

With Solaris 10 SMF

The Solaris 10 OS uses the Service Management Facility (SMF) to handle services.

Create a service manifest file:/var/svc/manifest/network/jboss4.xml




name='network/jboss'
type='service'
version='1'>




name='fs'
grouping='require_all'
restart_on='none'
type='service'>
value='svc:/system/filesystem/local' />




grouping='require_all'
restart_on='none'
type='service'>
value='svc:/network/loopback' />


name='ssh_multi-user-server'
grouping='optional_all'
restart_on='none'>
value='svc:/milestone/multi-user-server' />


type='method'
name='start'
exec='/lib/svc/method/svc-jboss4 start'
timeout_seconds='-1'>




type='method'
name='stop'
exec=':kill'
timeout_seconds='-1'>


type='method'
name='restart'
exec='/lib/svc/method/svc-jboss4 restart'
timeout_seconds='-1'>









Now create your "Service Method File" in /lib/svc/method called svc-jboss4:

#!/usr/bin/sh
# William Pool (Puddle) 01/05
# SMF Method file for JBoss
# E-mail: puddle@flipmotion.com
#
# NOTE: If you start JBoss with su -c 'cmds' instead
# You "will" be able to start and stop it via root using the method file
# itself. However, it "does" confuse SMF. It's best to have the straight
# commands and let the actual SMF service do the user rights and permissions
# of execution. You've been WARNED! Use su -c 'cmds' at your own risk!
#
. /lib/svc/share/smf_include.sh
case $1 in
'start')
echo "starting jboss.."
/opt/software/jboss-4.0.1/bin/run.sh & >/dev/null 2> /dev/null
;;
'stop')
echo "stopping jboss.."
/opt/software/jboss-4.0.1/bin/shutdown.sh -S &
pkill java
;;
'restart')
stop
# give stuff some time to stop before we restart
sleep 35
# protect against any services that can't stop before we restart (warning
this kills all Java instances running as 'jboss' user)
pkill java
start
;;
*)
echo "Usage: $0 { start stop restart }"
exit 1
;;
esac
#---EOF

Now fix the permissions for the two files created:

chown root:bin /lib/svc/method/svc-jboss4
chmod 555 /lib/svc/method/svc-jboss4
chown root:sys /var/svc/manifest/network/jboss4.xml
chmod 444 /var/svc/manifest/network/jboss4.xml

Import the service into the service repository:
# svccfg import /var/svc/manifest/network/jboss4.xml
Enable the service:
# svcadm -v enable jboss

Friday, June 26, 2009

Dynamically add jar for JDBC

Java Almanac (exampledepot.com) has example of loading class not in the classpath. For example, if I want to load driver class from the Oracle JDBC Driver jar:
File file = new File("C:\\oracle\\ora92\\jdbc\\lib\\ojdbc14.jar");
// file.toURL() directly is unsafe - does not escape characters illegal in URLs
URL url = file.toURI().toURL();
URL[] urls = new URL[]{url};
ClassLoader cl = new URLClassLoader(urls);
Class cls = cl.loadClass("oracle.jdbc.driver.OracleDriver");

However, if you then call
Class.forName("oracle.jdbc.driver.OracleDriver");
Connection con = DriverManager.getConnection(…);
The calls will fail.

How do tools such as DBVisualizer dynamically add jar files to the classpath and then work with JDBC? DBVisualizer even figures out there are 2 driver classes in ojdbc14.jar and allows you to select which one to load.
This part is easy. It was looking for files like %Driver.class:
JarFile jar = new JarFile(file);
Enumeration allFiles = jar.entries();
while (allFiles.hasMoreElements()) {
JarEntry jarEntry = allFiles.nextElement();
String name = jarEntry.getName();
if (name.endsWith("Driver.class")) {
System.out.println(jarEntry.getName());
}
}

But how to add the jar to classpath so that Class.forName(..) will work?

The important thing to note is that Class.forName() is using the SystemClassLoader.
The groovy Sql class implements loadDriver with:
Class.forName(driverClassname);
and then the thread context class loader ...
Thread.currentThread().getContextClassLoader().loadClass(driverClassName);
and then the class loader that loaded the Sql class itself ...
Sql.class.getClassLoader().loadClass(driverClassName);

So the trick is really to modify the System Class Loader.
http://forums.sun.com/thread.jspa?threadID=300557&start=0&tstart=0 provides a good solution.

---------------------------------------------------------------------------------------
import java.net.URL;
import java.net.URLClassLoader;
import java.io.IOException;
import java.io.File;
import java.lang.reflect.Method;

/**
* The System Class Loader (ClassLoader.getSystemClassLoader()) is a subclass of URLClassLoader.
* It can therefore be casted into a URLClassLoader and used as one.
*
* URLClassLoader has a protected method addURL(URL url),
* which you can use to add files, jars, web addresses - any valid URL in fact.
*
* Since the method is protected you need to use reflection to invoke it.
*/
public class ClasspathAppender {

private static final Class[] parameters = new Class[]{URL.class};

public static void addFile(String s) throws IOException {
File f = new File(s);
addFile(f);
}//end method

public static void addFile(File f) throws IOException {
//addURL(f.toURL());
addURL(f.toURI().toURL());
}//end method


public static void addURL(URL u) throws IOException {

URLClassLoader sysloader = (URLClassLoader)ClassLoader.getSystemClassLoader();
Class sysclass = URLClassLoader.class;

try {
Method method = sysclass.getDeclaredMethod("addURL",parameters);
method.setAccessible(true);
method.invoke(sysloader,new Object[]{ u });
} catch (Throwable t) {
t.printStackTrace();
throw new IOException("Error, could not add URL to system classloader");
}//end try catch

}//end method

}//end class

Monday, September 22, 2008

Accessing DBF and MDB files

I was tasked to interface with some legacy systems and they were written in Visual FoxPro. Essentially my Java code has to select and insert records from/into DBF files. I started to search for 3rd party Java libraries but then I thought "Microsoft has perfectly good drivers for Visual FoxPro. Why not use them?" Even though these are ODBC drivers, I can "bridge" them using the JDBC-ODBC bridge.

The solution became very simple - as long as you figure out the correct URL:
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
java.sql.Connection conn = java.sql.DriverManager.getConnection(
"jdbc:odbc:DRIVER={Microsoft Visual FoxPro Driver};SourceType=DBF;SourceDB=C:/",
"admin", "");

To figure out the URL, I created File DSNs using the ODBC Data Source console and then looked at the files under
C:\Program Files\Common Files\ODBC\Data Sources

Similar setups can be used for Microsoft Access MDB files. The URL is:
Jdbc:odbc:DRIVER={Microsoft Access Driver (*.mdb)};DBQ=C:\test\test.mdb

Wednesday, July 23, 2008

Date constant with SQL*Loader

How to put a hard coded date (with its own date format, say, YYYY-MM-DD) into one of the fields you are loading with SQL*Loader?

I tried something like
PUT_DATE DATE(10) 'YYYY-MM-DD' "'2008-08-01'"
and it worked on Windows.
But then I found out later that it did not work on Solaris.

The format that works for both is simply:
PUT_DATE "to_date('2008-08-01','YYYY-MM-DD')"

I tried also
PUT_DATE CONSTANT "to_date('2008-08-01','YYYY-MM-DD')"
and it did not work.

Wednesday, August 8, 2007

WBI ESQL - only 1 level of function call?

The forecast data exported by i2 are fixed width decimals (10,3)
e.g. WK1=' 123.000', WK2=' 89.000', etc.
.000 is redundant as they only contain integer data.

So the ESQL code to insert them into an Oracle table with Integer fields was:

PASSTHRU('INSERT INTO ...',
CAST(TRIM(WK1) AS INTEGER),
CAST(TRIM(WK2) AS INTEGER),
...);
The idea is simple.
TRIM(' 123.000') becomes '123.000' and then it is CASTed as integer 123.
You cannot call CAST(' 123.000' AS INTEGER) directly as the leading spaces will throw errors.

However, this always silently failed. No records were inserted into Oracle.
The trace shows:
Evaluating expression TRIMBOTH( BOTH FROM ' 123.000')
The result was '123.000'
But where is the CAST?
It looks like WBI is not capable of evaluating further and call CAST.

I had to change the code to:

DECLARE pattern CHARACTER '##########.000';
PASSTHRU('INSERT INTO ...',
CAST(WK1 AS INTEGER FORMAT pattern),
CAST(WK2 AS INTEGER FORMAT pattern),
...);

Then the trace becomes:
Evaluating expression CAST( ' 123.000' AS INTEGER FORMAT '##########.000')
The result was 123
and everything worked.