ant vs java – using ** to match directories

Suppose I have the following files:
/MyFiles/dir/a.txt
/MyFiles/dir/child/b.txt

In Ant, this is how you write code to output all text files in any subdirectory of a “dir” directory.

<fileset id="jb" dir="/MyFiles">
  <include name="**/dir/**/*.txt" />
</fileset>

<pathconvert pathsep="${line.separator}" property="out" refid="jb"/>
<echo>${out}</echo>

In Java, the equivalent is

public class MyPathMatcher extends SimpleFileVisitor<Path> {
  private PathMatcher matcher =
     FileSystems.getDefault().getPathMatcher( "glob:**/dir/**.txt");

 @Override
 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
   if (matcher.matches(file)) {
     System.out.println("File " + file);
   }
   return FileVisitResult.CONTINUE;
 }

  public static void main(String[] args) throws Exception {
    MyPathMatcher dirs = new MyPathMatcher();
    Files.walkFileTree(Paths.get("/MyFiles"), dirs);
   }
}

The Java documentation says “Two asterisks, **, works like * but crosses directory boundaries. This syntax is generally used for matching complete paths”. Whereas in the Ant documentation, “When ** is used as the name of a directory in the pattern, it matches zero or more directories.”

The Ant approach probably feels more natural to me because I’ve been using it longer. But the Java approach seems more logical because it doesn’t have the extra slash that doesn’t actually get matched.

upgrading to mavericks – problems including broke eclipse/ant java for jmockit – and how to fix

I didn’t upgrade to Mavericks right away because I was getting FIOS.  If there were internet problems, I wanted to know they were Verizon’s fault and not be in a position where they could claim it was my computer’s problem.  I’ve had Mavericks long enough that I can write about the experience.

The problems describe here:

  • git
  • Eclipse/Java/Ant/JMockIt
  • OpenOffice crashing

How long it took

Not counting the download, the install took 2 hours.  Much of which was Apple saying there were “17 minutes” left.  I left it alone and it didn’t finish though.  I’ve seen reports of significantly faster installs though.

Problem #1 – git

The first problem I ran into was the error message The “git” command requires the command line developer tools.  Not a big deal.  It was a two minute install.  I was just surprised as I was expecting what was installed before the upgrade to  be installed after.

Problem #2 – Java and Eclipse

And now for the meat of this.  Being able to develop again in Eclipse was by no means seemless.

Error: Ant can’t find Java

Exact message: Specified VM install not found: type MacOS X VM, name Java SE 6 (MacOS X Default)

Solution – for each Ant build:

  • Run > External Tools Configuration
  • Run in same JRE as the workspace

Error: JUnit tests don’t run in Eclipse view

When running from Eclipse, I got the stack trace:


java.lang.IllegalStateException: Unable to load Java agent; please add lib/tools.jar from your JDK to the classpath

at mockit.internal.startup.JDK6AgentLoader.getVirtualMachineImplementationFromEmbeddedOnes(JDK6AgentLoader.java:102)

at mockit.internal.startup.JDK6AgentLoader.loadAgent(JDK6AgentLoader.java:74)

at mockit.internal.startup.AgentInitialization.initializeAccordingToJDKVersion(AgentInitialization.java:41)

at mockit.internal.startup.Startup.initializeIfNeeded(Startup.java:271)

at org.junit.runner.Runner.<clinit>(Runner.java:25)

at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)

at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)

at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)

at java.lang.reflect.Constructor.newInstance(Constructor.java:525)

at org.junit.internal.builders.AnnotatedBuilder.buildRunner(AnnotatedBuilder.java:35)

at org.junit.internal.builders.AnnotatedBuilder.runnerForClass(AnnotatedBuilder.java:24)

at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:57)

at org.junit.internal.builders.AllDefaultPossibilitiesBuilder.runnerForClass(AllDefaultPossibilitiesBuilder.java:29)

at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:57)

at org.junit.internal.requests.ClassRequest.getRunner(ClassRequest.java:24)

at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.<init>(JUnit4TestReference.java:33)

at org.eclipse.jdt.internal.junit4.runner.JUnit4TestClassReference.<init>(JUnit4TestClassReference.java:25)

at org.eclipse.jdt.internal.junit4.runner.JUnit4TestLoader.createTest(JUnit4TestLoader.java:48)

at org.eclipse.jdt.internal.junit4.runner.JUnit4TestLoader.loadTests(JUnit4TestLoader.java:38)

at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:452)

at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)

at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)

at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)

java.lang.NoClassDefFoundError: org.junit.runner.Runner

at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:59)

at org.junit.internal.requests.ClassRequest.getRunner(ClassRequest.java:24)

at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.<init>(JUnit4TestReference.java:33)

at org.eclipse.jdt.internal.junit4.runner.JUnit4TestClassReference.<init>(JUnit4TestClassReference.java:25)

at org.eclipse.jdt.internal.junit4.runner.JUnit4TestLoader.createTest(JUnit4TestLoader.java:48)

at org.eclipse.jdt.internal.junit4.runner.JUnit4TestLoader.loadTests(JUnit4TestLoader.java:38)

at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:452)

at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)

at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)

at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)

Solution:

First I tried switching creating a new installed JVM of type MacOS X JVM.  That didn’t help directly.  I left it like that since I didn’t want to muck around with my original configuration.  Then I added tools.jar to the installed JRE.

  1. Eclipse > Preferences
  2. Java > Installed JREs
  3. Click target JRE
  4. Edit
  5. Add external jars
  6. Add  /Library/Java/JavaVirtualMachines/1.7.0.jdk/Contents/Home/lib/tools.jar

Warning: The first time I tried this procedure, Eclipse hung.  I had it to kill it.  When I opened my workspace, I saw “unable to load plugin x” in every single view.  I used Time Machine to restore to before I tried changing the JREs and then re-did that change.  The second time it was successful.

I could have added tools.jar to my classpath for the project instead.  I choose not to because I was hoping updating it in the JVM install would solve the Ant problem (described below) as well.  It didn’t.  I also didn’t want to change the project .classpath as that is shared in Subversion and I didn’t want to make a change that affects others.

This whole exercise felt like a hack until I read the JMockIt instructions.  This leads me to believe I was running with a different JDK before the Mavericks upgrade.

If you are developing on a JDK of version 1.6 or newer on Mac OS X, add <jdkDir>/lib/tools.jar to the classpath, where <jdkDir> is the home directory for your local JDK 1.6 installation.

Error: JUnit tests don’t run from Ant

When running from Ant, every test failed with:

Caused an ERROR</span>
<pre>
null

java.lang.reflect.InvocationTargetException

at java.lang.reflect.Constructor.newInstance(Constructor.java:525)

What I learned:

  • I tried setting JAVA_HOME at the command line to no avail.  I confirmed javac is on the path. (/usr/bin/javac); it was.
  • I tried adding tools.jar to the classpath in launch configuration in Eclipse for running Ant.
  • If I change to fork=”no” in the <junit> task in the Ant script, most of the tests pass.  (Some of them require the JVM be forked which is why it was in the first place.) Showing the problem is when a new JVM is created, it doesn’t inherit the tools.jar from the previous step.
  • If I hard code the classpath entry for tools.jar in the Ant build file, all of the tests pass.

Solution:

If you need to fork the JVM, add the following insider you <junit> task.

<classpath location="/Library/Java/JavaVirtualMachines/1.7.0.jdk/Contents/Home/lib/tools222.jar" />

I don’t like this solution.  I also didn’t like the hack for Eclipse so my unease is pretty uniform here.  Luckily Ant doesn’t choke if you include a file (or disk or filesystem) in the classpath that doesn’t exist.  Which means this won’t hurt others working on the same project.

Problem #3 – Open Office crashes when opening a document

The solution was to upgrade from Open Office 3.3.0 to the latest (4)

My favorite new feature

It’s a minor one, but I like the feature to synchronization of “Read Later” between my Mac and iPad.  (I haven’t tried it yet so it may not be that nice in practice.)  Most of the new features are things I don’t need.

getting computer vision to work on a raspberry pi

This year, I helped a local high school FIRST robotics team get computer vision working on a Raspberry Pi.   Two students worked on the problem.  At the beginning, they both worked on computer vision.  Later, one specialized on the Pi and the other on CV.  I learned a lot from a book and from playing with it.  We encountered some interesting and memorable problems along the way.

Recognizing a target

We started with sample code from another team from last year.  This helped learn how to write the code and understand the fundamentals.  It also helped with the “plumbing” code.  As hard as it was to recognize a target, this didn’t prove to be the most frustrating part.  The Pi itself mounted a number of challenges.

Parts for the Pi

We bought/scavenged parts for the Pi.  A USB keyboard, USB mouse and cell phone micro charger where donated.  A HDMI/DVI cable we needed to buy.  We borrowed a computer monitor and ethernet cable.

Finding the right jars

The Pi is built on ARM.  We needed javacv-0.2-linux-arm.jar.  It turned out there is no linux arm version in the latest javacv (0.3).  There is one in 0.2 which we used.  Which was incompatible with the versions of other tools.  (see next problem.)

Setting up the Pi

Compiling opencv on the pi takes 4 hours.  Since that’s how long a meeting is, this meant running the compile overnight.  Having to wait overnight to find out if something worked was like a taste of what punchcard programmers had to go through!

Then it turned out we couldn’t even use our compile.  We were missing the libjniopencv_core.so file. We spent a few days trying to solve this.  We wound up  using a pre-compiled for Pi version.  This is how we got version compatibility.

Updating a NetBeans Ant script

Since speed matters in a competition,we wanted to change the build run target to not compile first.  Netbeans comes with an empty looking build.xml and a useful build impl xml file.  (This is actually my favorite feature of NetBeans – that the build can easily be run outside of NetBeans.)  We easily found the run target in the build impl file.  We copied it to build.xml, renamed it and removed the compile dependency.  This wasn’t actually a problem, but it was interesting how NetBeans sets up the build file.

Starting a script on startup

We wanted the Pi to start the computer vision script automatically on boot up.  We created a file in /etc/init.d since this is a Linux (Debian) install.  Then we made a fatal error.  We forgot to add the & to run the script in the background.  So when we tested rebooting, the Pi hung.  And we couldn’t SSH to it because it hadn’t booted.  The solution was to take the pi’s sd card to another computer and edit the bootup script to use single user mode.  We could then login and edit the startup script to add the missing &.

Networking

We used Java sockets to transfer the “answer” from the Pi to the robot.  The answer being a single number representing the number of degrees off from the center of the target.  We made the mistake of testing this with both ends on a regular computer.  When moving to the robot it didn’t compile because the robot uses J2ME.  We then refactored to use the mobile version (code here).

Performance – CPU

Success.  Computer vision works.  The problem is it took 3 seconds per image.  We reduced it to 1.3 seconds per image by reducing the resolution to the smallest one the camera supports.  We shaved off another 0.1-0.2 seconds by turning off file caching in ImageIO.  We learned the problem was full CPU usage when calling ImageIO.read.

I found an interesting thread showing the “old way” of creating a JPEG using ImageIcon was much faster.  We tried it and the thread was right.  It even created an image we could open in a photo editor.  The problem is that it didn’t work with our code/libraries for image processing.  We don’t know why.  Obviously ImageIO has some default we are unaware of.  A 1 second time is acceptable so this isn’t  a problem.  But it’s an interesting conundrum.

Another interesting CPU note.  We tried compiling image magik.  It took over 3 hours on the Pi.  By contrast, it took 2.5 minutes on a Mac.