Alan Burlison's Work Related Ramblings

All | General | Java | NetBeans | Perl | Solaris

20061023 Monday October 23, 2006

Nice one, Team NetBeans!

Just read this glowing testimonial on The Inquirer for the NetBeans World Tour, which has reached Buenos Aires. I met Roumen Strobl briefly at the last Tech Day in London, and he seems like a really nice guy - I'm pleased to see he's even impressing those hard-nosed hacks over at The Inq ;-)

Although I work in the Solaris organisation, and I guess I am therefore supposed to be a C/make/vi bigot, I'm a bit of a NetBeans fan - I'm currently doing a lot of work on the OpenSolaris website, using NetBeans almost exclusively. I've used most of the NetBeans functionality - web development, Java application development and even J2ME, on both Solaris and "That other platform" ;-) and it's really nice to have a single, consistent development environment that works everywere I want to. I'm even trying to get my co-conspirator Gary to switch to the Dark Side ;-)

If I didn't want to get into trouble, I'd even say NetBeans is rather good ;-)

Posted by alanbur ( Oct 23 2006, 11:53:09 PM BST ) Permalink Comments [1]

20061008 Sunday October 08, 2006

Magic NetBeans logo

This video shows a guy called Marco Tempest doing some street magic with something that looks exactly like the NetBeans logo. Bizzare!

Posted by alanbur ( Oct 08 2006, 02:40:11 PM BST ) Permalink Comments [0]

20060811 Friday August 11, 2006

HTML-formatted JLabels and Matisse

I was trying to lay out a dialog that used the Matisse layout manager and included some JLabels that used HTML tags to format the content. Although it looked fine in the NetBeans GUI designer, previewing the dialog showed that the content at the bottom of the dialog had disappeared because the dialog wasn't being made large enough. After a lot of fiddling around, it turns out that this is because Matisse can't determine the size of the JLabel properly if the text in it wraps over more than one line. The workaround is simple - insert <br> tags at the points at which the text wraps onto a new line, then the size of the JLabels can be determined correctly.

Posted by alanbur ( Aug 11 2006, 03:20:01 PM BST ) Permalink Comments [2]

20060628 Wednesday June 28, 2006

Netbeans, JNI, Webstart and Apache

Although NetBeans has a Webstart module, it isn't really up to snuff yet, as I detailed in a previous post. Specifically, it doesn't support JNI code, and it can't sign the JAR files, which is needed if you have any JNI code to deploy. However, it's possible to add the necessary goop to the Netbeans build.xml file to automate this, with the help of a couple of short scripts. In addition, we can compress the JAR files using pack200 - this can give really impressive results, for example it compresses the Matisse swing-layout-1.0.2.jar file down from 155Kb down to just 19Kb! The final flourish is to get the build process to generate the Apache configuration files needed to serve Webstart files correctly. The examples below are all Win32-based as that's the environment my application works in, but they are trivially convertable to *nix versions.

The first step is to create a self-signed certificate if you don't have a proper one, the steps for doing this are described in the keytool documentation - you need to first create the certificate using the -genkey option, then sign it with the -selfcert option.

Next, we need to add some extra properties into the nbproject/project.properties.file file to describe the key file and the directories we will use for the webstart build, you should edit these to reflect your setup:

# Properties needed for signing JAR files
webstart.src.dir=webstart_src
webstart.dist.dir=webstart_dist
key.alias=bleaklow.com
keystore.location=d:/shared/bleaklow.keys
keystore.password=password

We also need a couple of helper scripts - you should put these in the nbprojects directory:

signpack.bat

@echo off
REM compress and sign a JAR file, remove the original
REM args are <JAR> <keystore> <password> <alias>
pack200 --repack --no-keep-file-order --strip-debug %1
jarsigner -keystore %2 -storepass %3 %1 %4
pack200 --effort=9 %1.pack.gz %1
del %1

This script will pack the supplied JAR file using the pack200 utility, and will then sign it with the supplied key. The --repack dance is necessary because packing a file with pack200 may change the order of the contents of the file and if it was signed this would break the signing, so we need to pack it, unpack it, then sign it and finally repack it again.

genvar.bat

@echo off
REM generate a type mapping file for Apache
REM arguments are <JAR>
echo URI: packed/%1.pack.gz>%1.var
echo Content-Type: application/x-java-archive>>%1.var
echo Content-Encoding: pack200-gzip>>%1.var
echo.>>%1.var
echo URI: unpacked/%1>>%1.var
echo Content-Type: application/x-java-archive>>%1.var

This script generates the type mapping file needed to get Apache to serve either the normal unpacked versions or the packed versions of the files we are going to deploy. The layout of the generated file needs to be exact, down to each whitespace - if you want to understand this in detail see the pack200 deployment guide and the Apache documentation for content negotiation, but my advice is just to accept that the magic files created by the script work ;-)

Now we need to create the Apache .htaccess file and the Webstart descriptor (JNLP) file. Create a new directory - webstart_src and create the following two files in it:

.htaccess

AddType application/x-java-jnlp-file .jnlp
AddType application/x-java-archive .jar
AddHandler application/x-type-map .var
Options +MultiViews
<Files *.pack.gz>
    AddEncoding pack200-gzip .jar
    RemoveEncoding .gz
</Files>

This assumes that your webserver httpd.conf is set up to allow the .htaccess file to function, if not see the Apache docs for how to do this. This file sets up the MIME types for the Webstart files we are going to serve, and establishes the content negotiation mechanism needed to serve the packed versions.

OziGeocacheUK.jnlp

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE jnlp PUBLIC "-//Sun Microsystems, Inc.//DTD JNLP 1.5//EN"
  "http://www.netbeans.org/jnlp/DTD/jnlp.dtd">
<jnlp spec="1.0+"
 codebase="http://oziapi-java.sourceforge.net/OziGeocacheUK"
 href="OziGeocacheUK.jnlp">
  <information>
    <title>OziGeocacheUK</title>
    <vendor>bleaklow.com</vendor>
    <homepage href="http://oziapi-java.sourceforge.net/OziGeocacheUK"/>
    <description>GeocacheUK access for OziExplorer</description>
    <icon href="logo64.gif" kind="splash"/>
    <icon href="logo32.gif"/>
    <offline-allowed/>
    <shortcut>
      <desktop/>
    </shortcut>
  </information>
  <security>
     <all-permissions/>
  </security>
  <resources os="Windows">
    <j2se version="1.5+" href="http://java.sun.com/products/autodl/j2se"/>
    <jar href="OziGeocacheUK.jar"/>
    <jar href="OziAPI.jar"/>
    <jar href="swing-layout-1.0.1.jar"/>
    <nativelib href="OziAPINative.jar"/>
  </resources>
  <application-desc main-class="com.bleaklow.ozigeocacheuk.Main"/>
</jnlp>

You should name this file appropriately for your project - in my case the Netbeans project is called OziGeocacheUK. Obviously this file will need modifying to suit your particular project - the format of JNLP files is documented here, but I'll point out the most important bits:

In my directory I also have two icon files, icon32.gif and icon64.gif which are 32x32 and 64x64 pixel icon files that I use with my application. If you do have these files, they must be GIF format, Webstart doesn't support PNG.

The final step is to add an Ant target to build a Webstart-deployable file tree to your build.xml file, the following should be fairly self-explanatory:

<!-- Create a webstart tree -->
<target name="build-webstart" depends="init,compile"
  description="Build a webstart version">
    <!-- 1. Create a fresh webstart directory -->
    <delete dir="${webstart.dist.dir}"/>
    <mkdir dir="${webstart.dist.dir}/unpacked"/>
    <mkdir dir="${webstart.dist.dir}/packed"/>

    <!-- 2. Copy the source files -->
    <copy todir="${webstart.dist.dir}">
        <fileset dir="${webstart.src.dir}"/>
    </copy>

    <!-- 3. Copy in all the java JAR files -->
    <copy todir="${webstart.dist.dir}/unpacked" flatten="true">
        <fileset dir="${dist.dir}" includes="**/*.jar"/>
    </copy>

    <!-- 4. Create a JAR file from the DLLs -->
    <jar destfile="${webstart.dist.dir}/unpacked/OziAPINative.jar"
      basedir="${dist.dir}" includes="*.dll"/>

    <!-- 5. Create packed and signed versions of the JAR files -->
    <copy todir="${webstart.dist.dir}/packed">
        <fileset dir="${webstart.dist.dir}/unpacked" includes="*.jar"/>
    </copy>
    <apply executable="nbproject/signpack.bat" type="file"
      resolveexecutable="true" failonerror="true" ignoremissing="false">
        <fileset dir="${webstart.dist.dir}/packed" includes="*.jar"/>
        <srcfile/>
        <arg value="${keystore.location}"/>
        <arg value="${keystore.password}"/>
        <arg value="${key.alias}"/>
    </apply>

    <!-- 6. Sign all the unpacked JAR files -->
    <signjar
        alias="${key.alias}"
        keystore="${keystore.location}"
        storepass="${keystore.password}" >
        <fileset dir="${webstart.dist.dir}" includes="**/*.jar" />
    </signjar>

    <!-- 7. Create the Apache type mapping files -->
    <apply executable="nbproject/genvar.bat" type="file" relative="true"
      dir="${webstart.dist.dir}" dest="${webstart.dist.dir}"
      resolveexecutable="true" failonerror="true" ignoremissing="false">
        <fileset dir="${webstart.dist.dir}/unpacked" includes="*.jar"/>
        <flattenmapper/>
    </apply>
    <fixcrlf srcdir="${webstart.dist.dir}" eol="lf" eof="remove"
       includes="*.var"/>
</target>

Running this target in Netbeans produces the following directory tree under the webstart_dist subdirectory of your Netbeans project:

.htaccess
logo32.gif
logo64.gif
OziGeocacheUK.jnlp
packed\OziAPI.jar.pack.gz
packed\OziAPINative.jar.pack.gz
packed\OziGeocacheUK.jar.pack.gz
packed\swing-layout-1.0.1.jar.pack.gz
unpacked\OziAPI.jar
unpacked\OziAPINative.jar
unpacked\OziGeocacheUK.jar
unpacked\swing-layout-1.0.1.jar

This tree can then be directly deployed to an Apache webserver as it contains everything needed to serve your application. I'll cover how you can get Netbeans to automatically deploy the application in a subsequent post.

Posted by alanbur ( Jun 28 2006, 12:10:35 PM BST ) Permalink Comments [6]

20060627 Tuesday June 27, 2006

Building Win32 JNI code using NetBeans and MinGW

This entry is copied from my personal blog - I'm going to revamp that site in a while, so I've put a copy of it over here with the rest of my Java and NetBeans-related stuff. I've updated it to refer to the latest versions of both the JDK and NetBeans.

I use a GPS mapping application called OziExplorer, and just for laughs I started fiddling around with encapsulating the API for the application so that I could use it from Java. Access to the API is via a DLL, which meant delving into the scary world of both JNI and building DLLs on Win32. Whenever I've tried to do this in the past, it always seemed to require loading vast amounts of unwanted crap on my machine so that I got a compiler that worked, arcane spells involving mapfiles, sacrifical slaughter of various wildlife and so much pain in general that I'd always given up in disgust. However, I'm a glutton for punishment so I thought I'd have another crack at it. I couldn't find anything on the web that told you everything you needed to know all in one place, so I thought I'd document it since I'd got it all working.

Step 1

If you don't already have it, download and install the latest version of the Java Development Kit. I used version 1.5.0_07, the instructions below should work with other versions... Do this first to make you life easy - the NetBeans installer will pick up the latest version of the JDK that you have installed.

Step 2

If you don't already have it, download Netbeans and install it. I used version 5.0, that or anything later should be fine.

Step 3

Install the Ant cpptasks module. This adds support to Ant for building C code. The only bit of this you need is cpptasks.jar, and you should copy it to the lib subdirectory of the new version of Ant you installed in step 3.

Step 4

Install a copy of MinGW. This is a Win32 version of the well-know gcc compiler. Unfortunately the MinGW documentation is as clear as mud on exactly how to do this - if you go to the download page you get a huge shopping list of bits, with no clear directions as to which bits you actually need - the documentation is clearly written with the assumption that you already know. The easiest way to sort the mess out is to download the "MinGW" installer - look for the section entitled "MinGW" and you'll see a file called MinGW-<version>.exe, in my case <version> was 4.1.0. Download and run that and it will ask you for an installation location (I used C:\Program Files\MinGW, then deselect everything except The minimal set of packages required to build C/C++. Leave everything on the next screen selected and start the install. The installer will then pull the bits you need from the web. You can probably prune the list, but the whole thing is only 60Mb so it didn't seem worth the effort.

Step 5

Set up your environment. In order for Ant to be able to run the compiler, it will need to be able to find it, and the same goes for any DLLs that you will use or generate. Windows particularly sucks in this area, so to save spraying DLLs all over my C drive I created the directory C:\DLLs to dump everything in. With that done, right click on My Computer, click on Advanced then Environment Variables. Select Path from the list, then add ;C:\Program Files\MinGW\bin;c:\DLLs to the end, or wherever you installed the compiler, and wherever you intend to install your DLLs. If it isn't already there, add the path to the Java bin directory as well - in my case this is ;C:\Program Files\Java\jdk1.5.0_04\bin. I don't know if it is necessary, but at this point I logged out/into Windows to make sure my environment changes took effect.

Step 6

Add the appropriate JNI code to your project - I don't intend to cover the process for doing that here, instead see the JNI documentation. The tricky bits are getting everything to link together, the problem being caused by the awful Win32 name decoration mess. Basically, DLLs usually use the stdcall calling convention, and when you build them with gcc by default it 'decorates' the symbol names by appending '@' and a number (the number of bytes that will be popped off the stack by the function) - follow the links for details. There are two main issues:

Step 7

Javap can be used to output the method and field signatures that will be needed in the JNI C code. Unfortunately there isn't an Ant target to do this, so it's necessary to roll our own. The first thing is to put the following 2-line Windows batch script somewhere - I called mine javap-s.bat and put it under the nbproject subdirectory of my NetBeans project. See the following section for the explanation of how to hook it into Ant.

@echo off
javap -s -private -classpath %~dps1 %~n1 > %~s2

Step 8

The next tricky bit is adding the necessary goop to your project.xml file to get Ant to build your code bearing in mind the issues I described above. This isn't helped by the fact that the cpptasks add-on has a bug that means it doesn't work properly with MinGW. Here's what works for me. Firstly, add the following to the nbproject/project.properties file:

lib.dir=lib
jni.dir=jni
signatures.dir=signatures

Then add the following to the build.xml file:

    <!-- Load the cpptasks task -->
    <taskdef resource="cpptasks.tasks"/>
    <typedef resource="cpptasks.types"/>

    <!-- Compile the JNI code into a DLL -->
    <target name="-post-compile">
        <!-- Make sure the output directories exists -->
        <mkdir dir="${jni.dir}"/>
        <mkdir dir="${signatures.dir}"/>

        <!-- Run javah to produce a header file for the JNI functions -->
        <javah verbose="yes" classpath="${build.classes.dir}"
         destdir="${jni.dir}">
            <class name="com.oziexplorer.OziAPI"/>
        </javah>

        <!-- Run javap to produce files containing the JNI signatures -->
        <apply executable="nbproject/javap-s.bat" dest="${signatures.dir}"
          resolveexecutable="true" failonerror="true" ignoremissing="false">
            <fileset dir="${build.classes.dir}/com/oziexplorer"
              includes="*.class"/>
            <mapper type="glob" from="*.class" to="*.txt"/>
            <srcfile/>
            <targetfile/>
        </apply>

        <!-- Check the library definition file is up to date -->
        <apply executable="dlltool" dest="${jni.dir}" failonerror="true"
          ignoremissing="false">
            <filelist dir="${src.dir}/com/oziexplorer" files="OziAPI.def"/>
            <mapper type="glob" from="*.def" to="lib*.a"/>
            <arg value="-k"/>
            <arg value="--dllname"/>
            <arg value="OziAPI.dll"/>
            <arg value="--def"/>
            <srcfile/>
            <arg value="--output-lib"/>
            <targetfile/>
        </apply>

        <!-- Compile the C code -->
        <cc link="shared" outtype="shared" multithreaded="true" optimize="speed"
          objdir="${jni.dir}" outfile="${jni.dir}/OziAPI">
            <compilerarg value="-Wall"/>
            <compilerarg value="-D_JNI_IMPLEMENTATION_"/>
            <compilerarg value="-fno-strict-aliasing"/>
            <linker name="gcc">
                <linkerarg value="--kill-at"/>
                <linkerarg value="-oOziAPIJava.dll"/>
            </linker>
            <sysincludepath location="${java.home}/../include"/>
            <sysincludepath location="${java.home}/../include/win32"/>
            <fileset dir="${src.dir}/com/oziexplorer" includes="OziAPI.c"/>
            <libset dir="${jni.dir}" libs="OziAPI"/>
        </cc>
    </target>

    <!-- Copy the stripped DLL and OziAPI.DLL to the dist directory -->
    <target name="-post-jar">
        <apply executable="strip" dest="${dist.dir}" failonerror="true"
          ignoremissing="false">
            <filelist dir="${jni.dir}" files="OziAPIJava.dll"/>
            <mapper type="glob" from="*.dll" to="*.dll"/>
            <arg value="-s"/>
            <srcfile/>
            <arg value="-o"/>
            <targetfile/>
        </apply>
        <copy todir="${dist.dir}">
            <fileset dir="${lib.dir}" includes="*.dll"/>
        </copy>
    </target>

Let's go through it bit at a time:

Although it took a bit of fiddling to get all the bits to play together, the process of building DLLs is far easier than it was in the past - the work that the gcc and MinGW crowd have done to do all the dirty work for you is really very impressive.

Posted by alanbur ( Jun 27 2006, 04:37:33 PM BST ) Permalink Comments [0]

Using NetBeans with SourceForge (Win32)

I have a Win32 project hosted on SourceForge, and as it took me a bit of fiddling to get NetBeans to work with the SourceForge CVS server I thought I'd document the steps needed - although there is some information on the NetBeans and SourceForge sites, there isn't an all-in-one guide.

The prerequisites are that you already have NetBeans installed, and that you have an account on SourceForge.

The first step is to grab a copy of PuTTY if you don't already have it - PuTTY is a telnet/ssh client and you'll need it to connect via SSH to SourceForge.

Once you have PuTTY installed, use the PuTTYgen utility to generate a new SSH key. It's easiest if you save the keys without a passphrase - if you don't you'll have to run the Pageant authentication agent that comes with PuTTY and load your key into it before trying to connect to SourceForge. If you do decide to save them without a passphrase, make sure you put the files somewhere safe! Save both the public and private key files - I used the filenames SourceForge-shell.pub and SourceForge-shell.ppk. Also export the private key as a OpenSSH key (Conversions->Export OpenSSH Key), I saved mine in a file called SourceForge-shell.openssh. Leave PuTTYgen open - you'll need it in the next step.

Go to the Edit SSH Keys page on SourceForge and cut+paste the contents of the box in PuTTYgen labelled Public key for pasting into OpenSSH authorized_keys file into the SourceForge Authorized keys: form, and press submit. It will take at least 10 minutes for SourceForge to update your keys.

After waiting, check you can connect to the CVS server on SourceForge from the command-line before setting up NetBeans. Start up a command prompt and run the PuTTY plink command to check that you can connect to the SourceForge CVS server. In the example below, substitute in the appropriate values for your setup for all the my_ strings.

$ c:\progra~1\putty\plink -ssh -i c:\my_safe_dir\SourceForge-shell.ppk my_sf_id@my_sf_project.cvs.sourceforge.net
Using username "my_sf_id".
Last login: Wed May 27 16:20:41 2006 from 192.168.1.1

Welcome to cvs1.sourceforge.net

This is a restricted Shell Account
You cannot execute anything here.


$

Once you have checked you can access SourceForge CVS from the command-line, set up access in NetBeans. Select CVS->Checkout and for CVS Root enter (substituting the my_ values appropriately, as in the command-line test) :ext:my_sf_id@my_sf_project.cvs.sourceforge.net:/cvsroot/my_sf_project. Select the Use External Shell button and in the SSH Command box enter c:\program files\putty\plink -ssh -i c:\my_safe_dir\SourceForge-shell.ppk, then press Next. NetBeans will then connect to SourceForge and you can browse and checkout modules from CVS.

One thing to note - if you subsequently come back to the Checkout dialog, the Use External Shell setting doesn't 'stick', so you will have to relselect the button before pressing Next.

Posted by alanbur ( Jun 27 2006, 03:26:52 PM BST ) Permalink Comments [4]

20060202 Thursday February 02, 2006

Oooh! Shiny! ... NetBeans 5.0

I've been very happy with NetBeans 4.1, but I've been keeping an eye on all the new stuff in NetBeans 5.0 with anticipation, especially the new Mattise GUI designer. Over the last couple of days I've been knocking together a simple Java GUI and today I finally cracked, and I grabbed a copy of 5.0 from the internal staging servers rather than waiting for the public release of 5.0 and having to fight my way through the download crowds - hey, working for Sun has to have some benefits, right? ;-)

First impressions are very favorable - having already used NB4.1 a fair bit, NB5.0 feels very familiar from the start - which is a good thing - I get very tetchy when stuff is moved around just for the hell of it between releases of a product. There's a lot of nice usability touches in there - there's an Options dialog that allows you to set the most common preferences quickly and easily, and at last you can edit your code templates easily - it always bugged me that I had to hand-edit the banner blocks in each of the generated files. The new CVS stuff seems to just work - I pointed it to a project of mine on sourceforge and it synced up with no tweakage required.

One of the most noticeable features is the new Matisse GUI designer. I'd already done some outline design in my app and I expected I'd have to throw it all away and start over, but it turns out you can pretty easily retrofit existing GUI designs to use Matisse - I'd been using the GridBag layout manager, and to switch to Matisse I just had to right-click on the layout manager icon in the Navigator pane and select 'Free layout'. Once that's done you can start dragging your components around Matisse-style - nice!

I also pulled down the profiler and mobility packs and tested out an existing MIDP application which all worked fine. The only thing I'd forgotten to do was to pull down a copy of cpptask so I could build the JNI code I have in one of my Win32 projects. I've only given the tyres a cursory kick so far, but I like what I see.

Whilst I was looking around for the the install images I found one of the internal wikis where the team were discussing categories of developers who might use NetBeans, and I think I'm in the 'tough nut to crack' category, i.e. someone who uses mainly vi and make for development. I must admit I was initially sceptical about NetBeans, having had some *bad* experiences in the past with IDEs that were anything but what their names suggested, but I'm a firm convert, and NetBeans is now my prefered environment - I just wished it handled C with the same degree of cleverness as it does Java ;-)

Nice work and kudos to the NetBeans community!

Posted by alanbur ( Feb 02 2006, 01:08:51 AM GMT ) Permalink Comments [0]

20050726 Tuesday July 26, 2005

Win32, JNI and NetBeans

I'm back at Sun, and while I was away (amongst other things) I wrote an entry on my personal blog entitiled Smörgåsbord: Building Win32 JNI code using NetBeans 4.1 and MinGW that might be of interest, if it is take a look and feel free to send me corrections ;-) Posted by alanbur ( Jul 26 2005, 11:35:21 PM BST ) Permalink Comments [1]