Java


11/21/2009: 1:08 pm: RobertJava, Scala

A few weeks ago I decided to learn Scala in order to develop the website for my latest side project. This led me down the road of trying to learn Google App Engine/Java, Lift, Maven, Git, ScalaTest, Spec and much more, so it’s been pretty slow going. Especially due to diversions into Cassandra, MongoDB, Tokyo Cabinet, Hadoop and HBase.

I’m a huge fan of JUnit for unit testing Java code, so I was happy when Bil Venners announced the release of ScalaTest 1.0 at a BASE meeting at Twitter. I really like being able to quickly run tests in Eclipse, but it wasn’t obvious to me how to do this with ScalaTest, especially when using the BDD styles of testing, like Spec. Creating test runners manually for each class was a deal killer. I finally found some info that mostly clarified things.

A simple example is helpful, so here you go:

import org.scalatest.Spec
import org.scalatest.junit.JUnitRunner
import org.junit.runner.RunWith
 
@RunWith(classOf[JUnitRunner])
class MinimalSpec extends Spec {
   describe("My class") {
     it("should have testable behavior like...") {
       assert(true)
     }
   }
}

After creating the class, do one of the following:

  • Right click on file in tree and select Run As -> JUnit Test
  • Select file in tree and press Alt-Command-X + T on OS X or Alt-Shift-X + T on other OSs

After the test completes, the JUnit view should display something like:

Screenshot of JUnit view after running MinimalSpec with ScalaTest

6/10/2009: 10:05 pm: RobertJava, MySQL

The C3P0 database connection pooling library has worked very well for me, but I recently ran into a problem when I wanted to log the SQL that was being generated for a PreparedStatement if an insert failed. Then, if database problems caused inserts to fail beyond an automated retry period, I could easily harvest the SQL statements from the log file and retry them later. Also, if a data problem caused the inserts to fail, I could harvest the statements from the log, use a script to adjust the data and then retry them.

C3P0 wraps the JDBC driver’s PreparedStatement class with its C3P0ProxyStatement. Unfortunately, it doesn’t override toString() to return something useful. So, you just get the default of a class name and the hash code for the instance. Not helpful.

Unfortunately, there is nothing in the API that gives you an obvious means of getting at the real PreparedStatement, which I can see in the debugger inside a private variable named inner. I knew that the rawStatementOperation() method might be the key, but it wasn’t obvious. However, a bit of googling turned up a solution in the unit test code for C3P0.

Unit tests are awesome. Not just for testing your code, but also for providing example code.

So, here’s some example code. First, I created a simple table in the test database of a local MySQL install.

create table rs (a int);

And here’s some code that connects to the database, sets up a prepared statement and then extracts the SQL from the prepared statement with the parameters bound:

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
 
import com.mchange.v2.c3p0.C3P0ProxyStatement;
import com.mchange.v2.c3p0.ComboPooledDataSource;
 
public class C3P0Demo {
 
  public static void main(String[] args) throws Exception {
 
    // Get a connection
    String url = "jdbc:mysql://localhost:3306/test";
    ComboPooledDataSource ds = new ComboPooledDataSource();
    ds.setDriverClass("com.mysql.jdbc.Driver");
    ds.setJdbcUrl(url);
    ds.setUser("myuser");
    ds.setPassword("mypassword");
    Connection conn = ds.getConnection();
 
    // Prepare the PreparedStatement
    PreparedStatement ps = conn.prepareStatement("insert into rs values (?)");
    ps.setInt(1, 10);
 
    // Extract the SQL
    String sql = "";
    try {
      C3P0ProxyStatement c3p0Stmt = (C3P0ProxyStatement) ps;
      Method toStringMethod = Object.class.getMethod("toString", new Class[] {});
      Object toStr = c3p0Stmt.rawStatementOperation(toStringMethod,
          C3P0ProxyStatement.RAW_STATEMENT, new Object[] {});
      if (sql instanceof String) {
        sql = (String) toStr;
        sql = sql.substring(sql.indexOf('-') + 1).trim() + ";";
        System.out.println(sql);
      }
    } catch (SQLException e) {
      System.out.println("Exception extracting SQL: " + e.getMessage());
    } catch (SecurityException e) {
      System.out.println("Exception extracting SQL: " + e.getMessage());
    } catch (NoSuchMethodException e) {
      System.out.println("Exception extracting SQL: " + e.getMessage());
    } catch (IllegalArgumentException e) {
      System.out.println("Exception extracting SQL: " + e.getMessage());
    } catch (IllegalAccessException e) {
      System.out.println("Exception extracting SQL: " + e.getMessage());
    } catch (InvocationTargetException e) {
      System.out.println("Exception extracting SQL: " + e.getMessage());
    }
  }
}

Inside the C3P0ProxyStatement is a com.mysql.jdbc.ServerPreparedStatement. For my example, ServerPreparedStatement.toString() returns:

com.mysql.jdbc.ServerPreparedStatement[1] - insert into rs values (10)
11/6/2008: 3:51 pm: RobertJava, Mac

After using the built-in self upgrade feature of Eclipse to go from 3.4 to 3.4.1, I was no longer able to launch Eclipse. Instead of the happy Ganymede splash screen, I got a poorly formatted dialog saying “The Eclipse executable launcher was unable to locate its companion shared library.” I discovered that the problem was that eclipse.ini had not been updated to point to the updated launcher library. Here’s how to fix it.

  1. Open a Finder Window to the Applications directory, or wherever you installed Eclipse
  2. Expand the plugins directory
  3. Scroll down to the directory starting with the name “org.eclipse.equinox.launcher.carbon.macosx_”
  4. Copy the text of the name following “carbon.”. After the update, mine was 1.0.101.R34x_v20080731.
  5. Right click Eclipse.app and select Show Package Contents
  6. Expand the Contents and MacOS directories
  7. Open eclipse.ini in a text editor
  8. Look for two lines that look like
  9. --launcher.library
    ../../../plugins/org.eclipse.equinox.launcher.carbon.macosx_1.0.100.v20080509-1800
  10. Change the part after “_” to the text you copied above
  11. Close and save changes to eclipse.ini
  12. Restart Eclipse

In case you weren’t aware of this, you can also edit eclipse.ini to change the vmargs Java will use when launching Eclipse. For example, if Eclipse itself is running out of heap, you might want to boost the min and max heap size with something like:

-Xms64m
-Xmx512m

Of course, you should also click “Show heap status” in the General section of the Preferences dialog so you can monitor how much heap is being used and can force a GC before a big compile. You shouldn’t boost the min heap size from the default, though, unless you blow past it pretty quick and experience initial sluggishness as the heap keeps getting expanded.

5/11/2008: 9:37 pm: RobertJava

I went to JavaOne last week and was once again amazed by the number of attendees. I heard someone say 15,000 people had registered. I believe it. Other programming languages like Python and Ruby might be gaining in popularity on Java, but Java is still pretty dominant.

And just like last year, the queues for getting into the sessions were chaos. The people trying to organize the queues were trying hard (well, a few were), but the space where people lined up for the biggest sessions was the least conducive to organization. Someone at Sun needs to go to a major theme park and take lots of notes. Theme parks figured this out long ago.

And then the last thing you want to hear about happening when you were recently in a crowded space with 15,000 of your best friends did happen. On Friday I got an email from Sun that about 70 of those best buddies (actually, almost all of the 70 were workers not included in the registrant head count) caught a highly contagious norovirus.

I shared my pass with some co-workers, so I was there only on Tuesday. Fortunately, none of us got sick. The best sessions I attended were:

  • More Effective Java
  • Defective Java™ Code: Turning WTF Code into a Learning Experience
  • Let’s Resync: What’s New for Concurrency on the Java™ Platform, Standard Edition
  • Real World, Not Hello World: GWT Development for Java™ Technology Shops

I was really impressed by the quality of the web apps that had been built with GWT. I co-developed an app at work using GWT, but the part of the UI I developed was not nearly as cool as what these two companies built. It made me want to do some more work with GWT.

While I was impressed with JavaFX Script, the presentation Tuesday morning went at much too slow of a pace. Although 50 minutes had elapsed, I felt like only about 15 minutes of material had been covered.

The EBay presentation was also sort of interesting, but it felt too much like “here’s a little bit of info about a lot of cool technologies we have developed, but it won’t be much use to you since there are a lot of interdependencies and it’s closed source.” In the Q&A session they said they were considering open sourcing some of it, but I would understand if they didn’t. It would probably take a very large amount of work to unwind all the internal dependencies on their other infrastructure.

8/1/2007: 11:03 pm: RobertJava, Tutorials

I’ve been meaning to try out the JPackage repository for a long time to manage the Java installations on my Fedora Linux installs at home. However, the documentation on the JPackage site is incomprehensible. I don’t see how anyone could possibly use it to install the Sun JDK for use with the JPackage repository with the nosrc rpm. It’s almost like they are daring you to use GCJ, which in my opinion, is a total waste of time. Even the instructions for the compat rpms are overly verbose.

Fortunately, I found some documentation for installing Java on CentOS. CentOS is a free-of-charge distribution of Red Hat Enterprise Linux that we use for our production servers at Voxify. Since Fedora is cut from the same cloth as RHEL and CentOS, I was hoping the instructions would work with minimal change, and I was right. A million thanks to the people who wrote that document.

I was able to follow the directions exactly as written, except for the need to first install libXp ($ sudo yum -y install libXp) and to change the 11 to 12 in the file references for the JDK, since the newest Java 5 distribution is now 1.5.0.12.

The Java 6 install is even easier, although it uses the compat rpm instead of the nosrc rpm. From my perspective, though, using the compat rpm is worth it for the time it saves.