Monday, July 23, 2018

GeoTools Hello World on the Mac

Last time I did a hello world on Linux (Debian Testing).  This time, I'm going to go through trying it on the Mac.  If you use home brew, mac ports, or something else, you may see something different.  Also, my install of Java 9 on my mac isn't likely 100% normal.  I'm too new to know the differences.

The resulting pom.xml and App.java are exactly the same as with my prior post, which is the way it is supposed to be!

https://gist.github.com/schwehr/b433076cab0cdb99c61163c6fb56bf7f

Getting setup with JAVA_HOME is where I hit some speed bumps.  But they turned out not to be too bad.  So I know this is where I want to end up pointing my JAVA_HOME:

/Library/Java/JavaVirtualMachines/jdk-9.jdk/Contents/Home

But first, I wanted to see what else is around.  Here is my default path:

type javac
javac is /usr/bin/javac

javac --version
javac 9

java --version
java 9
Java(TM) SE Runtime Environment (build 9+181)
Java HotSpot(TM) 64-Bit Server VM (build 9+181, mixed mode)

I wanted to see if I could use the default /usr/bin stuff.  Before I try that, I need to install maven.  I use fink, but I bet brew and macports have it too.

fink install maven

mvn --version
Error: JAVA_HOME is not defined correctly.
  We cannot execute /usr/libexec/java_home/bin/java

Uh oh!  Time to see what /usr has.

ls -ld /usr/libexec/java_home 
lrwxr-xr-x  1 root  wheel  79 Jan 31 14:36 /usr/libexec/java_home -> /System/Library/Frameworks/JavaVM.framework/Versions/Current/Commands/java_home

find /System/Library/Frameworks/JavaVM.framework/Versions -name javac
/System/Library/Frameworks/JavaVM.framework/Versions/A/Commands/javac

That doesn't look like the correct structure, so I'm going to avoid that and go with what I originally thought would work:

export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk-9.jdk/Contents/Home

mvn --version
Apache Maven 3.2.3 (33f8c3e1027c3ddde99d3cdebad2656a31e8fdf4; 2014-08-11T13:58:10-07:00)
Maven home: /sw/share/maven
Java version: 9, vendor: Oracle Corporation
Java home: /Library/Java/JavaVirtualMachines/jdk-9.jdk/Contents/Home
Default locale: en_US, platform encoding: UTF-8
OS name: "mac os x", version: "10.13.6", arch: "x86_64", family: "mac"

So now it looks like I've got maven and a probably working Java env.  Time to start building the app.  Start by creating a space to work on it and create the initial project:

cd && mkdir maven && cd maven

mvn --batch-mode archetype:generate -DgroupId=com.example -DartifactId=geotools-hello -DarchetypeGroupId=org.apache.maven.archetypes

Now checkout what's in the default setup:

cd geotools-hello/

tree -d

find . -type f
./pom.xml
./src/test/java/com/example/AppTest.java
./src/main/java/com/example/App.java

Can I build anything with it?

mvn compile

[ERROR] COMPILATION ERROR : 
[INFO] -------------------------------------------------------------
[ERROR] Source option 1.5 is no longer supported. Use 1.6 or later.
[ERROR] Target option 1.5 is no longer supported. Use 1.6 or later.
[INFO] 2 errors 
[INFO] -------------------------------------------------------------
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE

Nope!  This is the same issue I had on Linux.  Time to start editing the pom.xml file.  Add this:

  <properties>
    <maven.compiler.source>1.9</maven.compiler.source>
    <maven.compiler.target>1.9</maven.compiler.target>
  </properties>

Now is is actually able to build and run the application!  I added a line to print out the Java version.  On the Mac, I'm seeing "9", while on Linux I was seeing 9.0.4.

mvn compile exec:java -Dexec.mainClass="com.example.App"
[INFO] Scanning for projects...
Downloading: https://repo.maven.apache.org/maven2/org/codehaus/mojo/exec-maven-plugin/maven-metadata.xml
[SNIP]
Downloaded: https://repo.maven.apache.org/maven2/org/codehaus/mojo/exec-maven-plugin/1.6.0/exec-maven-plugin-1.6.0.jar (57 KB at 434.0 KB/sec)
[INFO]                                                                         
[INFO] ------------------------------------------------------------------------
[INFO] Building geotools-hello 1.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ geotools-hello ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory /Users/schwehr/maven/geotools-hello/src/main/resources
[INFO] 
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ geotools-hello ---
[INFO] Changes detected - recompiling the module!
[WARNING] File encoding has not been set, using platform encoding UTF-8, i.e. build is platform dependent!
[INFO] Compiling 1 source file to /Users/schwehr/maven/geotools-hello/target/classes
[INFO] 
[INFO] --- exec-maven-plugin:1.6.0:java (default-cli) @ geotools-hello ---
Downloading: https://repo.maven.apache.org/maven2/org/apache/maven/reporting/maven-reporting-api/2.2.1/maven-reporting-api-2.2.1.pom
Downloaded: https://repo.maven.apache.org/maven2/org/apache/maven/reporting/maven-reporting-api/2.2.1/maven-reporting-api-2.2.1.pom (2 KB at 15.1 KB/sec)
Downloading: https://repo.maven.apache.org/maven2/org/apache/maven/reporting/maven-reporting/2.2.1/maven-reporting-2.2.1.pom

[SNIP]

Downloaded: https://repo.maven.apache.org/maven2/commons-cli/commons-cli/1.2/commons-cli-1.2.jar (41 KB at 161.3 KB/sec)
Downloaded: https://repo.maven.apache.org/maven2/org/codehaus/plexus/plexus-utils/3.0.20/plexus-utils-3.0.20.jar (238 KB at 730.6 KB/sec)
Hello World!
9

Now I change App.java to include.  It works!

mvn compile exec:java -Dexec.mainClass="com.example.App"
[INFO] Scanning for projects...
[INFO]                                                                         
[INFO] ------------------------------------------------------------------------
[INFO] Building geotools-hello 1.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ geotools-hello ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory /Users/schwehr/maven/geotools-hello/src/main/resources
[INFO] 
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ geotools-hello ---
[INFO] Changes detected - recompiling the module!
[WARNING] File encoding has not been set, using platform encoding UTF-8, i.e. build is platform dependent!
[INFO] Compiling 1 source file to /Users/schwehr/maven/geotools-hello/target/classes
[INFO] 
[INFO] --- exec-maven-plugin:1.6.0:java (default-cli) @ geotools-hello ---
Hello World!
9
GeoTools version 20-SNAPSHOT (built from rc52833cadf758699a1969d258ab6172b305f9416)
Java version: 9
Operating system: Mac OS X 10.13.6
GeoTools jars on classpath:

Tuesday, July 3, 2018

GeoTools hello world

I just wanted to get started playing with GeoTools.  When you are a beginner to Java, Maven, and GeoTools, it's impressive how many pitfalls there are.  I did just about everything wrong and found all sorts of dark alleys that involved my machine downloading junk for a hour and then declaring failure.  I can't believe how much time it took me to figure out how to get this to work on my Linux workstation.

Now that I have it working, it's time to document what I did.  My environment is pretty close to Debian Testing.  You will probably find things in this description that don't work for you.  I've redacted a few little things here and there, so expect the output you see to be much more verbose.

App.java and pom.xml as I have them at the end of this are here:

https://gist.github.com/schwehr/b433076cab0cdb99c61163c6fb56bf7f

The first hurdle is installing Java and the JDK.  I've got Java 9 preinstalled on my workstation in a funky location, so pardon that confusion.  Java lives here for me:

ls /usr/local/buildtools/java

The first thing I need to do is be explicit about which version of Java to use.  My system is setup to default to Java 8, and why not live from head?

export JAVA_HOME=/usr/local/buildtools/java/jdk9

$JAVA_HOME/bin/java -version
openjdk version "9.0.4"
OpenJDK Runtime Environment (build 9.0.4+11)
OpenJDK 64-Bit Server VM (build 9.0.4+11, mixed mode)

Now I need Apache Maven to control the build and run process.

apt-cache show maven | grep Ver
Version: 3.5.0-5

sudo apt-get install maven

mvn -version
Apache Maven 3.5.0
Maven home: /usr/share/maven
Java version: 9.0.4
Java home: /usr/local/buildtools/java/jdk9
Default locale: en_US, platform encoding: UTF-8
OS name: "linux", version: "4.9.0-6-amd64", arch: "amd64", family: "unix"

Where is maven?

ls -l /usr/bin/mvn
lrwxrwxrwx 1 root root 21 May 24 21:56 /usr/bin/mvn -> /etc/alternatives/mvn

readlink -f /usr/bin/mvn
/usr/share/maven/bin/mvn

dpkg -S `readlink -f /usr/bin/mvn`
maven: /usr/share/maven/bin/mvn

Let's start a project that will be our starter.

mkdir maven && cd maven

mvn --batch-mode archetype:generate -DgroupId=com.example -DartifactId=geotools-hello -DarchetypeGroupId=org.apache.maven.archetypes

tree -d

cd geotools-hello

find .
.
./pom.xml
./src
./src/main
./src/main/java
./src/main/java/com
./src/main/java/com/example
./src/main/java/com/example/App.java
./src/test
./src/test/java
./src/test/java/com
./src/test/java/com/example
./src/test/java/com/example/AppTest.java

mvn compile

[ERROR] COMPILATION ERROR : 
[INFO] -------------------------------------------------------------
[ERROR] Source option 1.5 is no longer supported. Use 1.6 or later.
[ERROR] Target option 1.5 is no longer supported. Use 1.6 or later.

Well, that sucks!  I've got Java 9 and this thing is trying to use 1.5?  According to Java version history, 2004 is calling and they want their old Java back.  Edit the pom.xml and tell it to get to at least the year 2017.

  <properties>
    <maven.compiler.source>1.9</maven.compiler.source>
    <maven.compiler.target>1.9</maven.compiler.target>
  </properties>

Try it again and it should work.

mvn compile

mvn exec:java -Dexec.mainClass="com.example.App"
[INFO] Scanning for projects...
[INFO] 
[INFO] ------------------------------------------------------------------------
[INFO] Building geotools-hello 1.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] --- exec-maven-plugin:1.6.0:java (default-cli) @ geotools-hello ---
Hello World!

Now we want to make sure the java version is what we expect.  Edit src/main/java/com/example/App.java to look like this:

package com.example;

import java.lang.System;

public class App
{
    public static void main( String[] args )
    {
        System.out.println( "Hello World!" );
        System.out.println(System.getProperty("java.version"));
    }
}

Running it should now give a little bit more info.  We can compile and run in one maven command.  The commands added will be run in the order given on the command line.

mvn compile exec:java -Dexec.mainClass="com.example.App"
[INFO] Scanning for projects...
[INFO] 
[INFO] ------------------------------------------------------------------------
[INFO] Building geotools-hello 1.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ geotools-hello ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory /usr/local/google/home/schwehr/maven/geotools-hello/src/main/resources
[INFO] 
[INFO] --- maven-compiler-plugin:3.6.1:compile (default-compile) @ geotools-hello ---
[INFO] Changes detected - recompiling the module!
[WARNING] File encoding has not been set, using platform encoding UTF-8, i.e. build is platform dependent!
[INFO] Compiling 1 source file to /usr/local/google/home/schwehr/maven/geotools-hello/target/classes
[INFO] 
[INFO] --- exec-maven-plugin:1.6.0:java (default-cli) @ geotools-hello ---
Hello World!
9.0.4

So we indeed have java 9.  Woot!

Now on to trying to do the simplest possible thing I can think of with GeoTools.  Can we print the version information?  Finding the GeoTools.getAboutInfo() method took me way to long.  Sigh.

Edit the App.java file to import org.geotools.factory.GeoTools and call the static method:

package com.example;

import java.lang.System;
import org.geotools.factory.GeoTools;

public class App
{
    public static void main( String[] args )
    {
        System.out.println( "Hello World!" );
        System.out.println(System.getProperty("java.version"));
        System.out.println(GeoTools.getAboutInfo());
    }
}

Next you need to make your pom.xml match mine.  You need to add the geotools version to the properties.  Then you need to add the gt-metadata library to the dependencies.    And then add the repositories where the dependencies can be found.

  <properties>
    <maven.compiler.source>1.9</maven.compiler.source>
    <maven.compiler.target>1.9</maven.compiler.target>

    <geotools.version>20-SNAPSHOT</geotools.version>
  </properties>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.1</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.geotools</groupId>
      <artifactId>gt-metadata</artifactId>
      <version>${geotools.version}</version>
    </dependency>
  </dependencies>

  <repositories>
    <repository>
      <id>maven2-repository.dev.java.net</id>
      <name>Java.net repository</name>
      <url>http://download.java.net/maven/2</url>
    </repository>
    <repository>
      <id>osgeo</id>
      <name>Open Source Geospatial Foundation Repository</name>
      <url>http://download.osgeo.org/webdav/geotools/</url>
    </repository>
    <repository>
      <snapshots>
        <enabled>true</enabled>
      </snapshots>
      <id>boundless</id>
      <name>Boundless Maven Repository</name>
      <url>http://repo.boundlessgeo.com/main</url>
    </repository>
  </repositories>

Now try it and hopefully you will succeed with the about showing you a couple of lines about GeoTools.

mvn compile exec:java -Dexec.mainClass="com.example.App"

Hello World!
9.0.4
GeoTools version 20-SNAPSHOT (built from r9accd1de3af6dcbd69ee56eebd4375a5b5ceb19b)
Java version: 9.0.4
Operating system: Linux 4.9.0-6-amd64
GeoTools jars on classpath:

Finding that GeoTools.getAboutInfo() was in gt-metadata was a bit painful.  I wandered through the upstream source's pom.xml files and got very confused.  In the end, I confirmed the location after thrashing about and having maven download a huge amount of stuff to ~/.m2 (maven's cache directory) and inspecting the compiled jars.  Silliness ensued...

cd ~/.m2

find . -name \*.jar | while read f ; do (jar tf $f | awk '{print "'"$f"'" " " $0}' | grep -i "GeoTools") ; done

jar tf ./repository/org/geotools/gt-metadata/20-SNAPSHOT/gt-metadata-20-SNAPSHOT.jar | grep factory/GeoTools.class
org/geotools/factory/GeoTools.class

mkdir foo

cp ./repository/org/geotools/gt-metadata/20-SNAPSHOT/gt-metadata-20-SNAPSHOT.jar ~/foo

cd ~/foo

jar xf gt-metadata-20-SNAPSHOT.jar

find . -name GeoTools.class
./org/geotools/factory/GeoTools.class

strings org/geotools/factory/GeoTools.class | grep About
getAboutInfo

Now to do stuff with GeoTools that is more interesting than just a hello world.