Tuesday, June 17, 2008

Groovy script for building maven dependencies based on information from OSGI bundle

Sometimes when you work with OSGI container outside of Exlipse, you want to have access to dependencies for a given bundle. This information is hidden within the bundle in "META-INF/MANIFEST.MF" file. By analyzing "Bundle-Version", "Import-Package", "Export-Package" attributes we can understand which dependencies are required.

Unfortunately, this work is tedious and here we'll look for the way how to make it easier. I wrote groovy script that takes OSGI bundle and eclipse home as input parameters and produces maven dependencies (with "system" scope) section. It could be used along with "maven-ant-tasks" library for "ant" scripts or, after small modification with ant directly.

Wednesday, February 27, 2008

Announcement: New release of Scriptlandia (ver. 2.2.4)

Scriptlandia is the effort to build scripting command line environment on top of JVM. The user don't have to worry how to install or configure libraries for different scripting languages. It will be done automatically, partially at installation time and partially at execution time.

This release includes:

1. Support for latest versions of languages/tools: Scala, Jython, Pnuts, Groovy, JRuby, Sleep, Scriptella, Fortress, Freemarker, Ivy, Maven.

2. Added support for new languages: Clojure, P~, YOIX, GANT (Groovy Ant);

3. Integration with JLaunchPad 1.0.1 (Added support for proxy with authenication);

4. Removing dependency on JDIC library - replacing it with builtin support from "deploy.jar";

5. Other bug fixes/improvements.

Tuesday, February 26, 2008

Announcement : New version of JLaunchPad 1.0.1 - universal java launcher

This program tries to resolve the following challenges:

1. It gets rid of complex starting scripts to run JVM with different parameters (memory optimizers, classpath,
bootclasspath, native library path, system properties etc.). The idea is to have one universal script that fits all possible scenarios. The part that is different from one project to another is moved to configuration file. JLaunchPad provides ready to use scripts both for Windows and Unix environments that can be used by any Java program. If you need to customize the process of starting JVM, you don't modify these scripts. Instead, you make modifications in the configuration file.

For example, if you want to run your program that requires native library (*.dll), your configuration will
look something like this:


# my_prg.jlcfg
# my configuration

<java.library.path>
_path_to_your_dll_library_

<jvm.args>
-Xms256m
-Xmx512m

<launcher.class>
_your_fully_qualified_class_name_

<command.line.args>
_param1_
_param2_


You can use the following sections within your configuration file:

- "java.classpath";
- "java.endorsed.dirs";
- "java.ext.dirs";
- "java.library.path";
- "java.system.props";
- "java.bootclasspath";
- "java.bootclasspath.prepend";
- "java.bootclasspath.append";
- "jvm.args";
- "launcher.class";
- "set.variables";
- "command.line.args".

It seems that for projects with simple start scripts it's not worth using it. But JLaunchPad
offers some other features that could be useful for your project.

This part of the functionality is based (with some modification) on Java App Launcher project.

2. It downloads all dependent libraries (if necessary) from remote server and installs them locally. You don't have to install libraries manually. Once you declare the dependency, it will be downloaded and installed automatically.

It requires to have all used libraries located on remote server. It's true for all
installations that exist in WWW, but we have to organize these installations a little bit more. To eliminate infamous jar hell we want to introduce separation based on group name and version. Closest candidate that could make this organization happen is maven repository.

We have to make these changes in configuration to make it happen:


# my_prg.jlcfg
# my configuration

<launcher.class>
_special_launcher_class_

<command.line.args>
-deps.file.name=_my_deps_file_.xml


Remote maven repositories are used as the storage for libraries and maven project file is used for describing dependencies. As you can see, we try to reuse maven abilities as much as possible.

This part of functionality becomes available thanks to "bootstrap-mini" project that is
the part of maven2 source.
It provides file delivery mechanism, independent from maven2 standard delivery mechanism.

3. It builds CLASSPATH dynamically based on declared dependencies. You can put all the dependencies for your project in dependencies file. Current implementation
uses maven2 pom.xml file for describing dependencies. As an example, if you want to run JRuby engine, you need to have these files:

a). JLaunchPad configuration:


# my_prg.jlcfg
# my configuration

<launcher.class>
_special_launcher_class_

<command.line.args>
-deps.file.name=_jruby_deps_file_.xml
-main.class.name=org.jruby.Main


b). JRuby dependencies file


<!-- _jruby_deps_file_.xml -->

<project>
<modelVersion>4.0.0</modelVersion>
<groupId>org.sf.scriptlandia</groupId>
<artifactId>jruby-starter</artifactId>
<version>1.0</version>
<packaging>pom</packaging>

<name>JRuby Starter</name>
<description>JRuby Starter</description>

<dependencies>
<dependency>
<groupId>jruby</groupId>
<artifactId>jruby</artifactId>
<version>1.0.2</version>
</dependency>
</dependencies>
</project>


This functionality is possible thanks to classworlds project.

JLaunchPad's main responsibility is to integrate these 3 different approaches into one powerful solution.
Classworlds takes care of classloader management. We read dependencies files and inform classworlds about new libraries that should be added to CLASSPATH. To accomplish it, "_special_launcher_class_" should be substituted with "org.codehaus.classworlds.Launcher" class.
We only need to tell classworlds which class to run: "org.sf.jlaunchpad.JLaunchPadLauncher". This class is main integration point that performs main preparation work: it reads dependencies file, download them if required and add them dynamically to CLASSPATH. Then it tries to execute java class declared with "main.class.name" parameter.


Changes for current release:

- added proxy with authentication;

- storing configuration parameters (proxy, local repository location, remote repositories location)
in settings.xml file;

- added new examples;

- changes in installation program layout;

- other changes and improvements.

Sunday, February 24, 2008

New Free Service from Google - WebCall Button

If you have a blog on Google's www.blogspot.com site, you can benefit from new exciting service - WebCall Button. What does it mean?

Simply by putting this button on your web site (I don't think it's restricted with your blog only), you will get one "virtual" phone number that could redirect calls to your home, cell or work phone numbers. Or to voice mail. And sender shouldn't know your phone number at all.

Or imagine another situation. You don't want to share your home, cell and work phones. Instead, you expose your "virtual" phone number to outside world and this service will redirect all incoming calls to home, cell, work phones (or to voicemail).

You also could publish separate voice mail on your blog/web-site as embedded object.

In order to use this service you have to register your account at Grand Central. Now it's part of Google.

You can try this service by calling my home number right from my blog page at Scriptlandia blog.

Monday, February 11, 2008

Maven Archetypes as standalone program

Initial Release.

This project helps to generate initial Java project layout with the help of Maven Archetypes.

It is not required to have Maven 2 preinstalled - instead it uses JLaunchPad (as part of the installation). It means that required by Maven and Maven Archetypes Plugin parts will be downloaded and installed into your local repository automatically.

All you need to do is to specify the location of Java and the location of Maven repository. If you are working behind the firewall, you have to specify proxy parameters too.

Saturday, February 09, 2008

Tip: How to use Intellij IDEA and Maven 2 together for debugging and context help

Latest version of Intellij IDEA has excellent integration with Maven 2. I want to explain one tip that could help you to gain productivity.

When you debug with IDEA, it's nice to have sources and javadocs for each used library registered with IDEA. If you have registered additional sources, you can debug deeper, going inside those sources. If you have registered additional javadocs, you can get context help for used classes from that libraries.

Unfortunately, this process is manual and tedious. Usually you have to do the following actions:

1. Find out on Internet sources for given library and unzip them locally.


2. Find out on Internet javadocs for given library and unzip it locally.


3. With the help of Intellij IDEA you have to register unzipped sources and javadocs.

This should be done for each external library in the project. If you have a lot of dependencies,
it'll take a lot of time.

Even if your project is dependent on other your own libraries, you still won't get sources and context helps
for them automatically. To have this support, you will do same things as what you did for external libraries -
IDEA should know where your dependent sources/javadocs are located.

Is it possible to make this process less time consuming, less painful? And the answer is: Yes.

When your project is equipped with maven's pom.xml file, you can do the following trick. Close your current
project and next time, when you try to open this project again, use pom.xml file as the project file (use "Open Project" functionality) instead of regular IDEA project file.

What happens here, IDEA is trying to resolve dependencies, defined inside maven project file and populates them back into IDEA's project (synch up). After such synchronization, IDEA is looking for sources and javadocs inside maven reopsitory. They should be located in same folder as your jar file and should have name built by special scheme. For example, my example library is located here:


${maven.local.repository}/org/google/code/maven-archetypes/1.0.0/maven-archetypes-1.0.0.jar


My sources and javadocs should be layed out in this way


${maven.local.repository}/org/google/code/maven-archetypes/1.0.0/maven-archetypes-1.0.0-sources.jar
${maven.local.repository}/org/google/code/maven-archetypes/1.0.0/maven-archetypes-1.0.0-javadoc.jar


As you can see, maven has "classifier" notion that helps us to differentiate "jar" artifact from "sources" and
"javadocs" artifacts.

Good thing is that most of new artifacts to be deployed recently into maven repositories (or at least everybody who understand it and wants to benefit from it), follow this pattern.

Now, what we have to do for our project components to be "in synch" with this pattern? Follow these steps:

1. Generate sources and javadocs with the help of "maven-source-plugin" and "maven-javadoc-plugin"
plugins:


>mvn source:jar
                                                                              
>mvn javadoc:jar


After the execution of these commands, 2 new files will be created. By default, you will see these files in "target" folder:


maven-archetypes-1.0.0-javadoc.jar
maven-archetypes-1.0.0-sources.jar


2. Install sources into maven repository:


mvn install:install-file ^
  -Dfile=target/yourArtifactId-yourVersion-sources.jar ^
  -DgroupId=yourGroupId ^
  -DartifactId=yourArtifactId ^
  -Dversion=yourVersion ^
  -Dpackaging=jar ^
  -Dclassifier=sources ^
  -DgeneratePom=false


3. Install javadocs into maven repository:


mvn install:install-file ^
  -Dfile=target/yourArtifactId-yourVersion-javadoc.jar ^
  -DgroupId=yourGroupId ^
  -DartifactId=yourArtifactId ^
  -Dversion=yourVersion ^
  -Dpackaging=jar ^
  -Dclassifier=javadoc ^
  -DgeneratePom=false


You can use this batch script to do all commands in one step:


rem install-jar-javadoc-sources.bat

rem 1. generate sources jar file

call mvn source:jar

rem 2. generate javadoc jar file

call mvn javadoc:jar

SET GROUP_ID=org.google.code
SET ARTIFACT_ID=maven-archetypes
SET VERSION=1.0.0

SET SOURCES_CLASSIFIER=sources
SET JAVADOC_CLASSIFIER=javadoc

rem 3. install sources jar file

call mvn install:install-file ^
  "-Dfile=target/%ARTIFACT_ID%-%VERSION%-%SOURCES_CLASSIFIER%.jar" ^
  "-DgroupId=%GROUP_ID%" ^
  "-DartifactId=%ARTIFACT_ID%" ^
  "-Dversion=%VERSION%" ^
  "-Dpackaging=jar" ^
  "-Dclassifier=%SOURCES_CLASSIFIER%" ^
  "-DgeneratePom=false"

rem 4. install javadoc jar file

call mvn install:install-file ^
  "-Dfile=target/%ARTIFACT_ID%-%VERSION%-%JAVADOC_CLASSIFIER%.jar" ^
  "-DgroupId=%GROUP_ID%" ^
  "-DartifactId=%ARTIFACT_ID%" ^
  "-Dversion=%VERSION%" ^
  "-Dpackaging=jar" ^
  "-Dclassifier=%JAVADOC_CLASSIFIER%" ^
  "-DgeneratePom=false"


You also can run these commands as Beanshell script:


// installSourcesJavadocs.bsh

import org.sf.pomreader.PomReader;
import org.apache.maven.bootstrap.model.Model;
import org.sf.scriptlandia.MavenHelper;

MavenHelper.executeMaven(null, new String[] { "source:jar" });
MavenHelper.executeMaven(null, new String[] { "javadoc:jar" });

PomReader pomReader = new PomReader();
pomReader.init();

Model model = pomReader.readModel(new File("pom.xml"));

void installArtifact(Model model, String classifier) {
  System.setProperty("file", "target/" + model.getArtifactId() + "-" + classifier + ".jar");
  System.setProperty("groupId", model.getGroupId());
  System.setProperty("artifactId", model.getArtifactId());
  System.setProperty("version", model.getVersion());
  System.setProperty("packaging", model.getPackaging());
  System.setProperty("classifier", classifier);
  System.setProperty("generatePom", "false");

  MavenHelper.executeMaven(null, new String[] { "install:install-file" });
}

installArtifact(model, "sources");
installArtifact(model, "javadoc");


In order to run this script, you have to install Scriptlandia
launcher on your computer. It will take care of downloading all required dependencies, installing them locally on your computer and then executing Beanshell script.