26 September 2006

Up arrow in Java console input on Linux

[edit] Whaddaya know, this is documented here: http://wiki.python.org/jython/ReadlineSetup

So I was running Jython on Linux which has a console application for entering Jython commands. Unfortunately pressing the up-arrow did not recall the last command in the history. Instead, it printed the escape code for up-arrow (^[[A).

I've seen this problem before on Linux. Not sure if it is limited to Java console applications. I'm not even sure why it occurs. Perhaps something to do with buffered input?

Anyway, I did find a solution: JLine. You don't even have to modify a program to get it to work. You can wrap the invocation of the Java main class as follows:


java -cp jline-0.9.9.jar jline.ConsoleRunner MyMainClass


So my command line for Jython became:

java -classpath "jline-0.9.9.jar:jython.jar:$CLASSPATH" jline.ConsoleRunner org.python.util.jython


You can simply edit the Jython startup script to add the JLine jar and insert the jline.ConsoleRunner before the jython main class.

Up-arrow now recalls the last command. Left, right and down arrow work too.

18 September 2006

Enabling Oracle Managed Files

It turns out I was wrong about Oracle XE not supporting Oracle Managed Files. It is just turned off by default. Here is how to turn it on:


sqlplus system@XE
...
SQL> alter system set db_create_file_dest='/usr/lib/oracle/xe/oradata/';

System altered.


You can confirm it works with:

SQL> create tablespace my_test_tablespace datafile size 1M;

Tablespace created.

SQL> select file_name from dba_data_files;

FILE_NAME
--------------------------------------------------------------------------------
/usr/lib/oracle/xe/oradata/XE/users.dbf
/usr/lib/oracle/xe/oradata/XE/sysaux.dbf
/usr/lib/oracle/xe/oradata/XE/undo.dbf
/usr/lib/oracle/xe/oradata/XE/system.dbf
/usr/lib/oracle/xe/oradata/XE/datafile/o1_mf_my_test__2jxoyjpr_.dbf

SQL> drop tablespace my_test_tablespace;

Tablespace dropped.

SQL> exit

Oracle 10g Standard Edition on Ubuntu

I've been using Oracle XE on Ubuntu 6.06 (installed from http://oss.oracle.com/debian). It works fine. However, I was trying to create a database from scripts provided to me that assumed Oracle Managed Files were supported.

That is, the scripts attempted to create a tablespace without explicitly naming the datafile.

This failed on XE. I figured I could install Oracle 10g Standard Edition. However, the installer wouldn't even run. It fails after performing a distribution check.

You can workaround this issue with the following command to run the installer:

./runInstaller -ignoreSysPrereqs

The only other issue I encountered is documented here:
http://forums.oracle.com/forums/thread.jspa?threadID=413032&tstart=0

Make sure that you either remove Oracle XE or override $ORACLE_HOME to point to your new Oracle home before executing those commands.

This also seems to be a good reference http://www.dizwell.com/prod/node/52

13 September 2006

Unnecessary transitive Maven2 dependencies considered harmful

While deploying an EAR, I kept getting this error when instantiating my Spring beans which happen to use Hibernate to talk to the database:


java.lang.ClassCastException: org.jboss.tm.TxManager
at org.hibernate.transaction.JNDITransactionManagerLookup.getTransactionManager(JNDITransactionManagerLookup.java:23)
at org.hibernate.impl.SessionFactoryImpl.(SessionFactoryImpl.java:302)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1176)
at org.springframework.orm.hibernate3.LocalSessionFactoryBean.newSessionFactory(LocalSessionFactoryBean.java:825)


Of course, good old ClassCastException doesn't tell me what class it expected, only what class it got. From looking at the javadocs, its expecting a javax.transaction.TransactionManager. However, org.jboss.tm.TxManager does, in fact, implement that interface.

So what gives? Well, its going to be a classloader issue. Again, ClassCastException (or most likely the caller) fails to realize that a class's name isn't enough to uniquely identify a class. The classloader that loads it is very important.

Given that I'm using Maven2, I figured that I must be including a copy of javax.transaction.TxManager somewhere.

This command will list the contents of all jars:
find -name '*.jar' -exec unzip -l '{}' \;

Redirect that to a text file, then grep for javax/transaction/TransactionManager

I found I was bundling jta-1.0.1B.jar in my ear. How did that get there? Transitive dependencies, of course. I didn't have to guess too hard as to who might be including that: Hibernate. Now, I thought I was already exlcuding the jta dependency. Turns out I'd only done it in a sub-project. I decided to fix it for all hibernate dependencies in my project by adding it in the parent pom.xml:


<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate</artifactId>
<version>3.1.3</version>
<exclusions>
<!--
This is container-provided; we don't need the dependency here;
In fact, java.lang.ClassCastException: org.jboss.tm.TxManager may occur if we
accidentally include a jta jar
-->
<exclusion>
<groupId>javax.transaction</groupId>
<artifactId>jta</artifactId>
</exclusion>
</exclusions>
</dependency>


With that, the problem is solved.

Maven2 antrun plugin and xdoclet issues

I was adding a custom ant task to maven2 pom.xml. It worked just fine when I built from the sub-project. E.g:

<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
<configuration>
<tasks>
<echo>Hello</echo>
</tasks>
</configuration>
</plugin>


However, when I stepped back up to the parent project and ran the build, it failed with:

[INFO] ------------------------------------------------------------------------
[ERROR] BUILD ERROR
[INFO] ------------------------------------------------------------------------
[INFO] Internal error in the plugin manager executing goal 'org.apache.maven.plugins:maven-antrun-plugin:1.0:run': Unable to find the mojo 'org.apache.maven.plugins:maven-antrun-plugin:1.0:run' in the plugin 'org.apache.maven.plugins:maven-antrun-plugin'
Component descriptor cannot be found in the component repository: org.apache.maven.plugin.Mojoorg.apache.maven.plugins:maven-antrun-plugin:1.0:run.


It turns out that when building from the parent build, XDOclet is used by certain sub-projects. XDoclet depends on the maven antrun plugin version 1.0. With Maven 2.0.4 the default antrun plugin is 1.1. Thus, a conflict arises.

Found this reference: http://jira.codehaus.org/browse/MANTRUN-37

While there is no fix (XDoclet can't [yet?] be modified to use antrun 1.1, there is a workaround: Simply specify version 1.0 of the antrun plugin. I did this in pluginManagement in my parent pom.xml:


<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.0</version>
</plugin>

Maven2 ejb-client woes

The Maven2 ejb plugin can automatically generate an ejb-client for you:


<plugin>
<artifactId>maven-ejb-plugin</artifactId>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
</manifest>
</archive>
<generateClient>true</generateClient>
</configuration>
</plugin>


This can be dependend-upon from other projects:


<dependency>
<groupid>com.shopzilla.ca.bidding.impl.google</groupid>
<artifactid>ejbs</artifactid>
<type>ejb-client</type>
</dependency>


Unfortunately, it often includes many classes / resources that you don't want to provide to the client. The default excludes get rid of some stuff. In my case, I had a beanRefContext.xml Spring config file in there that broke clients (since it referred to other Spring xml files that are in a server-side jar).

Supposedly you can add <clientexcludes> elements:

<clientexcludes>
<clientexclude>beanRefContext.xml</clientexclude>
<clientexclude>META-INF/ejb-jar.xml</clientexclude>
<clientexclude>META-INF/jboss.xml</clientexclude>
<clientexclude>**/mdb/</clientexclude>
</clientexcludes>


Sadly, that doesn't work with Maven 2.0.4 due to a bug in the ejb plugin: http://jira.codehaus.org/browse/MEJB-19

My workaround was to add an Ant task that rather heavy-handedly simulated this by unzipping the client jar and re-zipping with the exlcuded files. here it is:

<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
<configuration>
<!-- Manually strip client files since clientExcludes doesn't work on ejb plugin -->
<tasks>
<property name="ejb-client-excludes-list" value="beanRefContext.xml, META-INF/ejb-jar.xml, META-INF/jboss.xml, **/mdb/" />
<echo>Manually excluding from ejb client jar: ${ejb-client-excludes-list}</echo>
<property name="ejb-client-expanded.dir" value="${project.build.directory}/tmpClient" />
<property name="ejb-client-artifact-name"
value="${project.build.directory}/${project.artifactId}-${project.version}-client.jar" />
<delete dir="${ejb-client-expanded.dir}" />
<unzip src="${ejb-client-artifact-name}" dest="${ejb-client-expanded.dir}" />
<delete file="${ejb-client-artifact-name}" />
<zip destfile="${ejb-client-artifact-name}" basedir="${ejb-client-expanded.dir}"
excludes="${ejb-client-excludes-list}" />
</tasks>
</configuration>
</plugin>

01 September 2006

Sending emails in Spring using Freemarker

Create a mail sender

<bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
<property name="host" value="localhost">
<property name="port" value="25">
</bean>


Create the freemarker configuration

<bean id="freemarkerMailConfiguration" class="org.springframework.ui.freemarker.FreeMarkerConfigurationFactoryBean">
<property name="templateLoaderPath" value="/WEB-INF/mail">
</bean>


Create a class for sending the mail:

public class InfoSenderMail implements InfoSender {

private Configuration configuration;
private String freemarkerTemplate;
private MailSender mailSender;

public void send() throws SendException {
SimpleMailMessage message = new SimpleMailMessage();

// Add stuff to them model for inserting into the template
final Map model = new HashMap();
model.put("name", "Tim");

// Merge the model into the template
final String result;
try {
result = FreeMarkerTemplateUtils.processTemplateIntoString(configuration.getTemplate(
freemarkerTemplate), model);
} catch (IOException e) {
throw new SendException(
"Unable to read or process freemarker configuration or template", e);
} catch (TemplateException e) {
throw new SendException("Problem initializing freemarker or rendering template '"
+ freemarkerTemplate + "'", e);
}

// Construct and send the message
message.setTo("someone@somewhere.com");
message.setSubject("Here is the info you requested");
message.setText(result);

mailSender.send(message);
}

public void setFreemarkerMailConfiguration(Configuration configuration) {
this.configuration = configuration;
}

public void setFreemarkerTemplate(String freemarkerTemplate) {
this.freemarkerTemplate = freemarkerTemplate;
}

public void setMailSender(MailSender mailSender) {
this.mailSender = mailSender;
}

}


Define the bean; the template should be in the location specified by templateLoaderPath (e.g. /WEB-INF/mail)


<bean id="infoSender" class="InfoSenderMail"
<property name="mailSender" ref="mailSender">
<property name="freemarkerMailConfiguration" ref="freemarkerMailConfiguration">
<property name="freemarkerTemplate" value="mytemplate.txt">
</bean>


Create the template. E.g. mytemplate.txt


Hello, ${name}!


Then simply inject the bean in another class and invoke send();