Saturday, September 27, 2008

I am lazy!

It is annoying to feel compelled to start every post with "Wow... It has been a long time since I've posted something here". Of course one answer is "So don't". I'd rather think that some people would prefer to say "So post more". I am known for having an occasional delusional moment here and there, though ;-)

I believe one of the reasons that I don't update this blog as often as I wanted to, besides being insanely busy, is that, bottom line, I am a lazy person. Fortunately, a characteristic that would put me out of any work back 50 years ago, is actually something that should be rewarded on the software development world. Let me illustrate this with an example.

When I am heads down, working 15+ hours per day to develop an entire product or a complicated feature, I get extremely paranoid (way more than normal) about introducing regressions by entering a, or better, any new line of code. And testing everything all the time is too tiring (I started by saying that I am lazy, remember? ;-). So I usually choose to automate as many tests I can. Sure I have to invest some time putting together an automation strategy (usually based on JUnit). But after that, I just need to click a button and watch the JUnit progress bar go while I exercise the divine right of not doing anything for a few minutes - OK, I am lying: usually I am working on another portion of the code while the tests are running.

On my last endeavor, for example, testing the code consisted on executing 11 Java examples that invoke an API written to identify security issues on web applications by looking at their code and by interacting with them as a hacker would do. Each one of these Java applications run for about 2 to 10 minutes and produces a long log that has to be verified. After doing this by hand 3 times, my laziness kicked in and forced me to find an easier way to test my code. After one or two hours of JUnit fun, I had a class that, for a test method like the one below, executes the XYZ.class on a separate VM, collects the generated log, and compares it with a baseline I know is correct.

@Test
public void codeCoverageExample() throws Exception {
runExampleAndCompareOutput(XYZ.class);
}

The comparison part was a bit tricky to implement because the logs have things like timestamps and temporary file paths that change from execution to execution. With some creativity and several regular expressions, I managed to circumvent all the issues and, like a famous black-turtle-neck-guy says, "BOOM" : the annoying tests were out of my way ;-)

I will write at least one more post pointing out cases in which being lazy was extremely valuable. I am already thinking about EMF related examples...

Actually, on a minor digression, is my passion for EMF the ultimate proof that I am lazy? After all, it does generate a bunch of the code that I would need to code by hand ;-)

Wednesday, June 18, 2008

"Ch-ch-ch-ch-Changes" and "I am still around!"

I couldn't really decide what would be the best title for this post. So I chose to use both ideas that I had in mind. Fortunately, as this title, there are several opportunities in which we can actually choose all the options that are presented to us. For example, three years ago, I've chosen to be both Brazilian and Canadian. More recently, I've also chosen to change a bit my professional life and do some product work while keeping my duties as an EMF committer.

After four years dedicating myself exclusively to EMF, I am now working on the Rational AppScan Developer Edition team (our beta is available here). A minor digression, the security-related technology behind this product is quite amazing. I really don't want to sound like a salesman, though. I know that it wouldn't go really well given my "amazing" commercial skills ;-)

The good news is that both Ed and my new manager (Jeff Turnham) have been extremely supportive in my decision of work both internally and on open source. Hopefully most of you agree with their attitude. I certainly do ;-)

To me it will be a bliss to employ the technology that I help build. Although the EMF team has always eaten its own dog food caviar ;-), it is quite different to face the problems that consumers of our code have to deal with. On the other hand, I am really expecting that the product experience will make me a better EMF developer. EMF has been part of my life for quite a while and it certainly deserves the best I can be.

Thursday, March 20, 2008

Platform scheme URI

Wow... It has been more than six months since the last time I took a few minutes to write something here. Things have been a bit crazy lately. A lot has kept me occupied, like, for example, writing the 2nd version of the EMF book (which is "already" available as a rough cut), working on a few bugzillas, and supporting the EMF community. On a more personal note, in October we moved to our new house right when our son was entering the "terrible twos" days. Actually, since then, my wife and I have been working as maniacs to finish the basement and prepare the kids' rooms for the arrival of the new member of the Paternostro family, who is supposed to be here by the end of April.

But I am sure you are not reading this to hear about what I've been doing ;-) So, let's move on to the subject of this post. Quite frequently people ask me how to "locate" files in Eclipse. In 80% of the cases, the conversation is about things like IFile and IWorkspace. Sometimes it digresses to files available inside bundles and, rarely, it involves the "state location" of a bundle (see the Plugin.getStateLocation() javadoc for more details). Obviously you can use IResources or java.io.File to work with files in these places. There is another way, though: platform URI. Although this scheme has been around since the beginning of time, I decided to ramble about it here because I've never seem its uses described in one single place.

There are a few ways to work with the "platform" scheme:

platform:/resourceIt is used to identify a resource located in the workspace. The next path segment after "resource" should be the name of a project, which can be followed by the folder and/or file we want to locate.
platform:/pluginIt is used to locate a resource available in a plug-in (I know, I know, bundle). One really cool thing about this one is that it doesn't really matter if this resource is available in a directory or in a jar file. It also doesn't matter if the bundle is installed in a link folder or in the default directory.

The path segment after "plugin" should be the identifier of the bundle, which can be followed by the path of the resource in the bundle.
platform:/fragmentThis one is quite similar to "platform:/plugin", being to used to locate fragment resources instead of bundle resources. As you are probably guessing, the segment after "fragment" should be the fragment's identifier.
platform:/metaWe can use this to access a bundle's stage location. The path segment after "meta" should be the bundle's identifier, followed by the path of the resource we want to refer to.
platform:/configThe "config" segment causes the platform URI to refer to the configuration area of the running Eclipse (usually the eclipse/configuration directory). This can be useful to read the config.ini file, for example.
platform:/baseThis always refers to the directory of the Eclipse being executed.

It is interesting to note that, for example, platform:/base/plugins/org.eclipse.emf/plugin.xml and platform:/plugin/org.eclipse.emf/plugin.xml don't necessarily refer to the same resource. The former is a "pointer" to a plugin.xml file located in a directory plugins/org.eclipse.emf under the directory that Eclipse is installed. The latter points to the plugin.xml of the "org.eclipse.emf.ecore" bundle regardless of where it is installed and whether it is jarred or not.

For the URI-savvy people, you should see "resource", "plugin", "fragment", "meta", "config", and "base" as authorities. Perhaps they could become authorities in e4 (yey! Now this is an e4 related post ;-)

Since we all like actual code...
IProject project = 
ResourcesPlugin.getWorkspace().getRoot().getProject("myproject");
if (!project.exists())
{
project.create(new NullProgressMonitor());
}

System.out.println("\n==== platform:/resource ====");
{
URI uri = URI.createPlatformResourceURI("myproject", true);
System.out.println(uri);
System.out.println(CommonPlugin.resolve(uri));

uri = uri.appendSegments(new String[]{"folder", "file.txt"});
System.out.println(uri);
System.out.println(CommonPlugin.resolve(uri));
}

System.out.println("\n==== platform:/plugin ====");
{
// Just for fun, choose a bundle that is not in the default location
URI uri = URI.createPlatformPluginURI("org.eclipse.emf.ecore", true);
System.out.println(uri);
System.out.println(CommonPlugin.resolve(uri));

uri = uri.appendSegments(new String[]{"model", "Ecore.ecore"});
System.out.println(uri);
System.out.println(CommonPlugin.resolve(uri));

// Choose a bundle that is in the default location (<eclipse-dir>/plugins)
uri = URI.createPlatformPluginURI("org.eclipse.core.resources", true);
System.out.println(uri);
System.out.println(CommonPlugin.resolve(uri));

uri = uri.appendSegments(new String[]{"META-INF", "MANIFEST.MF"});
System.out.println(uri);
System.out.println(CommonPlugin.resolve(uri));
}

System.out.println("\n==== platform:/fragment ====");
{
URI uri = URI.createURI("platform:/fragment/org.eclipse.swt");
System.out.println(uri);
System.out.println(CommonPlugin.resolve(uri));

uri = uri.appendSegments(new String[]{"META-INF", "MANIFEST.MF"});
System.out.println(uri);
System.out.println(CommonPlugin.resolve(uri));
}

System.out.println("\n==== platform:/config ====");
{
URI uri = URI.createURI("platform:/config/");
System.out.println(uri);
System.out.println(CommonPlugin.resolve(uri));
}

System.out.println("\n==== platform:/base ====");
{
URI uri = URI.createURI("platform:/base/");
System.out.println(uri);
System.out.println(CommonPlugin.resolve(uri));
}
The call to CommonPlugin.resolve(URI) returns a URI that uses a protocol which is native to the Java class library (file, jar, http, etc).

I will leave it to you to run this code and see the results :-P Don't forget that it must be executed in an Eclipse shell. Probably the simplest way to do so is to paste these lines into a JUnit test located in a bundle and execute it as a "JUnit Plug-in Test".

So what can we do with platform URIs? For one, read the contents of the resources pointed by them. We may also be able to write to such resources or even delete or create them. A tip for EMF 2.4 users: URIConverter.INSTANCE allows easy access to methods that are extremely handy when dealing with URIs (createOutputStream(URI), createInputStream(URI), delete(URI), and exists(URI)).

Personally I consider URIs a good fit for APIs that would normally use "plain" paths. Take as an example the icon attribute of the extension point below. Because its value is handled as a URI, we are allowed to refer to an image located in a different bundle.
<extension point="org.eclipse.ui.editorActions">
<editorContribution ...>
<action
icon="platform:/plugin/com.myplugin/icons/me.gif"
...
/>
</editorContribution>
</extension>
BTW, if I were to implement the code to process such an extension point, I would probably do something like this:
IConfigurationElement configurationElement = ...
URI iconURI = URI.createURI(configurationElement.getAttribute("icon"));
if (iconURI.isRelative())
{
URI pluginURI =
URI.createPlatformPluginURI(
configurationElement.getContributor().getName() + "/", true);
iconURI = iconURI.resolve(pluginURI);
}

try
{
ImageDescriptor imageDescriptor =
ImageDescriptor.createFromURL(new URL(iconURI.toString()));
descriptorImpl.setIcon(imageDescriptor.createImage());
}
catch (Exception e)
{
e.printStackTrace();
}
This code assumes that when the value of the icon attribute is a relative URI (like icon/me.gif for example), the developer is indicating that the image is contained in the bundle that uses the extension point.

For obvious reasons, I wrote these examples using EMF APIs, including our URI class. It shouldn't be terribly difficult to rewrite the code in this post to use basic Eclipse and Java code (like, java.net.URI).