Sunday, October 16, 2011

NetBeans project for Play - fixing the source path

If you used NetBeans with Play framework, you could notice one feature, missing from Eclipse counterpart. In NetBeans "Navigate | Go to source" does not show the internals of Play framework, while in Eclipse the sources are readily available. So I decided to fix the command which creates NetBeans project.

The commands of Play framework are located in framework/pym/play/commands, they are good old python scripts. The one responsible for netbeansify command is in file netbeans.py. Looking at the sources, I immediately spotted a useful shortcut: intstead of play netbeansify we can run play nb, I like that! :) Eclipse fans, consider switching, our command is shorter (just kidding, eclipsify has a shortcut, too)

Anyway, to fix the problem with "navigate to sources" I replaced the following lines:

    if os.name == 'nt':
        replaceAll(os.path.join(nbproject, 'project.xml'), r'%PLAY_CLASSPATH%', ';'.join(classpath + ['nbproject\\classes']))
    else:
        replaceAll(os.path.join(nbproject, 'project.xml'), r'%PLAY_CLASSPATH%', ':'.join(classpath + ['nbproject/classes']))

to these lines:

    newClasspath = []
    playJar = 'framework%splay-%s.jar' % (os.sep, play_env['version'])
    [newClasspath.append(x) for x in classpath if not x.endswith(playJar)]
    replaceAll(os.path.join(nbproject, 'project.xml'), r'%PLAY_CLASSPATH%', ';'.join(newClasspath + ['nbproject%sclasses'%os.sep, '%s%sframework%ssrc'%(play_env["basedir"], os.sep, os.sep)]))

If you already have NetBeans project for your Play application, you need to generate it again, and reopen the project in NetBeans, to see the changes.

I sent the fix to the Play framework developers, hope they accept it and you will not need to patch the script very soon.

Crossposted to Tikal Community site

Monday, September 19, 2011

UTF8 in OSX terminal

After 3.5 years of using MacBook Pro I could not stand ???? symbols in OSX terminal anymore. Funny thing, the trigger was an example of Union types for Scala, which used some mathematical symbols in the identifier names. So I looked for a solution, and it was so easy, I could not believe I did not do this earlier.

  1. Open /Developer/Applications/Utilities/Property List Editor. I suppose, it was installed together with developer tools from the OSX installation disk.
  2. Select File | Open in menu and open or create file environment.plist in .MacOSX directory in your home dir.
  3. Add key LANG with type String and value en_US.UTF-8
  4. Add key JAVA_OPTS with value -Dfile.encoding=UTF-8
  5. Save everything, log out and back into your account.

After doing that, I was able to use UTF-8 in Terminal. So easy!

P.S. Many kudos to blogger.com for autosaving the draft of this post. When I logged out to check this solution, I was sure the text I entered was lost. But when I opened Chrome, all the text I entered was still here. Nice!

Sunday, September 18, 2011

Node.js plugin for NetBeans and daemons

Today I tried NodeJS plugin for NetBeans by Syntea software group. It allows to run a node straight from the NetBeans. I took an HTTP variant of "Hello, world" sample, which listens to port 8000, and responses with greetings to every request after a short delay. NetBeans stopped responding. So I downloaded the sources of this plugin to see how to fix it.

First of all, this is the source of my "Hello, world":
var sys = require('sys'),
    http = require('http');

    http.createServer(function (req, res) {
        setTimeout(function () {
            sys.puts('requested '+req.url)
            res.writeHead(200, {'Content-Type': 'text/plain'});
            res.end('Hello World\n');
        }, 100);
    }).listen(8000);

sys.puts('Server running at http://127.0.0.1:8000/');
So to narrow the problem, I commented out the http.createServer block, so this program only lies about listening, and exits immediately. This time, NetBeans did not stuck, ran the code as expected, and printed the "Server running..." in the output window. Same happens if I do start listening, make a few requests, and then kill the node with killall node. But the output 'requested /' and 'requested /favicon.ico' which were supposed to be printed during requests, were all printed after I killed the Node.js server.

So I started to suspect they run the server synchronously, and was right. Here is a snippet from the sources of the plugin, method cz.kec.nb.nodejs.RunNode.performAction:

            Process proc = Runtime.getRuntime().exec(cmd);
            proc.waitFor();                                           //    <------ NetBeans gets stuck here   !!!
            BufferedReader read = new BufferedReader(new InputStreamReader(proc.getInputStream()));
            BufferedReader readerr = new BufferedReader(new InputStreamReader(proc.getErrorStream()));
            while (read.ready()) {
                out.println(read.readLine());
            }
            while (readerr.ready()) {

                printErrLine(readerr.readLine(), io);
                //erout.println(readerr.readLine());
            }
Unfortunately, Java designers did not provide a way to check the process status without blocking, you can only wait until process ends. So we have to waste a thread on this proc.waitFor. Really annoying and wasteful.

BTW, I looked at the implementation class, package private java.lang.UNIXProcess, and it has private hasExited field. Many thanks to whoever decided to make this unaccessible for the rest of us, all the wasted threads will torture you for eternity, when the time comes! Mwahaha!

Anyway, in addition to this thread, necessary because of a lame API of java.lang.Process, another thread is necessary for reading and printing whatever Node.js application wants to print. I'm pretty sure, that's not a correct way for a NetBeans plugin to deal with long-running tasks, and will be glad if someone shows me a correct way. But it worked for me :-). So, after my changes the code looked like this:

            final Process proc = Runtime.getRuntime().exec(cmd);
            final BufferedReader read = new BufferedReader(new InputStreamReader(proc.getInputStream()));
            final BufferedReader readerr = new BufferedReader(new InputStreamReader(proc.getErrorStream()));
            final AtomicBoolean done = new AtomicBoolean(false);
            new Thread(new Runnable() {
                public void run() {
                    try {
                        while (!done.get()) {
                            while (read.ready()) {
                                out.println(read.readLine());
                            }
                            while (readerr.ready()) {
                                printErrLine(readerr.readLine(), io);
                                //erout.println(readerr.readLine());
                            }
                            Thread.sleep(500);
                        }
                        
                        read.close();
                        readerr.close();
                    
                    } catch (Exception ex) {
                        Exceptions.printStackTrace(ex);
                    }
                }
            }).start();
            new Thread(new Runnable() {
                public void run() {
                    try {
                        proc.waitFor();
                    } catch (InterruptedException ex) {
                        Exceptions.printStackTrace(ex);
                    }
                    finally {
                        done.set(true);
                    }
                }
            }).start();
Cross-posted to Tikal community site