Friday, July 20, 2007

JRuby runtime initialization and problems with 'require'

I have been battling an issue with JRuby, where I got the following exception when trying to evaluate a ruby script from Java:

`require': no such file to load -- rubygems (LoadError)

I was not using the JSR-223 API (which is only part of Java6 and on), but simply programming directly against the JRuby apis. What I was doing was in the line of this:

public class MyApp {
static String jrubyHome = "C:\\software\\jruby\\jruby-0.9.8";
static {
System.setProperty("jruby.base", jrubyHome);
System.setProperty("jruby.home", jrubyHome);
System.setProperty("jruby.lib", jrubyHome + "\\lib");
System.setProperty("jruby.shell", "cmd.exe");
System.setProperty("jruby.script", "jruby.bat");
}

public static void main(String[] args) throws IOException {
Ruby runtime = Ruby.getDefaultInstance();
runtime.evalScript(...my script here...); // => failed in require of rubygems
}
}

After diving into the sources (I love open-source), I found out that load-path was initialized in LoadService.init and I then traced calls to this method. Seems like this method is called from:
  • The main method of the jruby command-line tool, which also adds some extra paths if given on command-line
  • The IRBApplet start() method
  • The IRBConsole main method
  • A lot of tests
  • From JavaEmbedUtils.initialize, which is what JSR-223 implementations are supposed to use in their implementations
Seems like an important method to remember to call :-)

The problem was fixed when I simply added the line:
        runtime.getLoadService().init(new ArrayList());

But of course, I should simply use JavaEmbedUtils to get my Ruby instance. Lesson learned!

Wednesday, July 18, 2007

What is the best way to read JavaBlogs?

I have been a regular reader og JavaBlogs through a long time now, and I have always been doing it through the "20 most read latest day" daily email. It seemed to work well, as I would "only" have to process 20 links a day from JavaBlogs, and most of the time, they would have some kind of relevance to me.

But, as I am trying to get rid of all my email newsletters and converting everything to rss/atom feeds, I tried the Popular Entries feed for JavaBlogs. But boy, there is a lot of traffic on that feed.

What is your preferred way of reading javablogs?

I am actually thinking of quitting it completely. Most of the stuff appears on DZone anyway.

Wednesday, July 11, 2007

IntelliJ IDEA charset encoding plugin released in v1.0

I have just released my first and very own, IntelliJ IDEA plugin. Hurray! :-) I have named it "encoding-plugin" and you can see it here.

One very annoying thing in IDEA, is that the charset encoding setting is at the IDE-level, and not project-level. When different projects use different encodings for their sources, you will need to:
  1. Remember to set IDE-level charset encoding correct before opening project
  2. If IDE-level setting changed, restart IDEA to make it take effect
  3. Cleanup the project files that became trashed anyway, because someone forgot the above steps and edited a file, using the wrong IDE-level encoding
There are actually lots of issues in IDEA jira about it, and an explanation about why it is so, can be seen here (think about voting for it!).

How does encoding-plugin help?
It provides you with an option to select a charset encoding (UTF-8, ISO-8859-1, ...) but at the project level. It does not though, actually change the charset encoding when opening a project. This is still not possible in IDEA.

What it can do though, is warn you, or someone else opening the project, if the IDE-level charset encoding and the project-level charset encoding does not match at project open.

Installation is easy
It is in the official repository, so you can just fire up your IDEA, go to settings, plugins, and find it in the list. It is under "Misc" category, with the name "EncodingPlugin".

I have tested it with 6.0.1 and up to Selena release build #7041. As this is what I have tested with, I have set the required build number to be at least #5784, which is 6.0.1. If you are using an older version and would like to use the plugin, drop me a note and I will have a look at it.

Usage
While having a project open, go to settings and find the plugin config under the project settings.


Choose your project charset encoding. From here on, when project is opened, it will be checked against IDE-level setting.

Tuesday, July 10, 2007

Sending logs to the server using custom LogTarget in flex

In flex you can use the global trace() method to output debug info from a flash. Unfortunately, these data ends up either in the debugger console, or in a local file, if setup correctly on the client. Most user installations will never get access to the data. When going into production with a flash application, it is nice to be able to see client-side logs at the server-side.

Luckily, this can be nicely done using the flex logging framework.

The Remote Part
The webservice interface I am calling in on, is this:

public interface ClientLog {
void acceptLog(String message);
}

I wont go into detail on that one. To see how to call a webservice from AS3, see my previous post on the subject.

Implementing a new LogTarget
To be able to simply use the existing logging API and still have logs sent to the server, we need to implement a LogTarget, that sends logs to the server side. Simplest thing seems to be extending LineFormattedTarget , and override its internalLog method.

import com.foo.bar.remote.ClientLogService;
import mx.logging.targets.LineFormattedTarget;
import mx.logging.LogEvent;
import mx.logging.LogEventLevel;
import mx.logging.ILogger;
import mx.core.mx_internal;

use namespace mx_internal;

public class ServerLogTarget extends LineFormattedTarget {
private var server : ClientLogService;

public function ServerLogTarget() {
super();

// change the defaults for these
super.fieldSeparator = "##";
super.includeTime = true;
super.includeDate = true;
super.includeCategory = true;
super.includeLevel = true;
super.level = LogEventLevel.ALL;

server = ClientLogService.getInstance();
}

public override function logEvent(event : LogEvent) : void {
// Take care to NOT include mx.*, as code in std lib issues log calls,
// which will call this target, which will send a message to server
// (through rpc operation), which will log to this again, ... until StackOverflowException
var category : String = ILogger(event.target).category;
if (category.indexOf("mx.") != 0) {
super.logEvent(event);
}
}

/**
* Super class calls this method when it has formatted the message
* and wants to send it somewhere.
* The default implementation does nothing.
*/
mx_internal override function internalLog(message:String) :void {
if (server != null) {
server.logToServer(message);
}
}
}

Things to note:
  • I import and use the mx_internal namespace, to be able to override the internalLog method
  • The ClientLogService class is simply a wrapper around my webservice (see previous post about calling a webservice from AS3)
  • The logEvent method is overridden, to check if it is a "mx.*" class logging, to avoid "infinite" recursion
Using the new LogTarget
Only thing you need now, is to configure the flex logging framework with the new target. This includes constructing a target instance, and setting filters on it, for the logs you want on the server.

var logTarget:ServerLogTarget = new ServerLogTarget();
logTarget.filters = ["com.foo.bar.*"];
Log.addTarget(logTarget);

One possible place to execute the above code is in the creationComplete event on the main mxml file.

Using the logger is then as simple as this:

Log.getLogger("com.foo.bar.Bleech").info("Hello, server!");

With intelligent use of the logger names and proper filters, you can easily enable only some logs to go to the server side. All without changing any logging code around in the application.

ActionScript3 singletons

Turns out it is kinda hard to do a singleton in ActionScript3. This is due to the fact that AS3 only allows public constructors, which makes it hard to prevent instantiations.

This dude, and this dude, mention a solution where you use a trick with declaring a class outside package, which is then only accessible from singleton class. Another solution is mentioned here, which looks kinda clean.

Of course, I have my own solution too:
package com.foo.bar {
public class Singleton {
private static var singleton : Singleton = null;
private static var calledFromGetInstance : Boolean = false;

public function Singleton() {
// This little trick is due to AS3 only allowing public constructors
if (!calledFromGetInstance) {
throw new Error("this is a singleton - use getInstance() to obtain the singleton instance")
}
calledFromGetInstance = false; // reset value back
}

public static function getInstance() : Singleton {
if (singleton == null) {
calledFromGetInstance = true;
singleton = new Singleton();
}
return singleton;
}
}
}

My trick is the use of calledFromGetInstance, which is private and static to the singleton type, hence only allowed to be set from within the singleton type. You can still try to create an instance from the outside, but you will be slapped!

Monday, July 09, 2007

Switch Considered Harmful - Advanced enums use instead

Whenever I see a switch statement in object-oriented code, an alarm bell goes of. Most of the time, it will be a code-smell, which should be expressed through the use of polymorphism instead. With Java5 enums, you can get around switch-logic, by adding per enum constant methods.

Consider the code below:

enum LogLevel { ERROR, WARN, INFO }

private void doLog(LogLevel logLevel, String msg) {
String prefix;
switch (logLevel) {
case INFO:
prefix = "INFO";
break;
case WARN:
prefix = "WARN";
break;
case ERROR:
prefix = "ERROR";
break;
default:
throw new RuntimeException("Oops, forgot to add stuff on new enum constant");
}
System.out.println(String.format("%s: %s", prefix, msg));
}

...

// and its usage..
doLog(LogLevel.WARN, "Dude, where's my car?");
The example is dumb, I know, but it illustrates how the switch is ugly. If we add a new enum constant like DEBUG to LogLevel, we would have to remember to update all places where the enum is switched upon.

Here is a better solution:

enum LogLevel {
ERROR {
void log(String msg) {
doLog("ERROR", msg);
}
},
WARN {
void log(String msg) {
doLog("WARN", msg);
}
},
INFO {
void log(String msg) {
doLog("INFO", msg);
}
};

abstract void log(String msg);

private static void doLog(String prefix, String msg) {
System.out.println(String.format("%s: %s", prefix, msg));
}
}

...

// and its usage..
LogLevel.WARN.log("Dude, where's my car?");
Here, the "log logic" is put onto each enum constant itself and we dispatch to that logic by calling the log method on the enum variable directly. If we were to add DEBUG to LogLevel, we would be forced to implement the abstract log method for that constant too.

Friday, July 06, 2007

Online storage services

I am going to setup automatic backup of an application I have hosted at an ISP. I was thinking about simply buying some remote disk storage somewhere, and periodically doing a simple scp or rsync of data.

I asked around and took a quick sweep on the net, which gave me this list:

OmniDrive
Requires either a special client (available for windows and mac only) or use of a simple web api. There seems to be no ssh-based api.

OmniDrive currently provides a 1GB free account.

Mozy
Same as with OmniDrive that it requires a special client to be downloaded and installed. Their client seems to have focus as a kind of backup application, in that it actually has features like backup in the background, and only transfer of modified blocks in files. In addition, you actually need to use mozyPro if you are a company.

Mozy currently provides a 2GB free account.

Strongspace
Now here is something in the lines of what I need. Their api is ssh-based, accepting sftp and rsync over ssh, which is just what I want. Aside from their apparent integration with basecamp, their focus seems to be providing a remote-disk, which is nice and simple.

Strongspace currently provides no free or trial account.

Xdrive
From what I can read, their focus is not as simple as strongspace, but I must admit that some of their features are cool. For instance, you can provide your email account information in their client, and xdrive will automatically strip off attachments from your emails and save them in your xdrive space. Real nice feature, but not at all what I need now :-)

Xdrive is an AOL service, so you need an AOL screen name. I cannot find any information about ssh-based uploads, so I assume it is not there. Hmmm.

Amazon S3
They call it "Storage for the Internet". Sadly, they seem to only provide REST and SOAP based apis (which is fine, just not for me right now). In addition to this, they have a pricing model based on both per GB storage used and per GB data transferred in and out. I like a more flat rate price model.

So, I think I will go with strongspace, as it looks like a ... strong solution. Would like to hear from anyone with actual experience in this field though!?

Monday, July 02, 2007

Flash for Java Programmers: Lesson 5 - Calling an xfire webservice from a flash client

Steps on learning to develop Flash, with a Java developer focus...

In this post, I will show how to call a webservice from a flash client. The webservice will be exposed using xfire and the flash client will connect to the wsdl and call methods on the web service endpoint.

This is lesson 5 in my series of posts on what I learn about developing filthy rich flash apps using flex2. The other lessons are:

An xfire based webservice
Here is how to construct a simple web service, exposed through xfire using the JSR-181 annotations. It is dead simple. My service is called "ClientLogService" and can be used to send a log message from the flash client to the server for logging. Here is the interface and the implementation:
public interface ClientLog {
void acceptLog(String message);
}
and
@WebService(serviceName = "ClientLogService")
public class ClientLogImpl implements ClientLog {
public void acceptLog(@WebParam(name = "message") String message) {
System.out.println(String.format("[LOG-MSG-FROM-FLASH] %s", message));
}
}
Notice the annotations. These are what xfire needs to expose it as a web service. I also need to package a META-INF/xfire/services.xml with this content:
<beans xmlns="http://xfire.codehaus.org/config/1.0">
<service>
<name>ClientLogService</name>
<namespace>http://techpolesen.blogspot.com/ClientLogService</namespace>
<serviceClass>com.blogspot.techpolesen.ws.ClientLog</serviceClass>
<implementationClass>com.blogspot.techpolesen.ws.ClientLogImpl</implementationClass>
</service>
</beans>
And lastly, add the xfire servlet definition to the web.xml:
<servlet>
<servlet-name>XFire</servlet-name>
<servlet-class>org.codehaus.xfire.transport.http.XFireConfigurableServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>XFire</servlet-name>
<url-pattern>/services/*</url-pattern>
</servlet-mapping>
That was the server side of it. Next comes the flex implementation accessing the web service.

Flex calling web service using ActionScript
Flex comes with RPC components for doing HTTP and WebService calls. You can actually use them by using only "mx:" xml tags, but constructing and controlling them completely from ActionScript gives you the full power.

Here I show a simple mxml flex2 file, where the UI consists of two components: An input field where simple text can be entered and a button, which triggers the web service call with the text input as parameter.

There is a Script element which contains a variable declaration clientLogService which is of type WebService, and two methods:
  • loadWebService() : Initializes the clientLogService variable by pointing it to the WSDL document published by xfire. Then, when calling clientLogService.loadWSDL(), flex will load and parse the wsdl document, and add dynamic methods to the clientLogService variable, corresponding to the methods published by the web service.
  • callWebService() : Simply takes the value of the input field and uses it as parameter when sending a method call to the web service.
The loadWebService() method is set to be called on the creationComplete event, which will be when the flash application starts up.
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" creationComplete="loadWebService()">
<mx:Script>
<![CDATA[
import mx.rpc.soap.WebService;
import mx.rpc.events.FaultEvent;
import mx.controls.Alert;

private var clientLogService : WebService = new WebService();

private function loadWebService() : void {
clientLogService = new WebService();
clientLogService.wsdl = "/services/ClientLogService?wsdl";
clientLogService.loadWSDL();

clientLogService.addEventListener("fault", function(event:FaultEvent) : void {
Alert.show(event.fault.faultString);
});
}

private function callWebService() : void {
clientLogService.acceptLog(message.text);
}
]]>
</mx:Script>

<mx:TextInput id="message" text="Text entered here will be logged on server..."/>
<mx:Button label="Send message" click="callWebService()"/>
</mx:Application>
Notice one important thing here: RPC calls are asynchronous in flash! This is why we register for a "fault" event on the web service. If something goes wrong, we will be called back on this method, asynchronously. Actually, we wont know after the call to clientLogService.acceptLog() method, if the call has succeeded. We will need to register for the "result" event to be sure, which is also how to get at any return values from web service calls.

Downloading the source
As usual, I have zipped up the sources for you. It can be downloaded from here. Ready to be build with maven.

This is a multi-module maven build. There are two directories:
  • client : Contains the flex source and a pom to build it
  • server : Contains the web service and a pom to build it
The war artifact output from the server module have in it the flash output from the client module and a index.html which loads it.

To start the server after a build, you simply jump into the server directory and do a "mvn jetty:run-exploded".