by Nathan Hamblen

Life outside the IDE

feature
Dec 18, 200719 mins

Coding in a parallel toolset has its perks -- freedom, for one

For many Java developers, the IDE is more than a high-powered telescope, it’s the Milky Way itself. In this article Coderspieler Nathan Hamblen reminds you how invigorating it can be to write code the old-fashioned way — by typing in a plain text editor and building with an external script. Not only will this old-school skill save you valuable time when your IDE goes awry, it could be your ticket to learning about newer technologies like Buildr, Jetty, JavaRebel, and more.

Nothing has done more to shape the experience of contemporary Java programming than its set of popular integrated development environments, or IDEs. As was true for C and C++ code, the benefits of editing Java code with feedback from a compiler and debugger are many. Compilation errors can be located visually rather than by line number, and execution attached to a debugger can be stepped through without a context switch — and those were 1.0 features.

Java as a platform has benefited enormously from its ecosystem of user-expandable IDEs. NetBeans and JBuilder set the bar initially with their support for plug-ins that added functionality, and IntelliJ IDEA distinguished itself with refactoring tools, but Eclipse swept the market by being expandable, free, heavily featured, and, thanks to its SWT framework, indistinguishable from standard Windows applications. (When Eclipse was first developed, Swing showed little hope of becoming satisfactorily responsive or seamless.) Eclipse and its base of plug-ins grew so quickly that JBuilder rebuilt itself on the platform, but other IDEs that held out now profit from a Swing toolkit that works almost as well as — or even better than — SWT, depending on the platform.

A fall from grace

It’s a curious turn of computing history that, just as the mass market had finally converted to protected-memory operating systems, programmers themselves began to work in an environment devoid of program isolation. For most Java programmers, Eclipse is effectively the operating system. It’s the application you open first and use for almost every part of your work; it can have software added to it, executed, configured, and removed. But as an operating system, it’s particularly shoddy. Even the non-OS Windows 3.1 was able to load and unload software without restarting, but as Figure 1 shows, Eclipse cautions the user against taking such liberties.

Figure 1. Eclipse may need to restart (click for larger image)

And though live installation of plug-ins might not be a practical concern, the fact that plug-ins are not sufficiently isolated for the operation leads directly to them bringing down the ship when things go awry. Some plug-ins are incompatible with others and refuse to work at all; some become gremlins that cause visual quirks and fiendishly crash the entire VM (just as you’re really getting things done). To eliminate the suspected troublemaker, you must often find and delete dozens of packages from the filesystem.

Along with fending off stability gremlins, a fair amount of effort is required to configure software that comes with such an eye-popping array of features and has the capability to add infinitely more. It’s easy to spend an entire day getting things just right — a day that could easily be lost if the installation goes south. A Java IDE is nothing if not high-maintenance.

Counterculture

Even when an IDE is working swimmingly, you might still reasonably doubt that its automating features are without a downside. It’s handy to have a tool that generates getters and setters, the six lines of baggage that travel with every bean property. But that such features are universal to Java IDEs has blinded us to weaknesses of the language and the downsides of boilerplate code.

Some examples are subtle. In the Wicket framework (see Resources for a link to this and other tools discussed here), values are bound to components with an implementation of the IModel interface. Thanks to anonymous subclasses, it’s possible to instantiate a model in place that makes use of the current scope. Given integer fields a and b, we can create a read-only Label that displays their sum, as shown in Listing 1.

Listing 1. Binding a Wicket model with an anonymous subclass

new Label("sum", new AbstractReadOnlyModel() {
      @Override
      public Object getObject() {
        return a + b;
      }
    });

This pile of code is largely IDE generated. AbstractReadOnlyModel is supplied by name completion and auto-imported. Once you open the braces for its anonymous subclass, a getObject() method returning null is suggested; you need only supply the body. It’s easy to go through the whole exercise without considering that the mandatory words public Object getObject() say little about this particular label. If you have 100 labels with 100 different ways of deriving their values from a and b, you will have 100 lines of that same signature.

Because this boilerplate is so easily generated, its problems are not concretely felt in the fingers, but the detriment to readability is very real. The eyes must filter out numerous passive symbols to process the effective few. If Java supported first-class functions, we could significantly improve on this approach by reducing the code to its unique logic, seen in the slimmed-down Listing 2. (The listing uses Scala syntax, which we’ll discuss in more detail towards the end of this article.)

Listing 2. Binding a model with an anonymous function (Scala syntax)

new Label("sum", () => a + b)

Aside from their role enabling a boilerplate stalemate, IDEs can unwittingly stifle the adoption of new technologies. Because users have become accustomed to built-in support for everything, anything that requires external software — particularly anything using the command line — is removed from serious consideration. Up-and-coming projects are distracted by the need to either make an appearance inside Eclipse or be invisible to users. Advances in dependency management, for example, have been slowed by the GUI-bedazzled conviction that the process depicted in Figure 2 is an efficient way to add and upgrade dependencies.

Figure 2. Managing dependencies by mouse (click for larger image)

Mixing in

This isn’t to say that we should delete our IDEs and go back to painting machine code on cave walls; still, it’s a good idea to go outside and breathe every once in a while. There are other applications to enjoy, other directories outside of workspace/, other languages than Java. Freedom to work outside the IDE is freedom to explore new things, long before they can be smoothly used (or used at all) inside a preferred IDE.

By maintaining a parallel toolset, you can have that freedom and be better prepared the next time your IDE mega-widget self-destructs. The best thing about smaller tools is that they’re replaceable, and simple enough that replacement is not often necessary. In a worst case scenario — not that those ever happen — you may need to fix a critical bug with nothing but an ssh terminal, and that will be a lot easier if your brain cells aren’t atrophied from exclusive use of the IDE cheat mode.

This article describes a handful of the many independent programming tools available. Its accompanying example application, called calc, is a testbed for experimenting with these tools. The Java version of the summing model above is from calc, as well as the source code and markup that appear below. (You can download the complete code from the Resources section.)

Editopia

The fundamental coder’s tool is one for editing code. Endless Internet wars — the traditional kind, with no stakes at all — have been fought over which is best. Everyone can and will continue to use whatever editor personally feels right, with largely no effect on anyone else. The one thing almost everyone agrees on is that no IDE has a general-purpose text editor that is worth using independently.

Because of their breadths of purpose and long histories, text editors are a software crop that has flourished even more variously than Java IDEs. They’ve put down healthy roots in every computing platform, so they have no need to concentrate resources on a base application in Swing, SWT, or any other cross-platform interface kit. Instead, general-purpose text editors can concentrate on using underlying platforms to their greatest potential.

In choosing an editor, you should look for one that stands at the top of the pile for whatever computing platform you prefer. Don’t fall for the geek-snobbery that demands that everyone who wants to be taken seriously must use one of two legendary editors designed for text consoles. Either you’re already one of those people running a Unix-like OS with Ratpoison, or you’re in the large majority of the population that appreciates a well-designed graphical interface. Click with pride.

Other than platform integration, the most important quality in an editor is customizability. At a minimum, you should be able to edit the bindings of command keys to internal macros and external shell scripts. Most editors allow the definition of syntax coloring for any programming language expressed in text. Because of this easy expandability, support for languages and markups can be added by amateurs; scriptable editors reach into leading-edge and niche technologies faster than IDEs, and without all the crashing that comes from alpha plug-ins running at application scope.

TextMate is the editor I use in the example here, but it certainly isn’t the only good choice; the Mac OS X platform alone has at least a half dozen top-tier editors.

Back to building

You’ve opened your project folder in a text editor and are looking at some syntax-colored Java source file. Now what?

Look around for ways your text editor has improved the ancient art of text editing that IDEs, in a rush to pile on glitzy features, may have overlooked. For example, TextMate has the ability to set soft tabs, with spaces that act like tabs as you move the cursor through or delete them; that subtle touch quietly ends the tabs versus spaces debate by giving you the best of both worlds.

Triggering a compile without leaving the editor is easily accomplished, so long as your project is serious enough to use an external builder. Many editors have some built-in support for running Ant; to use builders not already supported by an editor, it’s time to explore its expandability options.

TextMate comes packaged with a number of bundles. These can be easily edited or created from scratch, copied from one installation to another, and distributed on the Web. Each bundle can have shell scripts bound to command keys, among other things.

The best way to display extensive script output in TextMate, as from a build command, is by HTML. That sounds like it might be difficult, but TextMate makes it easy and worthwhile, turning errors and warnings into links back to the source lines referenced. Consider the command in Listing 3, cobbled together from a few included with the program.

Listing 3. A command to run Buildr from TextMate

. "$TM_SUPPORT_PATH/lib/webpreview.sh"
    html_header "Building "${TM_PROJECT_DIRECTORY##*/}"..."

    buildr  &> >("${TM_RUBY:-ruby}" -rtm_parser 
      -eTextMate.parse_errors)

    html_footer

This outputs a header, then the buildr command piped through an HTML converter supplied by TextMate, and a footer. In Figure 3, it’s bound to command-B for any Scala or Java source file.

Figure 3. Bundling a Buildr command (click for larger image)

The command runs Buildr, a builder that’s compatible with Maven’s local and remote repositories. Buildr is based on Rake, so its build instructions are Ruby scripts that can be directly hacked, not structured XML tied to plug-ins. It can also be a good deal faster than Maven, an important consideration when you’re building frequently from an editor. But because Buildr’s dependence on Ruby and Rubygems might be an unwanted complication for some Java-only projects, the example project distributed with this article includes Buildr and Maven descriptors that do nearly the same things.

Is it HTTPThing or HttpThing?

Using an editor that’s not joined at the hip to a compiler will show how well you really know the libraries you use every day. Unfortunately, the deck is stacked against you. The proliferation of code completion has allowed some libraries to give in to baroque tendencies, burying nearly every class five packages deep and naming it with an entire sentence of camel-cased words. Java code has mutated from plain text into something that nearly requires machine assistance to edit.

This doesn’t just limit options for editing verbose libraries; it makes coding in a creative flow quite impossible. Instead of translating mental logic into computer instructions with confidence, a programmer relying on command completion works like a lazy actor constantly calling out for the next line. There is some hope of reversing the trend, but for now using a simple editor will require determined typing and a ready access to API references.

And for those references, the way out of the hole is not so slippery after all. Because Maven repositories can hold not only compiled packages but their sources and Javadocs as well, it’s possible to obtain documentation for a project without regressing to Internet hunting and gathering — that is, when your builder is hackable. (Sorry, Maven.)

Listing 4. Buildr function to acquire library documentation

def doc_tasks(jar_spec)
      artifacts(jar_spec).map do |a|
        doc_a = artifact(a.to_hash.merge({:classifier=>'javadoc'}))
        doc_a.extend SoftFail
        file(_(LIB_DOCS) + '/' + a.to_spec => doc_a) do |task|
          unzip(task.name => doc_a) if File.exist? doc_a.name
        end
      end
    end

The Ruby function for Buildr illustrated in Listing 4 accepts artifact specifications and returns an array of tasks that will, if invoked, download and extract Javadocs paired with those artifacts into the project subdirectory specified by LIB_DOCS. The function could go into a Rakefile, or into another file included by it. Because Buildr’s artifacts method can handle all manner of nested artifact specifications, so can lib_doc_tasks.

The procedure quickly runs into a problem: not all libraries maintain corresponding Javadoc artifacts. Normally, Buildr’s artifact task will abort the build on a download failure, but we don’t want to abort, nor do we want to manually purge an artifact tree of those that don’t have matching Javadocs. The solution, then, is the ad hoc SoftFail module in Listing 5, which overrides the failure method to warn only.

Listing 5. Module to override a Buildr download failure

module SoftFail
      def fail_download(remote_uris)
        puts "Can't find javadoc #{to_spec}n"
      end
    end

From inside a Buildr project, call lib_docs with some specs, and add all the created tasks as prerequisites to the new lib_docs task in Listing 6.

Listing 6. Adding the documentation tasks to a build

doc_tasks(WICKET).each { |t| task :lib_docs => t }

Now you can sit back, go offline, and really get to know those libraries you’ve been using for the past few years.

Contain yourself

Editing and compiling is nice, but the destiny of all software code is execution. And these days, most of it is executed in the name of serving Web pages. Here the road to a simple and flexible toolset turns away from finicky servlet containers and leads to a landscape of server embedding.

If it seems strange to serve Web requests directly from an application, that’s only because the Servlet API has from its inception taken a different approach. But today the HTTP protocol is so fundamental, so much a part of the software zeitgeist, that it makes perfect sense to serve requests from an application — just as we initiate requests internally with a library like HttpClient.

Jetty is a very popular embeddable servlet container. By linking to its JARs (under org.mortbay.jetty at the Maven repo) you can start a server from a main() function in short order, as shown in Listing 7.

Listing 7. Kickoff function for an embedded Jetty server

public static void main(String[] args) throws Exception
    {
      Server server = new Server();
      WebAppContext web = new WebAppContext();
      web.setContextPath("/");
      web.setWar("src/main/webapp");
      server.addHandler(web);
      SelectChannelConnector httpConn = new SelectChannelConnector();
      httpConn.setPort(8080);
      server.setConnectors(new Connector[] { httpConn });
      server.start();
      System.out.println("Ready at http://localhost:8080/");
      server.join();
    }

This could be run from a packaged JAR, but it’s most convenient to run it before packaging from a builder that knows your classpath. To run with buildr myproj:run, add the run task in Listing 8 to a Buildr project.

Listing 8. Buildr task to run an embedded server

task :run => :compile do
      java('example.CalcServer', 
        :classpath => compile.classpath + [compile.target.to_s])
    end

Or, if the exec-maven-plugin is configured as shown in Listing 9, you could run from Maven with mvn exec:java.

Listing 9. Maven configuration for an embedded server

<plugin>
      <groupId>org.codehaus.mojo</groupId>
      <artifactId>exec-maven-plugin</artifactId>
      <configuration>
        <mainClass>example.CalcServer</mainClass>
      </configuration>
    </plugin>

Either way, you’ll want to add shortcuts for these commands to your editor. Embedding a server doesn’t just make it easy to run Web apps in a builder with no plug-ins or other configuration; it also means that such apps can be run inside any IDE as plain Java applications with no container configuration, allowing for the maximum use of running code replacement. Speaking of which, aren’t we going to miss code replacement in a decentralized toolset?

Reboot rebellion

Long have we labored under the regime of the 1.2 minute server bounce. As our development machines get faster, our applications find more elaborate ways to initialize themselves that eat up that advancement. In theory, the startup times are irrelevant, as we’re supposed to have everything figured out long before we try to execute code or even write it. In practice, the application startup delay is a productivity disaster that rewards superficial coding errors with a recess from work, whether it’s wanted or not.

One of the primary advantages of scripting environments is that a state of hobbled development is rarely needed and never glorified. But things in Java are not as bad as they’re made out to be, or are allowed to be. An embedded or plugged-in Jetty server fully supports the HotSwap functionality built in to Java since version 1.4. Unfortunately the usefulness of this approach has been eroded, as a fuller embrace of object-oriented programming in frameworks like Wicket quickly runs up against the limitations of HotSwap. Where adding a method was once a major decision that would require updating UML diagrams and the approval of a lead architect, it’s now something you do for a small UI component whose visibility you want to program. And that shouldn’t mean reloading fifty ORM definitions.

Now it doesn’t. JavaRebel, which can also run inside an IDE, brings better code replacement to a barebones environment than existed before there was such a thing as “inside an IDE.” To use it, you run your application as you normally would, but with some extra parameters. If you find you need to change or add something, just rebuild. JavaRebel is watching the classpath, and will notice the changes and reload classes just before they are next accessed.

To facilitate configuration, set the environment variable JAVA_REBEL to the full path to the single JavaRebel JAR. Our :run task for Buildr can then be generically expanded, as shown in Listing 10.

Listing 10. Buildr task to run through JavaRebel

task :run => :build do
      java('example.MyApp', 
        :classpath => compile.classpath + [compile.target.to_s],
        :java_args => ["-noverify", "-javaagent:$JAVA_REBEL"])
    end

And with Maven, the easiest thing to do is run its whole builder through JavaRebel (as shown in Listing 11), since the exec:java task does not spawn a new VM.

Listing 11. Environment variable to run Maven through JavaRebel

export MAVEN_OPTS="-noverify -javaagent:$JAVA_REBEL"

Using JavaRebel, you can keep an application instance running as you write code and significantly cut down on full application reboots. Try starting this article’s example application with either builder and then changing the form’s addition operator to multiplication, as shown in Figure 4. JavaRebel can update the component even after it’s instantiated, which is the case when submitting a Wicket form that has already been rendered. In many cases that’s sufficient, though naturally testing changes to code that only runs during object construction requires going back through whatever sequence will newly instantiate those objects.

Figure 4. Reloading with the multiplication operator (click for larger image)

Mental arithmetic in a world of calculators

After considerable effort and the exploitation of a variety of tools, some of which cost money to use beyond a demonstration period, you have an environment that is comparable to an IDE for writing Java code. It’s better in a few ways, but falls painfully short in others. Why bother?

Firstly, recall that the modern-day palace of IDE coding is built out of software sand. Most of the time it works great, but if a storm hits it on the wrong day you might have to explain why a crucial, simple fix took hours to code and test. With an external fallback environment, you can just leave your IDE closed if it decides to crash.

Secondly and more interestingly, a decentralized environment supports any underlying technology that the operating system supports. While IDEs struggle to keep up with the one or two alternative JDK languages to which they’ve decided to allocate resources, text editors support them all by default. As it turns out, languages that are less hindered by standards bodies incorporate exciting new features faster than IDEs can wrap them up in GUI packaging.

Consider Scala, a type-safe programming language with many modern features that can interoperate with Java. Despite valiant coding efforts, its Eclipse plug-in is still a ticking time bomb. But it goes without saying that Scala can be written in a plain editor; and, if you like syntax coloring, there are definitions for 19 editors waiting to be used.

Because Scala’s syntax and coding norms tend to be easier on typing fingers than Java’s, name completion is less of a concern. All of the techniques described so far, even JavaRebel class reloading, apply to Scala, as shown in Figure 5.

Figure 5. Decentralized workspace (click for larger image)

Scala isn’t the only alternate language to explore, and ultimately that is the point. With a flexible environment primed and ready to go, changing languages is just a matter of changing file extensions.

Besides, a little more time outside the padded walls of integrated development environments could do all of us some good.

Nathan Hamblen is a career software programmer who codes primarily in Java. In his spare time he works on the Databinder toolkit for Wicket and writes Coderspiel, a programming Weblog.