Why you SHOULD use a case sensitive file system on your Mac

There is a lot of noise on the Internet saying case sensitive file systems on the Mac are bad. I guess if you are running Adobe apps, you can listen to that, since they are apparently developed sloppily and depend on a lack of case sensitivity. Any real developer, however, will find that the lack of case sensitivity causes problems. Here are two I recently ran into that ended up wasting a LOT of time debugging:

  • In Tapestry 5, a lot of effort has been made to make things not case sensitive. Developing a t5 app on my Mac, I had that in my head, and created some page templates using all lower case, and their Java classes in typical upper camel case. I ran the app and tested it out until I was satisfied it was working. I deployed it to the Unix dev server and it mysteriously stopped working. It turns out, it searches for page templates based on the case sensitive class name. So my Index class was looking for the file Index.tml. I had index.tml. On the Mac, the OS returns index.tml when you look for Index.tml. Worst of all, I had to use a Unix machine to rename the files in the SVN repository because the Mac refused to acknowledge any change.
  • Working on another project, I had a directory structure that had a directory in all caps. I needed to change it to lower case. Once that was done, I tried to commit the changes to SVN. I got this:

    svn: Commit failed (details follow):
    svn: Server sent unexpected return value (405 Method Not Allowed) in response to MKCOL request for ‘/svn/!svn/wrk/626a333b-f635-42b4-896e-5df2dd0475c2/ecampus/src/com’

    Not cool.

The end of Netbeans week


I have finished my week of Netbeans usage and so far am pretty impressed. I was able to go all week without using Eclipse once. A large part of that is thanks to the fact that I’ve been using Maven 2 for a large number of my most recent projects. If you have a pom.xml, Netbeans will be able to check out your project and open it without you doing a single bit of configuration. This is great, since it goes a long way in preventing IDE lock-in. If you are working on a Maven project, you can use Netbeans easily, even if everyone else is using Eclipse. I didn’t get very deep into working on non-Maven projects, so that may have been quite a bit more hassle. All in all, though, Netbeans has come a long way since the last time I gave it a serious test. Here are some of my favorite features:

  • Autocomplete seems quite a bit faster than Eclipse
  • Maven integration is good, but I only briefly tried projects with parent-child relationships, which seemed to trip it up a bit
  • Adding getters/setters is a lot less hassle than Eclipse.
  • Using jetty:run right inside Netbeans was pretty easy, though it did require the use of external Maven, I’m not sure why.
  • The web.xml editor is pretty cool
  • It has a built in SOAP client, although it seems pretty clunky.
  • SVN integration seems complete and not very buggy. The interfaces are quick and simple, though I might say it’s a bit oversimplified from the way Eclipse does things.
  • The built in editor support for non-Java files seems less buggy than in Eclipse.

I may give Netbeans another week. It should be pretty obvious to anyone that it takes longer than a week to fully get a feel for such a complicated piece of software.

The modern Java web app

I’m always looking for ways to streamline Java web app development. In case I haven’t spread the word to you, here’s the current set of technologies I’m liking:

  • Tapestry 5 – This is the framework I’m using, but I’m not saying it’s better than anything in particular, but I like some of the stuff they are doing.
  • Hibernate with annotations – I’ve been a big fan of hibernate for a long time, using the annotations seems to simplify the mapping process quite a bit.
  • Spring Hibernate integration – makes transaction/error handling way way easier in Hibernate. I am using HibernateDaoSupport as a base class. Tapestry 5 also has Spring integration, allowing you to inject the DAO’s right into your page classes
  • Maven 2 – So much easier than ant and dependency hell, once you get used to the paradigm shift. It might cause issues with your Eclipse tomcat integration, but I always found that to be flaky anyway. Use Jetty instead.
  • Maven Jetty plugin – Allows you to build/deploy your app with one command (mvn clean jetty:run). Much easier than dealing with some of the flakiness of tomcat deployment, and allows you to just run one of your apps at a time.

I am also looking into Spring Security, and hoping it will simplify authorization, and I’m doing some IDE evaluations to see if it’s time to ditch Eclipse.

IDE of the week: Netbeans 6.1

Happy Monday morning. After battling with Maven and the Tomcat plugin in Eclipse a bit last week, I have decided to try some alternative IDEs. I’ve heard good things about IntelliJ, and I think the Maven integration in Netbeans is supposed to be good. So, I’ve decided to give each of them a week of use and see if they can beat out Eclipse. This week is Netbeans 6.1. So far, I’ve spent a few minutes installing updates and reading up on the Maven support (you have to install a plugin through the plugin manager). Now that the plugin is installed, it automatically recognizes Maven projects when I check them out, so that is cool. Hopefully I can make it through the week without reverting to Eclipse.

Subversion

Today I came across two interesting things about Subversion. First of all, I think I installed the binary version from the Subversion web site on my Mac, since it is in /usr/local/bin, so I dunno if these are true about the one that comes with Leopard. Anyway, I just typed “svn commit” and emacs opened up. I didn’t even know I had emacs. So, apparently emacs is the default editor on this build of svn. Once I stumbled my way through the editing of the commit message, and remembered the key sequence to quit (CTRL-X, CTRL-C), a dialog popped up on my mac prompting me to allow the command line version of svn access to my keychain. I accepted and didn’t have to type in my password. Pretty fancy.

Tapestry 5: First Impressions

My new group at ASU is loosely standardizing on Tapestry, so I’ve been spending some time learning it. We are officially using 4.1, I think, since it’s the latest official release, but I decided to go all rogue style and switch to 5.0 after spending some time on 4.1. From the noise on the web, it sounds like Tapestry gets completely rewritten for every new version, so 5.0 is pretty different from 4.1. Anyway, it seems much better, but there are still some weird problems. Here are some of the interesting things I’ve run into:

  • If you need to call some Tapestry page from an outside app with data, you can simulate Tapestry’s “activation/passivation” by building URL strings which will feed your onActivate method. They look like this: http://localhost:8080/app/page/param1/param2, where param1 and param2 will be passed in a List (not an array, as I found out the hard way) to onActivate. I’m not sure if this is the best way to do it.
  • If you loop through a list of items and want to be able to edit those items (like by having a checkbox), you pretty much need to set up a PrimaryKeyEncoder, otherwise it will serialize/deserialize stuff and all your changes will be lost. This was very hard to figure out.

So far I’m only moderately impressed. There seem to be too many assumptions that don’t fit the model of my first app very well, but we’ll see how it goes once I get better at it.

MathML vs LaTeX


So I was trying out some stuff in Sakai, pretending to write an assessment (quiz, to the uninitiated) with complicated equations. I ran into a few problems like you can’t do fractions, there is no square root symbol, no multiplication symbol, etc. Perhaps I will write a full review once I figure out if these limitations are real, or if I’m just missing something.

Anyway, I remember seeing complicated equations on Wikipedia, so I decided to investigate how they do it. It looks like the equations on there were generated by LaTeX, but they are just ugly png files with a lot of jaggies. I was thinking it would have been cool if they had used SVG. But, they had some info on possible future direction, which led me to the MathML entry, which is supposedly going to be implemented in next gen web browsers.

One of the more annoying aspects of what you can do with Sakai (and Microsoft’s Equation Editor I might add) is that you have to do tons of highlighting and clicking to format things. Like, if I want a chemical equation like HC2H3O2, it requires highlighting 3 things and clicking 3 buttons. That back and forth of the mouse gets quite frustrating. In LaTeX, things are much simpler. You would just write:

HC_2H_3O_2

and you would be done. MathML, on the other hand, is a terribly verbose XML format (check out the quadratic equation example in the Wikipedia entry), so it’s not much better than the point and click method. I realize that LaTeX is a little intimidating to write by hand, and a program could easily generate either LaTeX or MathML, but I have yet to see a program that is as efficient at building equations as hand-coding LaTeX markup is. With that in mind, I think allowing a subset of LaTeX is probably the best way to allow people to enter equations. There is some interesting work out there, like this JavaScript library. It would be cool to see something like that integrated into one of Sakai’s rich text editors.

Getting tomcat to stop deleting your context xml files

I’ve been plagued with this annoying problem in tomcat for about a year, and it has been driving me crazy. I’ve looked through the code, joined the mailing list, etc. etc. The problem is when you have context xml files, such as /usr/local/tomcat/conf/Catalina/localhost/app.xml. Generally, you would use such files to set up JNDI data sources for database connections, but I also use them to set up configuration beans in order to keep environment-specific data outside of your application code (which I consider the whole war file to be). Anyway, tomcat seems to randomly delete those files when I change anything in the application files. I tried various configurations using war files, no war files, and war files in funny locations, and it happened to all of them.

I never did find out why it deletes the files, but I did find a solution, thanks to a user on the tomcat mailing list. All you have to do is turn off “autoDeploy” on the “Host”. Basically, you need to edit /usr/local/tomcat/conf/server.xml and change the xml element, so that it says autoDeploy=”false” instead of autoDeploy=”true”. And with that, all my problems are over.

Dropping tables in MySQL

Say you have access to a MySQL database, and you need to restore it from a previous copy. You could just run the SQL file from the dump, but that is slightly unclean, since it won’t delete tables that it doesn’t know about. Now, let’s assume you don’t have full admin access to the server, so you can’t drop and recreate the db. You have to drop all the tables. Here’s what happens:

mysql> show tables;
+——————————+
| Tables_in_cem |
+——————————+
| academic_periods |
| admin_leases |
| admin_services |
| admins |
| course_adds_kills |
| course_gateways |
| course_organizations |
| course_services |
| course_types |
| courses |
| instructors |
| organization_course_gateways |
| organization_gateways |
| organizations |
| roles |
| section_params |
| sections |
| services |
| transactions |
+——————————+
19 rows in set (0.00 sec)

mysql> drop table academic_periods, admin_leases, admin_services, admins, course_adds_kills, course_gateways, course_organizations, course_services, course_types, courses, instructors, organization_course_gateways, organization_gateways, organizations, roles, section_params, sections, services, transactions;
ERROR 1217 (23000): Cannot delete or update a parent row: a foreign key constraint fails

mysql> show tables;
+——————+
| Tables_in_cem |
+——————+
| academic_periods |
| course_gateways |
| course_types |
| courses |
| organizations |
| services |
+——————+
6 rows in set (0.00 sec)

mysql> drop table academic_periods, admin_leases, admin_services, admins, course_adds_kills, course_gateways, course_organizations, course_services, course_types, courses, instructors, organization_course_gateways, organization_gateways, organizations, roles, section_params, sections, services, transactions;
ERROR 1217 (23000): Cannot delete or update a parent row: a foreign key constraint fails

mysql> show tables;
+——————+
| Tables_in_cem |
+——————+
| academic_periods |
| course_types |
+——————+
2 rows in set (0.00 sec)

mysql> drop table academic_periods, admin_leases, admin_services, admins, course_adds_kills, course_gateways, course_organizations, course_services, course_types, courses, instructors, organization_course_gateways, organization_gateways, organizations, roles, section_params, sections, services, transactions;
ERROR 1051 (42S02): Unknown table ‘admin_leases,admin_services,admins,course_adds_kills,course_gateways,course_organizations,course_ser’

mysql> show tables;
Empty set (0.00 sec)

Mission accomplished. You have to run the command n number of times, where n is the depth of the foreign key constraints, but keep feeding mysql the command and the tables will eventually be gone. I would contend that a “real” database would work out the dependencies and drop them all in one feel swoop.

Hibernate error reporting

Sometimes, Hibernate is really bad at error reporting. Today I added a NOT NULL constraint to a column in my database and ran my unit tests. Several failed with errors like this:

testAddInstructor(edu.asu.cem.test.dao.CourseDAOTest) Time elapsed: 0.009 sec <<
org.hibernate.exception.GenericJDBCException: Could not execute JDBC batch update
at org.hibernate.exception.SQLStateConverter.handledNonSpecificException(SQLStateConverter.java:103)
at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:91)
at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:43)
at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:253)
at org.hibernate.jdbc.AbstractBatcher.prepareStatement(AbstractBatcher.java:92)
at org.hibernate.jdbc.AbstractBatcher.prepareStatement(AbstractBatcher.java:87)
at org.hibernate.jdbc.AbstractBatcher.prepareBatchStatement(AbstractBatcher.java:222)
at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:2229)
at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:2665)
at org.hibernate.action.EntityInsertAction.execute(EntityInsertAction.java:60)
at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:279)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:263)
at org.hibernate.engine.ActionQueue.executeInserts(ActionQueue.java:158)
at org.hibernate.event.def.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:245)
at org.hibernate.event.def.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:181)
at org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:107)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.saveWithGeneratedOrRequestedId(DefaultSaveOrUpdateEventListener.java:187)
at org.hibernate.event.def.DefaultSaveEventListener.saveWithGeneratedOrRequestedId(DefaultSaveEventListener.java:33)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.entityIsTransient(DefaultSaveOrUpdateEventListener.java:172)
at org.hibernate.event.def.DefaultSaveEventListener.performSaveOrUpdate(DefaultSaveEventListener.java:27)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:70)
at org.hibernate.impl.SessionImpl.fireSave(SessionImpl.java:535)
at org.hibernate.impl.SessionImpl.save(SessionImpl.java:523)
at org.hibernate.impl.SessionImpl.save(SessionImpl.java:519)
at edu.asu.cem.test.dao.CourseDAOTest.setUp(CourseDAOTest.java:103)
at sun.reflect.GeneratedMethodAccessor6.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:585)
at org.junit.internal.runners.MethodRoadie.runBefores(MethodRoadie.java:122)
at org.junit.internal.runners.MethodRoadie.runBeforesThenTestThenAfters(MethodRoadie.java:86)
at org.junit.internal.runners.MethodRoadie.runTest(MethodRoadie.java:77)
at org.junit.internal.runners.MethodRoadie.run(MethodRoadie.java:42)
at org.junit.internal.runners.JUnit4ClassRunner.invokeTestMethod(JUnit4ClassRunner.java:88)
at org.junit.internal.runners.JUnit4ClassRunner.runMethods(JUnit4ClassRunner.java:51)
at org.junit.internal.runners.JUnit4ClassRunner$1.run(JUnit4ClassRunner.java:44)
at org.junit.internal.runners.ClassRoadie.runUnprotected(ClassRoadie.java:27)
at org.junit.internal.runners.ClassRoadie.runProtected(ClassRoadie.java:37)
at org.junit.internal.runners.JUnit4ClassRunner.run(JUnit4ClassRunner.java:42)
at org.apache.maven.surefire.junit4.JUnit4TestSet.execute(JUnit4TestSet.java:62)
at org.apache.maven.surefire.suite.AbstractDirectoryTestSuite.executeTestSet(AbstractDirectoryTestSuite.java:138)
at org.apache.maven.surefire.suite.AbstractDirectoryTestSuite.execute(AbstractDirectoryTestSuite.java:125)
at org.apache.maven.surefire.Surefire.run(Surefire.java:132)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:585)
at org.apache.maven.surefire.booter.SurefireBooter.runSuitesInProcess(SurefireBooter.java:308)
at org.apache.maven.surefire.booter.SurefireBooter.main(SurefireBooter.java:879)
Caused by: java.sql.BatchUpdateException: failed batch
at org.hsqldb.jdbc.jdbcStatement.executeBatch(Unknown Source)
at org.hsqldb.jdbc.jdbcPreparedStatement.executeBatch(Unknown Source)
at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:48)
at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:246)
… 44 more

I had made several changes since running the tests, so I had to think about what might be making a database call crash. Luckily, I was able to remember, but I have spent hours debugging these before. I wish the JDBC exception was floated to a higher point in the exception chain so it would be displayed. In this case, all I had to do was set a value for the fields that were no longer nullable.