My Notes

Many people ask questions about Java Mapscript on mapserver-users. This is an attempt to record my answers to them, my impressions on mapserver and Java Mapscript. Before you proceed I must warn you that the following are my opinions and they might not be endorsed or approved by the mapserver community at all.

Most of this is LINUX specific, but some is general.

Building Java Mapscript

Guidelines for building a working Java Mapscript.

The Golden Rule: KISS! (Keep it simple, stup^H^H^H^sir!)

Mapserver is really a big piece of software with plenty of functionalities (some say it is the most advanced web mapping software) and chances are you do need all of them. If you are a newbie to mapserver or to Java mapscript try to understand what you need and stick with it, removing the unneeded features.
This approach is generally suggested because:

  • it reduces the number of lines of code involved and thus reduces the number of possible bugs that could bite
  • it makes for a smaller binary which is more efficient
  • in case of a crash the number of suspects is lower and will speed up investigation and in some cases resolution
  • it requires less software to be installed on your computer which makes it easier to mantain in the long term
  • the first three items apply (with recursion) to the previous
  • if you ask for help on the mailing list the first general advice is to remove components you don't use: so do it in advance

See below for a example of a basic (but working) mapserver/mapscript build example.

How do I disable option X?

Chances are that even if you do not need tiff or mysql support it will get built in anyway because the configure script detects their libraries. In all cases you should review the output of the configure script and then use the proper command line argument to disable unwanted features.

When running the ./configure program use the --disable-X option. You can get the list of available options by running:

./configure --help

Know your enemy

Certain libraries/features are known NOT to work with mapscript because they are not thread-safe. Be sure not to enable them at compile time or you will get errors that will crash the JVM. You can review the list of unsafe components here.

Know your enemy (part 2: gdal and tiff)

If you need raster support through GDAL disable tiff (using --without-tiff) because they don't play well with each other.

Know your enemy (part 3: pdf)

PDF is supported as an output format, but it is not worth the price of risking a buggy mapscript. A better solution is to incorporate the image in a PDF document by using a pure Java library like the excellent iText.

By doing so you will be able to customize headers, footers, paper format and transparencies will also work as supposed. iText additionally supports PDF encryption to disallow printing, editing or modification.

Also remember that PDF output in mapserver cannot make use of an embedded scalebar because pixmap fonts are not supported.

Know your enemy (part 4: connection pooling)

Connection pooling is locked hence thread safe, but as of version 4.8.x (that is until Feb 2006) it has known memory leaks. There is an issue on bugzilla that tracks this problem with Oracle.
http://mapserver.gis.umn.edu/bugs/show_bug.cgi?id=1662.

Until that is cleared you can use connection pooling, just make sure that once in a while the application is restarted or you will eventually run out of memory. How often this should happen depends on the number of database connections defined in the map and the number of users. Sites with 1 or two database connections and moderate to low use could reboot the application (that is the tomcat process in case of web apps) once a day.

Note
There is no way to disable connection pooling.

Know your friends

Java Mapscript will NEVER work without the following configure flag: --with-threads. Do not complain about you JVM going belly up on the mailing lists if you do not have enabled this option.

The proper way to compile mapscript is...

To use mapserver and mapscript you will quite likely need to download and install many other libraries and this might not particularly easy as most of them (gdal, for instance) do not have a binary rpm for linux.
You have then to find, download all of them and install one by one, solving dependencies by hand. If this is not what you want and need a more portable ,reliable and easier to deploy environment you should seriously consider fwtools. Fwtools is collection of the most popular libraries used with mapserver already compiled and ready to run on almost any linux distribution.
This section describes how to compile mapserver and Java mapscript against fwtools.

The proper way to compile is to carefully select options as described above, then to run make, cd into mapscript/java and run:

make interface
make
make test

Make interface requires that you have swig installed, at least version 1.3.21.

My configure options to build mapscript with support for raster (through gdal), truetype fonts (through freetype), shape files (native, cannot be disabled), projection (through proj) are:

./configure --with-freetype --without-tiff --with-gdal=/opt/gdal/bin/gdal-config --with-proj --without-pdf --with-threads

These are known to work (for me at least :-)) and are used in production sites. Note that proj, even if it not used, is required for good operation of mapserver.

Compiling against fwtools

Download fwtools and install it. I usually install it in /opt/FWTools-1.0.0b2. Cd into the lib subdirectory and run the following two commands:

ln -s libpng.so.2 libpng.so
ln -s libfreetype.so.6 libfreetype.so

This is required for mapserver configure script to find and use the freetype and png libraries shipping with fwtools.

Download latest mapserver and unpack it, then compile it with the following configuration:

./configure --enable-debug --with-threads --without-pdf --without-tiff \
--with-gdal=/opt/FWTools-1.0.0b2/bin/gdal-config --with-proj=/opt/FWTools-1.0.0b2/ \
--with-gd=/opt/FWTools-1.0.0b2 --with-freetype=/opt/FWTools-1.0.0b2 \
--with-png=/opt/FWTools-1.0.0b2 --with-zlib=/opt/FWTools-1.0.0b2 --with-jpeg=/opt/FWTools-1.0.0b2

Before you run mapserver or mapscript make sure you load the fwtools environment by running /opt/FWTools-1.0.0b2/fwtools_env.sh.

What's the deal with make interface?

When I first used Java mapscript in 2003 running swig was a necessary prerequisite to generate the Java and c wrapper files as the source distribution did not have any. There was a problem though: swig would generate Java methods and c wrappers for all mapserver components (like postgis, wfs or ogr) regardeless of the fact that they were enabled in the configure phase.

Then I submitted bug #876 along with a patch which would let swig know of which components were enabled and consequently generate Java and c wrappers only for them. For the patch to work the user had to run make interface.

The problem then was that a rather new version of swig was required, which was not available on all distros so the developers decided to provide a default version of the Java and c wrappers to help those who did not have a recent swig (at least 1.3.21). That issue should be overcome by now as all linux distros should ship with a swig version that is two years old!
So, if you have a recent version of swig (run swig -version to know) you should run make interface to update your Java and c wrappers, even if the README does not recommend it.

Running Java Mapscript (on Linux)

To correctly run any Java Mapscript application (be it a servlet, jsp page or a standalone graphical client) you need to let the Java Virtual Machine know where to find two pieces of software:

  1. libmapscript.so whose role is to tie together mapserver and Java
  2. mapscript.jar which provides the mapserver Java objects which interface with libmapscript.so

The location of the second must be specified on the classpath by means of the CLASSPATH environment variable or the -cp option. The location of the first can be specified using the LD_LIBRARY_PATH variable or the -Djava.library.path option. For an example see the make test command you ran after compiling mapscript.

Dealing with unresolved dependencies

If your Java application brings up an error like the following:

javac -classpath ./:examples/:./mapscript.jar -d examples/ examples/*.java
java -classpath ./:examples/:./mapscript.jar -Djava.library.path=. DumpShp ../../tests/point.shp
java.lang.UnsatisfiedLinkError: /opt-4.4.2/mapscript/java/libmapscript.so:
libgdal.so.1: cannot open shared object file: No such file or directory
make: *** [test] Error 255

it means that the gdal library was not found by the linker: this is probably due to the fact that gdal was not supplied by your Linux distro and you installed it from source. In this case you must find the directory where libgdal.so.1 is and then add it to the LD_LIBRARY_PATH or the -Djava.library.path.

Specifying the library name

When the JVM attempts to load the shared library it will look by default for a file named libmascript.so in one of the system directories or in those specified by LD_LIBRARY_PATH or -Djava.library.path.
If necessary one can tell the JVM to load a shared library with a different name by passing the following argument to the java interpreter: -Dmapserver.library.name=yourname. For instance to load an hypothetical library named libmapscript-debug.so:

java -Dmapserver.library.name=mapscript-debug MyJavaProgram

Learning Java Mapscript

If you got this far then by now you probably want to know how to do X in Java Mapscript? (replace X with query, zoom, etc).

To date there is not a tutorial, nor code that you can look at (besides the examples, but that's not much frankly), so the best thing you can do is take the php mapscript example form the mapserver web site and translate it to Java. This is how I did the first time and somehow it worked.

Additional documentation is in the mapserver sources in mapscript/doc.

QueryByPoint example

The following is an example of a working code snippets that opens a layer and queries its features by shape. It has been used succesfully against shape and postgis sources.

Note the usage of the commons-logging api for debuggins the code.

protected List queryLayerByPoint(int i, pointObj queryPoint) {
List results = new ArrayList();

layerObj layer = map.getLayer(i);
if (layer != null && isVisible(layer)) {
if (log.isDebugEnabled()) {
log.debug("Querying layer: " + layer.getName());
}
if (layer.queryByPoint(map, queryPoint,mapscript.MS_MULTIPLE, -1) == mapscript.MS_SUCCESS) {
// OLD pre 4.2 code, see http://mapserver.gis.umn.edu/docs/howto/mapscript_querying
//resultCacheObj resultCache = layer.ggetResultcache();
if (layer.open() == mapscript.MS_SUCCESS) {
// OLD pre 4.4 code
//for (int j=0;j < resultCache.getNumresults();j++) {
for (int j = 0; j < layer.getNumResults(); j++) {
if (log.isDebugEnabled())
log.debug("Layer " + layer.getName() + ", result number:" + j);
resultCacheMemberObj resultMember = layer.getResult(j);
shapeObj shape = new shapeObj(layer.getType());
layer.getShape(shape,resultMember.getTileindex(), resultMember.getShapeindex());
if (shape != null) {
Map aResult = getResult(shape, layer);

results.add(aResult);
} else {
log.error("Shape " + j + " is null!");
}
}
layer.close();
} else {
log.error("Cannot open layer: " + layer.getName());
}
} else {
log.info("Query on layer " + layer.getName() + " failed.");
}
}
return results;
}

What to do if mapscript crashes

Make sure that you have done all I have suggested here, in particular review the list of unsafe components. After that try to isolate the piece of code that is failing: if you cannot do that because errors are random then look for a hs_err*.log somewhere in your filesystem. That is the file that the JVM creates when a crash occurs.

The file should report a stack trace that will tell you more or less exactly where the crash happened. Review that piece of code and eventually ask on the mailing list. Before you ask on the mailing list gather all information that you have available and that will be useful to diagnose the problem. A good report should include:

  • Linux version (both kernel and distro). Can be obtained by running: uname -a
  • GCC Version: gcc --version
  • mapserver version and compile flags: run mapserv -v and/or report the configure flags
  • Java version and vendor: java -version
  • swig version (if used): swig -version
  • support libraries used and their version: gdal, proj, curl, postgis, etc
  • whatever you think could help

Additionally some will definitely find telling the reading of the venerable guide: how to ask questions the smart way.