Thursday, July 22, 2010

Args4j, Netbeans, and Ant Oh My!

Args4j under netbeans


For some reason Args4j does not work with the simulated command line under netbeans. The application must be deployed for the library to work properly. A controlling shell script needs to wrap the call into the jar file holding the Java based command. The best place to keep this script file is the top directory of the project under netbeans. Netbeans performs no modification of this file. It just needs to have the .sh extension stripped when fully deployed and the file permissions set to execute. Rather than look for a plugin it seems that some simple modification of the ant build script was in order.

Ant is an xml based scripting language helpful in the processing of java builds. Its input file is called “build.xml” highlighting its role in the build process. The file contains “targets” and “tasks”. The tasks are the low level ant commands used to move files, compile files, or create archives. “targets” are the goals of the build process. Each target maintains a dependency list . Ant will check through the dependency list to see if anything prior in the dependency chain of a target has changed and will then execute all targets necessary to provide the latest changes to the build process.

Ant and Netbeans


Every Netbeans project has a build.xml file in the project directory. It imports all of its targets from a prepackaged ant script tailored for the netbeans environment. This means that there is basically nothing in this build.xml other than a few comments to tell the developer how to modify it.

The shell script source file needs to be placed in the distribution directory. The building process under ant knows all about how to make jar files compiled from the Java source code and copy them to the appropriate location in the distribution directory of the project (aptly named “dist”). But it really doesn't know about shell scripts associated with that source code (at least I am unaware if it does have a way of handling it). Ant needs to be told to move the script file from its location at the top of the project directory into the distribution directory. This operation must be performed every time the distribution directory is created. This means that one of the predefined targets netbeans provides is an appropriate place to put the code to move the script file.

The ant target looks like this:
< target name="-post-jar" >
< copy todir="${basedir}/dist">
< fileset dir="${basedir}" includes="*.sh"/>
< /copy>
< /target>

This target automatically gets run after the jar file for the project has been built and placed in the dist directory. The .sh on the shell script needs to come off but that will happen later. The .sh extension will uniquely identify new shell script commands during the deployment phase. This will distinguish them from already existing commands in the $HOME/bin directory.

The -post-jar target is an empty target created for netbeans ant scripts so the user can override it and place some specialized build handling instructions there. It is perfect for the purposes of moving files that netbeans doesn't really know about but are necessary for the execution of this java command.

Once the distribution directory is properly populated a custom target can be built to move the executable code and script file to $HOME/bin or other such user command directory. A custom target in the build.xml file called deploy-home:
< target name="deploy-home" depends="-post-jar">
< copy todir="${env.HOME}/bin">
< fileset dir="${basedir}/dist" excludes="README.*"/>
< /copy>
< chmod perm="777" type="file">
< fileset dir="${env.HOME}/bin">
< include name="*.sh"/>
< /fileset>
< /chmod>
< move todir="${env.HOME}/bin">
< fileset dir="${env.HOME}/bin">
< include name="*.sh"/>
< /fileset>
< mapper type="glob" from="*.sh" to="*"/>
< /move>
< /target>

The above target accomplishes 3 things. It copies files from <project>/dist to $HOME/bin. It then sets the permissions of the executable shell scripts. It then strips the .sh extension using the “move” task. Targeting a fileset of all .sh files. In this manner only newly deployed shell scripts are the target of the “chmod” task.

Executing a custom ant target in Netbeans


In the netbeans GUI on the left side there are a number of tabs: one for projects and one for files are there. By clicking on the files tab a new file tree view is displayed and by clicking on the project of interest the build.xml for that project can be exploded. This file viewer tab knows about ant build.xml files and will explode all the available targets. So after hand editing build.xml and adding the custom target “deploy-home” it will now appear as one of the targets in this netbeans exploded view. Right clicking the mouse on this task will bring up a pop-up menu, select Run Target and ant will perform the commands enclosed in the target definition. In the case above everything in the directory “dist” will be copied to $HOME/bin.

Java Based Command Example

Here is the code for a shell style command written in Java. The command uses args4j which uses @Option parameterization to input the command flag and usage argument along side the Java Object field name that will store the information. By using the args4j CmdLineParser class and the parseArguments method, all command line parameters are neatly stored in their corresponding field names.

This particular command is for getting files from the TapForms program on an iphone and downloading the file to a macbook. TapForms runs a web server so when the iphone is connected over wifi that webserver can accessed and files downloaded in essentially a webdav access.


/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package webdavaccess;

/**
*
* @author tmcguire
*/
import com.googlecode.sardine.Sardine;
import com.googlecode.sardine.SardineFactory;
import com.googlecode.sardine.util.SardineException;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
//import org.kohsuke.args4j.Argument;
import org.apache.log4j.Appender;
import org.apache.log4j.ConsoleAppender;
import org.apache.log4j.SimpleLayout;

import org.kohsuke.args4j.CmdLineException;
import org.kohsuke.args4j.CmdLineParser;
import org.kohsuke.args4j.Option;
//import org.kohsuke.args4j.spi.BooleanOptionHandler;

public class GetTFFileCmd {

@Option(name = "-ip", usage = "sets ip address")
String ipaddr;
@Option(name = "-port", usage = "sets ip port number")
String port;
@Option(name = "-file", usage = "sets the full file path of Tapform file")
String filename;
@Option(name = "-?", usage = "prints usage")
Boolean help = false;
static Logger myLogger = Logger.getLogger(GetTFFileCmd.class.getName());
static Appender myAppender;

public GetTFFileCmd() {
this.filename = "/Exports/ProgressNote.csv";
this.port = "8080";
this.ipaddr = "10.0.1.46";

}

private void copy(InputStream in, OutputStream out) throws IOException {
byte[] b = new byte[8192];

int read;
while ((read = in.read(b)) != -1) {
out.write(b, 0, read);
}
}

public void run() throws SardineException, FileNotFoundException, IOException {
BufferedOutputStream os = null;
PrintStream xmls = null;
File f = null, fxml = null;
Sardine sardine = null;
InputStream srdis = null;

sardine = SardineFactory.begin("anonymous", "");

System.out.println("sardine factory intialized");
System.out.println("InputStream from: "+"http://" + this.ipaddr
+ ":" + this.port + this.filename);

srdis = sardine.getInputStream("http://" + this.ipaddr
+ ":" + this.port + this.filename);

os = new BufferedOutputStream(new FileOutputStream("ProgressNote.csv"));

this.copy(srdis, os);
os.close();
}

public static void main(String[] args) {
myLogger.setLevel(Level.ALL);

// Define Appender
myAppender = new ConsoleAppender(new SimpleLayout());

//myAppender.setLayout(new SimpleLayout());
myLogger.addAppender(myAppender);
GetTFFileCmd gfile = new GetTFFileCmd();

CmdLineParser parser = new CmdLineParser(gfile);
try {
parser.parseArgument(args);
if (gfile.help) {
parser.printUsage(System.out);
System.exit(0);
}
System.out.println(gfile.ipaddr+":"+gfile.port+gfile.filename);
gfile.run();
} catch (SardineException ex) {
Logger.getLogger(GetTFFileCmd.class.getName()).log(Level.FATAL, null, ex);
} catch (FileNotFoundException ex) {
java.util.logging.Logger.getLogger(GetTFFileCmd.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
} catch (IOException ex) {
java.util.logging.Logger.getLogger(GetTFFileCmd.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
} catch (CmdLineException ex) {
Logger.getLogger(GetTFFileCmd.class.getName()).log(Level.FATAL, null, ex);
parser.printUsage(System.err);
}

}
}

Friday, July 2, 2010

Java based Shell Commands

The ultimate goal is to produce java command classes that run on the unix command line looking like any other shell script in its usage. This means the Java application must parse command line arguments. In java there is usually a library that already exists to accomplish standard bits of functionality. Parsing the command line is no exception, I found the args4j library to help parse command line arguments.
You can find it at:

args4j

The library uses Java 5 attributes to tag fields in the class that will represent the values of the command line arguments once parsed. The library will parse the arguments and sets the field attributes of the class. Now the programmer only needs to reference those values at the appropriate time. It's all quite easy.

Netbeans

This is the development environment I use. It's free, it does a lot without being overbearing. I run almost all my programs under Netbeans as well. I had forgotten how to run Java at the command line. Mainly because I had worked under a microsoft platform where I didn't have a suitable shell environment.

Now that I'm using the mac how do you package the Java application from Netbeans to run on the Command line? How do we deploy from Netbeans so the Java commands I program are available at the command prompt like other unix scripts and programs?

Some of the information on netbeans deployment of java apps can be found here:

netbeans java deployment