Sunday, February 28, 2010

Command Line Options in Java

A while back I cobbled together a Java library to parse command line options to a Java program. The coding is probably a bit "inartistic", and there may well be better libraries out there, but I offer it up here in case anyone has a use for it. The zip archive contains the OptionManager class, a related exception class, and a small test program. (see below) I'm releasing it under the Apache License (version 2.0), which should be liberal enough for pretty much any use.

OptionManager supports four types of options: toggles (for instance, -q to turn on "quiet mode"); key/value pairs separated by spaces (such as "-f myfile" to load myfile); key=value pairs (e.g., "user=paul" -- the separating punctuation can be any specified character); and position arguments (such as using the third argument as the target directory).  Options can be Integer, Long, Double, Boolean or String. Keys can be case-sensitive or case-insensitive.

A segment from the test program illustrates its use:

package commandLine;
//...
  public static void main(String[] args) {
    // create an option manager
    String delimiter = "~";  // instead of key=value, we'll use key~value
    boolean caseSensitive = true;  // keys will be case-sensitive
    OptionManager opt = new OptionManager(delimiter, caseSensitive);  // create the option manager

    // create some options
    try {
      opt.declareKeyvalPairOption("opt_a", "-a", String.class, "an a");
        // string-valued; typical use: "-a some_string"; default value "an a"
      opt.declareKeyvalPairOption("opt_A", "-A", Integer.class, 17);
        // integer-valued; typical use "-A some_number"; default value 17
        // using both -a and -A would not work with case-sensitivity turned off
      opt.declareKeyvalStringOption("name", "name", String.class, null);
        // string-valued; typical use "name~my_name"; default is a null string
      opt.declarePositionalOption("posOpt0", 0, Double.class, 37);
        // double-valued; assigns the first argument not attached to a key; default 37.0
      opt.declarePositionalOption("posOpt1", 1, Long.class, Long.MAX_VALUE);
        // long-valued; assigns the second argument not attached to a key; default a very big integer
      opt.declareToggleOption("toggle", "-xon", "-xoff", Boolean.class, true, false, null);
        // boolean-valued; -xon toggles the option to true, -xoff toggles it to false
    } catch (OptionManagerException ex) {
       // deal with construction problems here
    }
   
    opt.parse(args);  // parse the command line
    try {
      // fetch the option values and display the results
      System.out.println("opt_a = <" + opt.fetchString("opt_a") + ">");
      System.out.println("opt_A = <" + opt.fetchInt("opt_A") + ">");
      System.out.println("name = <" + opt.fetchString("name") + ">");
      System.out.println("posOpt0 = <" + opt.fetchDouble("posOpt0") + ">");
      System.out.println("posOpt1 = <" + opt.fetchLong("posOpt1") + ">");
      System.out.println("toggle is " + opt.fetchBool("toggle"));
      String[] excess = opt.excessArguments();
      // if the command line contained arguments in addition to those parsed
      // (perhaps attempts to use non-existent options, or misspelled keys)
      // the excessArguments method will return them as strings
      if (excess.length > 0) {
        // deal with the excess arguments
      }
    } catch (OptionManagerException ex) {
      // deal with problems parsing arguments here
    }
  }

Update: I've added some new capabilities (and spruced up the included test program a bit).

Update #2: The utility, which also includes methods to parse file specifications with wildcards and find matching files, is now available from Sourceforge under the name JCLILIB, still under the Apache 2.0 license.

Tuesday, February 16, 2010

Time Limits in Choco Solver

I'm introducing myself to constraint programming as part of a research project. Since I program mainly in Java (I swore off C++ after too much time swearing at it), I went looking for an open-source CP library written in Java.  My second attempt was Choco, which seems to work quite well for my purposes. Like many non-commercial software projects, though, the documentation lags a bit behind the code. For some reason, there's no Javadoc available (at least short of compiling the source code myself, which to date I've been too lazy to do).

What prompts me to write this reminder to myself is the adventure I had with time limits. The Choco documentation (a PDF file dated 01/20/2010, so it's pretty current as of this writing) mentions two methods, setTimeLimit and setCpuTimeLimit. NetBeans refuses to believe that the setCpuTimeLimit method (or anything starting with 'setCpu') exists. That's fine; setTimeLimit should work for me.

Unfortunately, my code started generating an unlabelled exception, which I could not catch even if I surrounded the solve() method (which lists no thrown exceptions) with a try ... catch block. Running the solve() method inside a SwingBackgroundTask probably didn't enhance my debugging efforts, either. I eventually deduced the exception was a consequence of hitting the time limit, but had a heck of time figuring out what was throwing it.

The solve() method returns a Boolean (not boolean) result. Eventually I paid attention to that and figured it out: if the solver hits its time limit, it returns null rather than true or false. I was using the return value in a conditional, as in

if (solver.solve()) { // do something with solution }

and that was causing a null pointer exception if the return value was null.  Live and learn.

Wednesday, February 10, 2010

Ubuntu/Mint: Restoring My Password

 A couple of weeks ago my office PC (Linux Mint Helena) abruptly decided not to believe my password. There are multiple possible causes for this, including user forgetfulness (not the case here), deletion/corruption of the file containing the authorized passwords (maybe but doubtful in my case), or a malware affliction (which was my best guess, although I never identified the source of the problem). In any event, the immediate concern was to regain access to my PC.

Members of the open source community, both developers and users, are typically very generous in sharing their knowledge. If you are one of the first to encounter a problem, you can pop onto various forums (or maybe IRC channels), post a question, and with reasonable certainty receive help (or attempts at help) within a day or so. I'm not typically the first to encounter any particular problem, though, so for me the mantra is "Google is my friend".

Unfortunately, a number of hurdles stand in your way of finding exactly the right bit of advice. Uncertainty about the terminology to use can be one issue. Variations in operating system, version, configuration etc. can lead to substantial numbers of unhelpful search hits, as can different problems with similar symptoms.

Cutting to the chase scene, I found a number of suggestions online, the seventh of which worked. On the chance that some other poor soul will be desperately googling for a way to restore his/her password, I'll post my results here. These apply to Mint Helena, which is built on Ubuntu Karmic Koala, but my guess is that they apply fairly broadly to Linux distributions using the Grub 2 boot loader.

I'll first note that one source of confusion was the shift from Grub (in earlier versions of Ubuntu/Mint) to Grub 2. If your boot sequence normally gives you a nice looking menu of Linux kernels from which to choose (including a recovery mode), I suspect you're looking at Grub 2. Online postings that refer to rebooting and beating on some unsuspecting key until you get to the Grub menu probably refer to the earlier version of Grub.

In case it's of any use to other people pounding on a search engine, during my attempts to hack into my own machine I repeatedly encountered a message "could not access PID file for nmbd" when in recovery mode.

Stuff that didn't work (all starting from the Grub menu):
  • running dpkg in recovery mode (it updated some things but didn't help with the password problem);
  • updating the boot loader (i.e. Grub itself);
  • selecting the current kernel, using 'c' to get a command prompt, then running 'password ' to reset the password, followed by a reboot; 
  • selecting the kernel, using 'e' to edit the line, changing 'quiet splash' to 'single', then ctrl-X to reboot.
What worked (sorry, I'd like to credit the source but I've lost track of it):
  • In the Grub menu, select the current kernel and type 'e' to edit the line;
  • change 'ro quiet splash' to 'rw init=/bin/bash' and continue the boot sequence;
  • in the root shell that comes up, run 'passwd ' to change the password for the account named (i.e., your account) back to what it should be;
  • cross your fingers (toes too if you're sufficiently dexterous) and reboot.
Something I learned by experience (and which would have lowered my stress level a bit had I known it up front) is that the 'e' option in Grub only edits the boot loader command for this one instance; it does not make permanent changes to the Grub menu.

Tuesday, February 9, 2010

Help Vampires

I spend a portion of each day on various technical forums, mailing lists and USENET news groups, such as sci.op-research. (Yes, USENET lives on.) The denizens of these forums are generally quite polite to and patient with newbies, as one might hope. Some of the newbies hang around and answer as well as ask questions, and some ask, receive answers and move on.

Unfortunately, some people treat the forums as a replacement for one lobe of their brain. They're usually quite polite in their phrasing, express their gratitude periodically, but (probably without meaning to) they treat the other members of the forum the way an executive might treat an intern: "Please look this up for me."  "Please find this out for me."  "Please help me to avoid actually having to confront my own problems."

Someone on one of the mailing lists recently circulated a link to an old blog entry by Amy Hoy titled "Help Vampires: A Spotter’s Guide". It's both amusing and insightful.  If, like me, you occasionally find yourself wishing some questioner would just f**king google it, Hoy's post will resonate with you.

Friday, February 5, 2010

Mint: Reloading the OS

I'm a big fan of Linux in general, Ubuntu in particular, and now Mint (which is built on top of Ubuntu, and whose user interface I slightly prefer -- plus, after 37 years at Michigan State, it doesn't hurt that Mint's color scheme is green).  Among other things, Linux seems to be somewhat less vulnerable to hacking than Windows (possibly excluding Win 7, which IMHO hasn't been out long enough to be thoroughly battle tested).  Less vulnerable is not invulnerable, though.  I came into the office one day and discovered that my office PC wouldn't accept my password.  Once I finally fought my way in, I found two bogus user accounts with uid = 0 (the root account has uid = 0), which apparently made them impervious to deletion.  So I ended up reinstalling the OS to  get rid of them ... which was not as annoying as it might sound (although it was a bit time-consuming).

First off, in an earlier bout of upgrading, I decided to create a separate home partition.  This is easy, although partitioning is not for the faint of heart. You use GParted (graphical partition editor) to create a new partition, mount it and copy everything from your home partition to the new one.  Then you make the mount point of the new partition /home.  I'm oversimplifying a bit: excellent, detailed instructions are at http://www.linuxmint.com/wiki/index.php/Move_home_to_its_own_partition.  Having /home on a separate partition meant that I could reinstall the OS (from a Mint install CD) without losing anything from the home partition.  Even most (though not quite all) of my software settings came through intact.  Also, I'd put a few software programs (that I downloaded manually, not through Synaptic) on the home partition, so they did not need to be reinstalled.

Second, I got some useful feedback on the Mint user forums, including someone who pointed out to me Synaptic's File > Save Markings As and File > Read Markings.  Before the reload, I did the former, saving the file to my home partition. After the reload (and after Synaptic had done some automatic upgrades to catch the CD version up to current patch levels), I read back the markings and Synaptic went off and downloaded pretty much everything I'd installed or updated ("pretty much" meaning that which came from repositories it knows about).  After that, all I had to do was reinstall one or two packages I'd downloaded manually.

Which left just one thing.  Most of our office machines use DHCP, but I have a static IP address for mine, to facilitate remote log ins. I use NoMachine's free NX client/server (highly recommended) to remote desktop to my office PC. It didn't work at first, because I'd forgotten that after an OS reload I have to go back and put the static IP address back in (the OS defaults to DHCP). Which pretty much explains the title of this blog.