Tuesday, December 28, 2010

Getting Started with Google Test and Google Mock

Writing unit test and mock frameworks in C++ is not an easy task. Fortunately, Google has created wonderful unit test and mock frameworks for us :) In this blog, I'm gonna show how to get started with Google Test and Google Mock frameworks.
1. Download Google Test and Google Mock
2. Extract them and build them by going to the make directory and type 'make'. This will create gtest_main.a and gmock_main.a static libraries.

HelloWorld.h
#ifndef HELLOWORLD_H_
#define HELLOWORLD_H_

#include <string>
#include "Messenger.h"
using namespace std;

class HelloWorld
{
public:
    HelloWorld();
    virtual ~HelloWorld();
    string getMessage(Messenger* messenger) const;
};

#endif /* HELLOWORLD_H_ */

HelloWorld.cpp
#include "HelloWorld.h"
#include "Messenger.h"

HelloWorld::HelloWorld()
{
}

HelloWorld::~HelloWorld()
{
}

string HelloWorld::getMessage(Messenger* messenger) const
{
    return messenger->getMessage();
}

Messenger.h
#ifndef MESSENGER_H_
#define MESSENGER_H_

#include <string>
using namespace std;

class Messenger
{
public:
    virtual ~Messenger() {}
    virtual string getMessage() = 0;
};

#endif /* MESSENGER_H_ */

MockMessenger.h
#ifndef MOCKMESSENGER_H_
#define MOCKMESSENGER_H_

#include "Messenger.h"
#include <string>
#include <gmock/gmock.h>
using namespace std;

class MockMessenger : public Messenger
{
public:
    MOCK_METHOD0(getMessage, string());
};

#endif /* MOCKMESSENGER_H_ */

HelloWorldTest.cpp
#include "HelloWorld.h"
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include "MockMessenger.h"
#include <string>
#include <memory>
using namespace testing;

TEST(HelloWorldTest, getMessage)
{
    MockMessenger messenger;
    std::string msg = "Hello World";
    EXPECT_CALL(messenger, getMessage()).WillRepeatedly(Return(ByRef(msg)));

    HelloWorld helloWorld;
    EXPECT_EQ("Hello World", helloWorld.getMessage(&messenger));
    EXPECT_EQ("Hello World", helloWorld.getMessage(&messenger));
    EXPECT_EQ("Hello World", helloWorld.getMessage(&messenger));
}

With gtest_main.a and gmock_main.a linked, we don't need to manually create the main function.

For more information, go to Google Test and Google Mock.

Escaping HTML String using Python

Sometimes back I wrote a Groovy script for escaping the HTML that can be useful, especially for putting source code into Blogger. Here is the Python version :) I'm seriously in love with Python's "batteries' included" motto.

escapehtml.py
import os, sys, cgi

def escapehtml(path):
    if not os.path.exists(path):
        print "Error:", path, "does not exist"
        sys.exit(1)
    text = open(path).read()
    return cgi.escape(text)

if __name__ == "__main__":
    if not len(sys.argv) == 2:
        print "Error: Invalid argument!"
        sys.exit(1)
    print escapehtml(sys.argv[1])

escapehtml_recurse.py
import os, sys, escapehtml

def escapehtml_recurse(source, destination):
    if not os.path.exists(source):
        print "Error:", path, "does not exist"
        sys.exit(1)
    for root, dirs, files in os.walk(source):
        for f in files:
            filename = os.path.join(root, f)
            content = escapehtml.escapehtml(filename)
            if not os.path.exists(destination):
                os.makedirs(destination)
            newfilename = os.path.join(destination, f + ".txt")
            newpath = open(newfilename, "w")
            print "Creating", newfilename
            newpath.write(content)
            
if __name__ == "__main__":
    if not len(sys.argv) == 3:
        print "Error: Invalid argument!"
        sys.exit(1)
    escapehtml_recurse(sys.argv[1], sys.argv[2])
    print "*** Done ***"

Building Boost with MinGW

In this blog, I'm gonna show you how to build Boost using MinGW. Below are the versions that I use.
- gcc 4.5.0
- boost 1.45.0

All the steps below need to be executed on CMD and not on MSYS.
1. Download Boost
2. Extract it to let's say C:\boost_1_45_0
3. Build bjam tool.
cd C:\boost_1_45_0\tools\build\v2\engine\src
build.bat mingw
bjam will be created in C:\boost_1_45_0\tools\build\v2\engine\src\bin.ntx86
4. Copy the bjam tool to C:\boost_1_45_0
copy bjam.exe C:\boost_1_45_0
5. Build the boost libraries.
cd C:\boost_1_45_0
bjam toolset=gcc
The include header files are located in the C:\boost_1_45_0\boost and the libraries are located in the C:\boost_1_45_0\stage\lib

Enjoy! :)

Thursday, December 23, 2010

Creating Static and Shared Libraries using GCC

There are two types of libraries in C/C++:
1. Static libraries (.a on Linux or .lib on Windows): the library of object code that is linked with becomes part of the application.
2. Shared libraries (.so on Linux or .dll on Windows): the library of object code that is linked with does not become part of the application.

In this blog, I am going to show you how to create static and shared libraries using GCC on Linux. Although the the example shown below uses C++, but the same concept is applicable to C. Simply replace g++ with gcc.

Hello.h
#include <iostream>

class Hello
{
public:
    void sayHello() const;
}; 

Hello.cc
#include "Hello.h"
using namespace std;

void Hello::sayHello() const
{
    cout << "Hello World" << endl;
} 

Main.cc
#include "Hello.h"

int main()
{   
    Hello hello;
    hello.sayHello();

    return 0;
} 

To create a static library, do the following:
1. Compile the library code.
g++ -Wall -c Hello.c
This will produce Hello.o object file.
2. Create a static library.
ar -cvq libhello.a Hello.o
This will produce libhello.a static library.
3. Compile the Main.cc code and link it against the libhello.a static library.
g++ -o Main libhello.a Main.cc
This will produce the Main executable file. When you do ldd, you won't see the dependency to libhello library since it's a static library.
ldd Main
4. Execute the Main program.
./Main

To create a shared library, do the following:
1. Compile the library code.
g++ -Wall -fPIC -c Hello.c
This will produce Hello.o object file. The -fPIC flag is required for creating a shared library. See man g++ or man gcc for more information.
2. Create a shared library.
g++ -shared -o libhello.so Hello.o
This will produce libhello.so shared library.
3. Compile the Main.cc code and link it against the libhello.so shared library.
g++ -o Main libhello.so Main.cc
This will produce the Main executable file. When you do ldd, you will see the dependency to libhello library.
ldd Main
4. Execute the Main program.
export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:."
./Main

Saturday, December 4, 2010

Getting Started with JNI

Java Native Interface (JNI) allows Java code to call native applications, e.g. applications written in C or C++. In this example, I will show you how to call C and C++ application from Java using JNI on Linux.

* JNI for C application *

CHelloWorld.java
public class CHelloWorld {
    
    static {
        System.loadLibrary("chello");
    }
    
    private native void print();

    public static void main(String[] args) {
        new CHelloWorld().print();
    }
}
1. Create a header file for CHelloWorld.
javah -jni CHelloWorld

2. Implement CHelloWorld.c
#include <jni.h>
#include <stdio.h>
#include "CHelloWorld.h"

JNIEXPORT void JNICALL
Java_CHelloWorld_print(JNIEnv *env, jobject obj)
{
    printf("Hello World from C\n");
    return;
}

3. Compile the CHelloWorld.c
gcc -Wall -fPIC -c CHelloWorld.c -I$HOME/jdk1.6.0_22/include \
-I$HOME/jdk1.6.0_22/include/linux
This will produce the CHelloWorld.o

4. Create a shared library (.so on Linux or .dll on Windows).
gcc -shared -o libchello.so CHelloWorld.o

5. Execute CHelloWorld in Java.
java -Djava.library.path=. CHelloWorld
The -Djava.library.path is required to tell where to load the library from. Alternatively, LD_LIBRARY_PATH (on Linux) or PATH (on Windows) can also be used.

* JNI for C++ application *
public class CppHelloWorld {

    static {
        System.loadLibrary("cpphello");
    }
    
    private native void print();

    public static void main(String[] args) {
        new CppHelloWorld().print();
    }
}
1. Create a header file for CppHelloWorld
javah -jni CppHelloWorld

2. Implement CppHelloWorld.cpp
#include <jni.h>
#include "CppHelloWorld.h"
#include <iostream>
using namespace std;

JNIEXPORT void JNICALL
Java_CppHelloWorld_print(JNIEnv *env, jobject obj)
{
    cout << "Hello World from C++" << endl;
    return;
}

3. Compile CppHelloWorld
g++ -Wall -fPIC -c CppHelloWorld.cpp -I$HOME/jdk1.6.0_22/include \
-I$HOME/jdk1.6.0_22/include/linux
This will produce the CppHelloWorld.o

4. Create a shared library (.so on Linux or .dll on Windows)
g++ -shared -o libcpphello.so CppHelloWorld.o

5. Execute CppHelloWorld in Java.
java -Djava.library.path=. CppHelloWorld
The -Djava.library.path is required to tell where to load the library from. Alternatively, LD_LIBRARY_PATH (on Linux) or PATH (on Windows) can also be used.

To call native library in Java using similar concept like Python's ctypes or C#'s P/Invoke, JNA is a good alternative.

Wednesday, December 1, 2010

Introduction to Makefile

Following the example from the previous blog on the Introduction to GCC, I am going to show you how to makefile to simplify all those steps.

If for example, we have a directory structure like this.
/home/cpp/HelloWorld.cpp
         /HelloWorld.hpp
         /Main.cpp

* Makefile *
CC=g++
CFLAGS=-Wall
OBJ=Main.o HelloWorld.o

Main: $(OBJ)

clean:
    rm -rf Main *.o

1. To build
make
2. To clean
make clean
Here we use Makefile's implicit rules by only specifying the CC and CFLAGS implicit variables. For more information, see this link.

If now, we have a directory structure like this.
/home/cpp/lib/libhello.a
         /include/HelloWorld.hpp
         /Main.cpp
* Makefile *
CC=g++
INCLUDEDIR=include
LIBDIR=lib/libhello.a
CFLAGS=-Wall -I$(INCLUDEDIR)
OBJ=Main.o
DEPS=$(INCLUDEDIR)/HelloWorld.hpp

Main: $(OBJ)
    $(CC) -o Main $(OBJ) $(LIBDIR) $(CFLAGS) 

%.o: %.cpp $(DEPS)
    $(CC) -c -o $@ $< $(CFLAGS)

clean:
    rm -rf Main *.o
For more information about the automatic variables, such as $@, $<, see this link.

Introduction to GCC

In this blog, I am going show you the basic stuff on how to compile, link, and create an executable of a C++ application using GCC. Although the example here uses C++, it should also be applicable to C by changing the compiler to use gcc instead of g++.

HelloWorld.hpp
#include <iostream>
using namespace std;

class HelloWorld
{
public:
    void sayHello() const;    
};

HelloWorld.cpp
#include "HelloWorld.hpp"

void HelloWorld::sayHello() const 
{
    cout << "Hello World" << endl;
}

Main.cpp
#include "HelloWorld.hpp"

int main()
{
    HelloWorld helloWorld;
    helloWorld.sayHello();        

    return 0;
}

If for example, we have a directory structure is as below.
/home/cpp/HelloWorld.cpp
         /HelloWorld.hpp
         /Main.cpp

In order to create a Main executable, we need to perform the following steps.
1. Compile the HelloWorld.cpp
g++ -Wall -c HelloWorld.cpp
-Wall option is used to display all the warnings. This will produce HelloWorld.o object file.
2. Compile the Main.cpp
g++ -Wall -c Main.cpp
This will produce Main.o object file.
3. Link the two object files (HelloWorld.o and Main.o) to create an executable (Main).
g++ -Wall -o Main Main.o HelloWorld.o
This will produce Main executable file.
4. Execute the Main file.
./Main

If we change the directory structure as below.
/home/cpp/HelloWorld.cpp
         /include/HelloWorld.hpp
         /Main.cpp
We will need to perform the following steps.
1. Compile the HelloWorld.cpp
g++ -Wall -c -Iinclude HelloWorld.cpp
The -Iinclude is used here to tell the that header files are located in the ./include directory since HelloWorld.cpp requires HelloWorld.hpp (see the source file of HelloWorld.cpp). This step will produce HelloWorld.o object file.
2. Compile the Main.cpp
g++ -Wall -c -Iinclude Main.cpp
The -Iinclude is used here to tell that the header files are located in the ./include directory since Main.cpp requires HelloWorld.hpp (see the source file of Main.cpp). This step will produce Main.o object file.
3. Link the two object files (HelloWorld.o and Main.o) to create an executable (Main).
g++ -Wall -o Main Main.o HelloWorld.o
This will produce Main executable file.
4. Execute the Main file.
./Main

To make things more complicated, let's change the directory structure as below.
/home/cpp/lib/HelloWorld.cpp
         /include/HelloWorld.hpp
         /Main.cpp
Before that, let's create a static library (libhello.a) in the lib directory.
1. Compile HelloWorld.cpp
cd lib
g++ -Wall -c HelloWorld.cpp -I../include
2. Create a static library (libhello.a).
ar -crv libhello.a HelloWorld.o
3. To view the content of the static library.
ar -t libhello.a

Now we the directory structure should look like this.
/home/cpp/lib/libhello.a
         /include/HelloWorld.hpp
         /Main.cpp

1. Compile Main.cpp
g++ -Wall -c Main.cpp -Iinclude 
This step will produce Main.o object file.
2. Link the library to create a Main executable file.
g++ -Wall -o Main Main.o lib/libhello.a -Iinclude
Alternatively, this statement can also be used.
g++ -Wall -o Main Main.o -Llib -lhello -Iinclude
Here, we don't tell where the libhello.a location explicitly. Instead, we tell where the library directory is by specifying the -Llib. This means that the library directory is located in the ./lib. The -l will strip the word lib. Thus, if we have libraries like below:
libhello.a ---> this will become -lhello
libabc.a ---> this will become -labc
libxyz.a ---> this will become -lxyz

3. Execute the Main file.
./Main

Thursday, November 18, 2010

How to Create a Playlist in Mediascape application for Sony Ericsson Xperia X10

I recently just upgraded my Sony Ericsson Xperia X10 phone from Android 1.6 to Android 2.1 expecting that the Mediascape application would allow me to create a playlist from the phone itself. Unfortunately I was wrong. The only way to create a playlist in Mediascape is to install a Media Go that can be downloaded from Sony PC Companion. Since I don't need another media player and I'm mostly running on Linux, installing Media Go isn't an option for me. After couple of hours googling about this issue, I found that Mediascape is able to recognize the M3U playlist file. I then went to Wikipedia to see what M3U format really is. It turns out that M3U file basically just a plain text file with some information in it and it's pretty easy to create/parse.

In order to create M3U format, there are 4 things that we need.
- Track length in seconds
- Artist name from ID3 tag
- Title name from ID3 tag
- Absolute/relative path where the audio file is located

This feature to create/read/modify the M3U format has been incorporated into my ID3Tidy application. To create a playlist in my Mediascape, all we need to do is to open the MP3 files that we wish to be included inside the playlist from /sdcard/music into the ID3Tidy application. And then create a M3U file and save it into the /sdcard/music/Whatever.m3u

Enjoy! :)

How to Implement Groovy's File.eachLine in Java

One thing that I like from Groovy is that the closures concept. The method File.eachLine() in Groovy is indeed very useful and handy when reading a file line by line. Because of Java's lack of closures, there is a tendency to repeat the steps of reading each line of a file in every code. It is actually quite easy to implement Groovy's File.eachLine in Java.

FileFunction.java
public interface FileFunction {

    public void read(String line); 
}

M3UParserFileFunction.java
public class M3UParserFileFunction implements FileFunction {

    private List<File> mp3Files; 
    private File m3uFile; 
    
    public M3UParserFileFunction(File m3uFile, List<File> mp3Files) { 
        this.mp3Files = mp3Files; 
        this.m3uFile = m3uFile; 
    } 
    
    @Override 
    public void read(String line) { 
        if (!line.trim().startsWith("#")) { 
            if (line.trim().toLowerCase().endsWith(".mp3")) { 
                // Try relative path first, then absolute path. 
                File file = new File(m3uFile.getParentFile(), line.trim()); 
                if (file.exists()) { 
                    mp3Files.add(file); 
                } else if (!file.exists()) { // Use absolute path 
                    file = new File(line.trim()); 
                    if (file.exists()) { 
                        mp3Files.add(file); 
                    } 
                } 
            } 
        } 
    } 
}

FileUtils.java
public class FileUtils {

    private FileUtils() { 
    }

    public static void eachLine(File file, FileFunction fileFunction) throws IOException { 
        BufferedReader br = null; 
        try { 
            br = new BufferedReader(new FileReader(file)); 
            String line = ""; 
            while ((line = br.readLine()) != null) { 
                fileFunction.read(line); 
            } 
        } finally { 
            if (br != null) { 
                br.close(); 
            } 
        } 
    } 
}

List<File> files = new ArrayList<File>(); 
FileUtils.eachLine(m3uFile, new M3UParserFileFunction(m3uFile, files)); 
for (File file : files) { doWhatever(file); }

Friday, November 5, 2010

ID3Tidy

I've created a new open source project for tidying up the ID3 tags information normally found in MP3 files.

ID3Tidy

Enjoy! :)

Monday, September 27, 2010

How to Create an Executable WAR

Have you ever wondered how Hudson let you run a WAR file as if you run an executable JAR file? The answer is simple, i.e. embed a web container inside a WAR file and create a META-INF/MANIFEST.MF that has Main-Class attribute. For Hudson, it embeds Winstone. In this example, I'm gonna use Jetty instead.

pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>org.fredy</groupId>
  <artifactId>exec-war</artifactId>
  <version>0.1</version>
  <packaging>war</packaging>
  <name>exec-war</name>
  <dependencies>
    <dependency>
      <groupId>org.mortbay.jetty</groupId>
      <artifactId>jetty</artifactId>
      <version>6.1.25</version>
    </dependency>
  </dependencies>
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <configuration>
          <source>1.6</source>
          <target>1.6</target>
        </configuration>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-assembly-plugin</artifactId>
        <configuration>
          <archive>
            <manifest>
              <mainClass>myproject.Main</mainClass>
            </manifest>
          </archive>
          <descriptorRefs>
            <descriptorRef>jar-with-dependencies</descriptorRef>
          </descriptorRefs>
        </configuration>
        <executions>
          <execution>
            <id>jar-with-dependencies</id>
            <phase>package</phase>
            <goals>
              <goal>single</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
</project>

web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
  <display-name>exec-war</display-name>
  <session-config>
    <session-timeout>30</session-timeout>
  </session-config>
  <servlet>
    <servlet-name>MyServlet</servlet-name>
    <servlet-class>myproject.servlet.MyServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>MyServlet</servlet-name>
    <url-pattern>/MyServlet</url-pattern>
  </servlet-mapping>
</web-app>

MyServlet.java
package myproject.servlet;
import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class MyServlet extends HttpServlet {

    protected void doGet(HttpServletRequest req, HttpServletResponse resp) 
            throws ServletException, IOException {
        process(req, resp);
    }

    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        process(req, resp);
    }

    private void process(HttpServletRequest req, HttpServletResponse resp) 
            throws ServletException, IOException {
        resp.setContentType("text/html");
        PrintWriter pw = resp.getWriter();
        try {
            pw.print("<html><body><h1>Hello World</h1></body></html>");
        } finally {
            pw.close();
        }
    }
}

Main.class
package myproject;

import java.net.URL;

import org.mortbay.jetty.Connector;
import org.mortbay.jetty.Server;
import org.mortbay.jetty.nio.SelectChannelConnector;
import org.mortbay.jetty.webapp.WebAppContext;

public class Main {
    
    public static void main(String[] args) throws Exception {
        Server server = new Server();
        
        Connector connector = new SelectChannelConnector();
        connector.setPort(8888);
        connector.setHost("127.0.0.1");
        server.addConnector(connector);
        
        URL warUrl = Main.class.getClassLoader().getResource("WEB-INF");
        System.out.println(warUrl);
        String warUrlString = warUrl.toExternalForm();
        server.setHandler(new WebAppContext(warUrlString, "/exec-war"));
        server.setStopAtShutdown(true);
        
        server.start();
    }
}

1. Package a WAR file
mvn package
2. Rename the package name from JAR to WAR. The JAR still has WAR structure. The file has a JAR extension because of the maven-assembly-plugin. So, it's basically a JAR file with a WAR structure inside.
3. Run it.
java -jar exec-war.war
4. Or deploy it in a web container, such as Tomcat.

My Open Source Projects

I've created two new open source projects.

FileRenamer - A small tool to do bulk renaming.
JUGen - A small tool to generate JUnit.

Do give a feedback! :)

Wednesday, September 22, 2010

Applying a License into Source Code

Below is a script to apply a license into source code.

ApplyLicense.groovy
validateArgs(this.args)

def projectDir = new File(this.args[0])
def licenseText = new File(this.args[1]).text
def fileExtension = this.args[2]

projectDir.eachFileRecurse { file ->
    if (file.name.endsWith(fileExtension)) {
        def tmpFile = new File(file.path + ".tmp")
        tmpFile.text = licenseText + file.text
        tmpFile.renameTo(file) 
    }
}

def validateArgs(def args) {
    if (args.size() < 3) {
        printUsage()
        System.exit(1)
    }
    if (!new File(args[0]).isDirectory()) {
        printUsage()
        System.exit(1)
    }
    if (!new File(args[1]).isFile()) {
        printUsage()
        System.exit(1)
    }
}

def printUsage() {
    println "Usage: groovy ApplyLicense.groovy <project_dir> <license_file> <file_extension>"
}

Getting Started with JAX-RS

The example below shows how to get started with JAX-RS with Jersey (JAX-RS RI). The client side uses jQuery 1.4.2 and jQuery UI 1.8.4. The nice thing about jQuery UI 1.8.4 is that it can style the buttons according to theme specified.

pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 
  <modelVersion>4.0.0</modelVersion> 
  <groupId>myproject</groupId> 
  <artifactId>jersey-app</artifactId> 
  <version>0.1</version> 
  <name>jersey-app</name> 
  <packaging>war</packaging> 
  <repositories> 
    <repository> 
      <id>maven2-repository.dev.java.net</id> 
      <name>Java.net Repository for Maven</name> 
      <url>http://download.java.net/maven/2/</url> 
      <layout>default</layout> 
    </repository> 
  </repositories> 
  <dependencies> 
    <dependency> 
      <groupId>com.sun.jersey</groupId> 
      <artifactId>jersey-server</artifactId> 
      <version>1.4</version> 
    </dependency> 
  </dependencies> 
  <build> 
    <plugins> 
      <plugin> 
        <groupId>org.apache.maven.plugins</groupId> 
        <artifactId>maven-compiler-plugin</artifactId> 
        <configuration> 
          <source>1.6</source> 
          <target>1.6</target> 
        </configuration> 
      </plugin> 
      <plugin> 
        <groupId>org.mortbay.jetty</groupId> 
        <artifactId>maven-jetty-plugin</artifactId> 
        <version>6.1.25</version> 
      </plugin> 
    </plugins> 
  </build> 
</project>

web.xml
<?xml version="1.0" encoding="UTF-8"?> 
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" 
  xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
  id="WebApp_ID" version="2.5"> 
  <display-name>jersey-app</display-name> 
  <servlet> 
    <servlet-name>Jersey Web Application</servlet-name> 
    <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class> 
    <init-param> 
      <param-name>com.sun.jersey.config.property.packages</param-name> 
      <param-value>myproject</param-value> 
    </init-param> 
    <load-on-startup>1</load-on-startup> 
  </servlet> 
  <servlet-mapping> 
    <servlet-name>Jersey Web Application</servlet-name> 
    <url-pattern>/ws/*</url-pattern> 
  </servlet-mapping> 
  <welcome-file-list> 
    <welcome-file>index.jsp</welcome-file> 
  </welcome-file-list> 
</web-app> 
The com.sun.jersey.config.property.packages tells the ServletContainer to scan which package that contains the JAX-RS classes.

HelloResource.java
package myproject;

import javax.ws.rs.GET; 
import javax.ws.rs.Path; 
import javax.ws.rs.PathParam; 
import javax.ws.rs.Produces;

@Path("/hello/{user}") 
public class HelloResource { 
    
    @Produces("text/plain") 
    @GET 
    public String getMessage(@PathParam("user") String user) { 
        return "Hello World, " + user; 
    } 
}

DefaultHelloResource.java
package myproject;

import javax.ws.rs.GET; 
import javax.ws.rs.Path; 
import javax.ws.rs.Produces;

@Path("/hello") 
public class DefaultHelloResource {

    @GET 
    @Produces("text/plain") 
    public String getMessage() { 
        return "Hello World"; 
    } 
}

index.jsp
<?xml version="1.0" encoding="ISO-8859-1" ?> 
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" 
    pageEncoding="ISO-8859-1"%> 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"> 
  <head> 
    <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> 
    <title>JAX-RS with Jersey</title> 
    <link type="text/css" href="css/ui-lightness/jquery-ui-1.8.4.custom.css" rel="stylesheet" /> 
    <link type="text/css" href="css/ui.jqgrid.css" rel="stylesheet" /> 
    <script type="text/javascript" src="js/jquery-1.4.2.min.js"></script> 
    <script type="text/javascript" src="js/jquery-ui-1.8.4.custom.min.js"></script> 
    <script type="text/javascript"> 
      $(function() { 
          $(".buttonStyle").button(); 
      });

      $(function() { 
          $("#button").click(function() { 
              $("#message").load("/jersey-app/ws/hello/" + $("#name").val()); 
          }); 
      }); 
    </script> 
    <style type="text/css"> 
        body { 
          font: 62.5% "Trebuchet MS", sans-serif; 
          margin: 50px; 
        } 
    </style> 
  </head> 
  <body> 
    <h1>Welcome to JAX-RS with Jersey Demo!</h1> 
    Name: <input id="name" type="text"/> 
    <button id="button" class="buttonStyle">Get Message</button> 
    <p></p> 
    <div id="message"/> 
  </body> 
</html>

Tuesday, September 21, 2010

How to Dynamically Load JAR Files

Suppose we have a Java application that need to load all the JAR files in a particular directory, e.g. plugins directory, here is how to do it.

plugin-api project
Hello.java
package myproject.api;

public interface Hello { 
    public String getMessage(); 
} 
Export this class as a JAR, e.g. plugin-api.jar


hello-impl project
HelloImpl.java
package myproject.api.impl;

import myproject.api.Hello;

public class HelloImpl implements Hello {

    @Override 
    public String getMessage() { 
        return "Hello World"; 
    } 
} 
Make sure the plugin-api.jar is in the hello-impl project classpath. Export the JAR as hello-impl.jar to main-project/plugins (see below).


bye-impl project
ByeImpl.java
package myproject.api.impl;

import myproject.api.Hello;

public class ByeImpl implements Hello {

    @Override 
    public String getMessage() { 
        return "Bye World"; 
    } 
} 
Make sure the plugin-api.jar is in the bye-impl project classpath. Export the JAR as bye-impl.jar to main-project/plugins (see below).


main project
Main.java
import java.io.File; 
import java.net.URL; 
import java.net.URLClassLoader; 
import java.util.Enumeration; 
import java.util.jar.JarEntry; 
import java.util.jar.JarFile;

import myproject.api.Hello;

public class Main {

    public static void main(String[] args) throws Exception { 
        for (File file : new File("plugins").listFiles()) { 
            URLClassLoader ucl = new URLClassLoader(new URL[] {file.toURI().toURL()}, 
                    Main.class.getClassLoader()); 
            Enumeration<JarEntry> jarEntries = new JarFile(file).entries(); 
            while (jarEntries.hasMoreElements()) { 
                JarEntry jarEntry = jarEntries.nextElement(); 
                if (jarEntry.getName().endsWith(".class")) { 
                    String className = jarEntry.getName().replace("/", 
                            ".").substring(0, 
                            jarEntry.getName().indexOf(".class")); 
                    Class c = Class.forName(className, true, ucl); 
                    for (Class i : c.getInterfaces()) { 
                        if ("myproject.api.Hello".equals(i.getName())) { 
                            Hello hello = (Hello) c.newInstance(); 
                            System.out.println(hello.getMessage()); 
                        } 
                    } 
                } 
            } 
        } 
    } 
} 
Make sure the plugin-api.jar is in the main project classpath. And create plugins directory and drop all the two implementation JARs into the plugins directory, such as below
main/plugins/hello-impl.jar 
             bye-impl.jar

Monday, September 20, 2010

SSH Login without Password

To login in SSH without password, we need to do the following:
1. Generate a key pair (private key and public key) in Server A
ssh-keygen -t rsa
2. Copy the public key from Server A to Server B.
scp id_rsa.pub testuser:[server_B_ip_address]:/home/testuser/.ssh
3. In Server B, add that public key into authorized_keys file.
cat id_rsa.pub >> authorized_keys
4. Verify it from Server A.
ssh testuser@[server_B_ip_address]

How to Setup NFS in Linux

Make sure we have the NFS (both client and server) package installed.
1. Edit /etc/exports
/home/myuser/share *rw(root_squash)
rw --> read write
ro --> read only
root_squash --> the remote root user is not allowed to have a root level access
no_root_squash --> the remote root user is allowed to have a root-level access
2. Verify it
showmount -e [nfs_server_address]
3. Open firewall port 2049
4. Restart the nfsserver deamon
/etc/init.d/nfsserver restart
5. Mount the remote export.
mount -t nfs [nfs_server_address]:/home/myuser/share nfs/

Friday, September 17, 2010

JVM Shutdown Hook

Suppose our Java application receives a SIGINT or SIGKILL signal and we need to do something before the JVM terminates. Java provides a way to add a shutdown hook into the JVM.
public class Main {

    static {
        Runtime.getRuntime().addShutdownHook(new Thread() {
            @Override
            public void run() {
                System.out.println("Bye, bye JVM :(");
            }
        });
    }
    
    public static void main(String[] args) {
        System.out.println("Doing something....");
        try {
            Thread.sleep(100000);
        } catch (Exception e) {
        } 
    }
}
Make sure to add the shutdown hook in the static block.

How to Setup Samba in Linux

Make sure we have the Samba package installed. For example, we want to share the /tmp directory in Linux so that we can access it from our Windows or from another Linux client (NFS is better for Linux to Linux file sharing), we need to do the following:
1. Edit /etc/samba.conf add the following lines
[tmp]
    comment = Test
    path = /tmp
    writable = Yes
    public = Yes
2. Add a Samba user.
smbpasswd -a sambauser
Enter the password.
3. We need to open firewall for the following ports:
- 137 (netbios-ns)
- 138 (netbios-dgm)
- 139 (netbios-ssn)
- 445 (microsoft-ds)
For more info, see /etc/services
3. Restart smb daemon.
/etc/init.d/smb restart

For Windows client to access the shared directory:
1. Go to Run and enter \\[ip_address]\tmp
2. Enter the Samba username and password.

For Linux client to access the shared directory:
mount -t cifs //172.16.242.129/tmp -o username=sambauser,password=password samba/

How to Setup DNS in Linux

To setup DNS in Linux, make sure we have BIND package installed.
1. Add these lines in /etc/resolv.conf
domain fw-geekycoder.com
nameserver 172.16.242.129
2. Add forward and reverse zones in /etc/named.conf
zone "fw-geekycoder" in {
    type master;
    file "master/fw-geekycoder.zone";
};

zone "242.16.172.in-addr.arpa" in {
    type master;
    file "master/242.16.172.zone";
};
3. As you we can see in step two, there are two zone files that need to be created.
In /var/lib/named/master/fw-geekycoder.zone file
$TTL 1W
@       IN SOA  ns1.fw-geekycoder.com. root.fw-geekycoder.com. (
                                2010191505      ; serial (d. adams)
                                2D              ; refresh
                                4H              ; retry
                                6W              ; expiry
                                1W )            ; minimum

fw-geekycoder.com.      IN NS           ns1.fw-geekycoder.com.
ns1                     IN A            172.16.242.129
www                     IN A            172.16.242.129
gateway                 IN A            172.16.242.2
In 242.16.172.zone file
$TTL 1W
@       IN SOA  ns1.fw-geekycoder.com. root.fw-geekycoder.com. (
                                2010191605      ; serial (d. adams)
                                2D              ; refresh
                                4H              ; retry
                                6W              ; expiry
                                1W )            ; minimum

129.242.16.172.in-addr.arpa.    IN NS           ns1.fw-geekycoder.com.
129                             IN PTR          ns1.fw-geekycoder.com.
129                             IN PTR          www.fw-geekycoder.com.
2                               IN PTR          gateway.fw-geekycoder.com.
4. Restart the named daemon.
/etc/init.d/named restart

Wednesday, September 15, 2010

jqGrid TreeGrid with JSON

The example below shows how to use TreeGrid using jqGrid with JSON.

index.html
<!DOCTYPE html> 
<html> 
  <head> 
    <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> 
    <title>jqGrid Example Page</title> 
    <link type="text/css" href="css/smoothness/jquery-ui-1.8.4.custom.css" rel="stylesheet" /> 
    <link type="text/css" href="css/ui.jqgrid.css" rel="stylesheet" /> 
    <script type="text/javascript" src="js/jquery-1.4.2.min.js"></script> 
    <script type="text/javascript" src="js/jquery-ui-1.8.4.custom.min.js"></script> 
    <script type="text/javascript" src="js/i18n/grid.locale-en.js"></script> 
    <script type="text/javascript" src="js/jquery.jqGrid.min.js"></script> 
    <script type="text/javascript">     
      $(function() { 
          $("#treegrid").jqGrid({ 
              url: 'tree.json', 
              datatype: 'json', 
              mtype: 'GET', 
              colNames: ["ID", "Description", "Total"], 
              colModel: [{ 
                  name: 'id', 
                  index: 'id', 
                  width: 1, 
                  hidden: true, 
                  key: true 
              }, { 
                  name: 'desc', 
                  index: 'desc', 
                  hidden: false, 
                  sortable: true 
              }, { 
                  name: 'num', 
                  index: 'num', 
                  hidden: false, 
                  sortable: true 
              }], 
              treeGridModel: 'adjacency', 
              height: 'auto', 
              width: '500', 
              pager: "#ptreegrid", 
              treeGrid: true, 
              ExpandColumn: 'desc', 
              ExpandColClick: true, 
              caption: "Tree Grid Example" 
          }) 
      }); 
    </script> 
    <style type="text/css"> 
        body { 
          font: 62.5% "Trebuchet MS", sans-serif; 
          margin: 50px; 
        } 
    </style> 
  </head> 
  <body> 
    <h1>jqGrid Example</h1> 
    <h2>Tree Grid</h2> 
    <table id="treegrid"></table> 
    <div id="ptreegrid"></div> 
  </body> 
</html>

tree.json
{ 
    "page": 1, 
    "total": 1, 
    "records": 2, 
    "rows": [ 
       {"id": 1, "cell": ["1", "Super Item", "300", 0, null, false, false]}, 
           {"id": 2, "cell": ["2", "Item 1", "100", 1, 1, false, false]}, 
           {"id": 3, "cell": ["3", "Sub Item 1", "50", 2, 2, true, true]}, 
           {"id": 4, "cell": ["4", "Sub Item 2", "25", 2, 2, false, false]}, 
           {"id": 5, "cell": ["5", "Sub-sub Item 1", "25", 3, 4, true, true]}, 
           {"id": 6, "cell": ["6", "Sub Item 3", "25", 2, 2, true, true]}, 
           {"id": 7, "cell": ["7", "Item 2", "200", 1, 1, false, false]}, 
           {"id": 8, "cell": ["8", "Sub Item 1", "100", 2, 7, false, false]}, 
           {"id": 9, "cell": ["9", "Sub-sub Item 1", "50", 3, 8, true, true]}, 
           {"id": 10, "cell": ["10", "Sub-sub Item 2", "50", 3, 8, true, true]}, 
       {"id": 11, "cell": ["11", "Sub Item 2", "100", 2, 7, true, true]} 
    ] 
} 

Tuesday, September 7, 2010

Creating ISO Images

Creating ISO images in Linux is very straightforward.
If for example, we need to create an ISO image for a CD/DVD, we can use dd command.
dd if=/dev/cdrom of=cd.iso

If we need to create an ISO image out for a directory/file, we can use mkisofs command.
mkisofs -o ~/docs.iso -J -r -V MY_LABEL ~/Documents

To verify if the ISO image is correct, we need to mount that ISO image.
mount -t iso9660 -o loop /media

To umount:
umount /media
See man dd and mkisofs for more information on the above commands.

Friday, August 20, 2010

Adding Swap Space

I just recently upgraded the amount of RAM in my system. As a result, it's a good to also increase the amount of swap space. Here is how to do it.
1. Create a swap partition using fdisk.
fdisk /dev/sdb
2. Enter n to create a new partition.
3. Enter e for extended or p for primary.
4. Enter the first and last cylinder.
5. Enter t to select the partition id. Swap has id of 82.
6. Enter w to save the changes.
7. Create a swap partition on the sdb2 partition.
mkswap /dev/sdb2
8. Enable the swap partition.
swapon /dev/sdb2
9. To enable it add boot time, add this line in /etc/fstab.
/dev/sdb2   swap   swap   defaults   0 0
10. Verify that the swap partition.
cat /proc/swaps

Tuesday, July 20, 2010

Creating Log4J Custom PatternLayout

Suppose we need to add a username information into our logging, with Log4J this can be implemented easily. Log4J allows us to create our own PatternLayout.

PatternLayout has a method createPatternParser() that will basically delegate the task to PatternParser. Here, we can create our own MyPatternLayout by extending it from PatternLayout and delegating the task to our own PatternParser as shown below.

MyPatternLayout.java
package myproject;

import org.apache.log4j.PatternLayout; 
import org.apache.log4j.helpers.PatternParser;

public class MyPatternLayout extends PatternLayout {

    @Override 
    protected PatternParser createPatternParser(String pattern) { 
        return new MyPatternParser(pattern); 
    } 
} 

Since we normally just want to add a new character in the pattern instead of redefining all the characters, we need to make sure that we call super.finalizeConverter(c) as shown below.

PatternParser.java
package myproject;

import org.apache.log4j.helpers.PatternParser;

public class MyPatternParser extends PatternParser { 
    private static final char USERNAME_CHAR = 'u'; 
    
    public MyPatternParser(String pattern) { 
        super(pattern); 
    } 
    
    @Override 
    protected void finalizeConverter(char c) { 
        switch (c) { 
        case USERNAME_CHAR: 
            currentLiteral.setLength(0); 
            addConverter(new MyPatternConverter()); 
            break; 
        default: 
            super.finalizeConverter(c); 
        } 
    } 
} 

MyPatternConverter.java
package myproject;

import org.apache.log4j.helpers.PatternConverter; 
import org.apache.log4j.spi.LoggingEvent;

public class MyPatternConverter extends PatternConverter {

    @Override 
    protected String convert(LoggingEvent evt) { 
        // For simplicity, assume this information is retrieved from somewhere. 
        return "User1"; 
    } 
} 

Main.java
package myproject;

import org.apache.log4j.Logger;

public class Main {

    private static final Logger logger = Logger.getLogger(Main.class); 
    
    public static void main(String[] args) { 
        logger.info("Hello World"); 
        logger.debug("Hello World"); 
        logger.warn("Hello World"); 
        logger.error("Hello World"); 
    } 
} 


Make sure to change the layout to our own custom PatternLayout in the log4j.properties.
log4j.properties
log4j.rootLogger=DEBUG, A1
log4j.appender.A1=org.apache.log4j.ConsoleAppender

log4j.appender.A1.layout=myproject.MyPatternLayout 
log4j.appender.A1.layout.ConversionPattern=[%-5p] <%u> [%c.%t] - %m%n

Thursday, July 15, 2010

Storing and Retrieving an Object with JNDI

In this article, I'm gonna talk about how to store and retrieve an object with JNDI. To make it easier, I'll use File System provider which can be downloaded from
http://java.sun.com/products/jndi/downloads/index.html

Let's do the world famous Hello World application.

Hello.java
package myproject;

public interface Hello { 
    
    public String getMessage(); 
} 

HelloImpl.java
package myproject;

public class HelloImpl implements Hello { 
    
    @Override 
    public String getMessage() { 
        return "Hello World"; 
    } 
} 

HelloFactory.java
package myproject;

import java.util.Hashtable;

import javax.naming.Context; 
import javax.naming.Name; 
import javax.naming.spi.ObjectFactory;

public class HelloFactory implements ObjectFactory {

    @Override 
    public Object getObjectInstance(Object obj, Name name, Context nameCtx, 
            Hashtable<?, ?> environment) throws Exception { 
        return new HelloImpl(); 
    } 
} 
This class is responsible in creating an instance of Hello.

Main.java
package myproject;

import java.util.Hashtable;

import javax.naming.Context; 
import javax.naming.InitialContext; 
import javax.naming.Reference;

public class Main {

    public static void main(String[] args) throws Exception { 
        Hashtable<String, String> env = new Hashtable(); 
        env.put(Context.INITIAL_CONTEXT_FACTORY, 
            "com.sun.jndi.fscontext.RefFSContextFactory"); 
        env.put(Context.PROVIDER_URL, "file:///C:/test"); 
        InitialContext ic = new InitialContext(env); 
        Reference ref = new Reference(Hello.class.getName(), 
            HelloFactory.class.getName(), null); 
        ic.rebind("whatever/object", ref); 
        
        System.out.println(((Hello) ic.lookup("whatever/object")).getMessage()); 
    } 
} 

Thursday, July 8, 2010

Solution to Code Golf: Word Frequency from StackOverflow (Hopefully!)

I just did this to kill my time. Not sure if it's the correct solution for this problem
http://stackoverflow.com/questions/3169051/code-golf-word-frequency-chart
but it seems to fulfill all the requirements :)

import java.io.*; 
import java.util.*;

public class WordFrequency {

    public static void main(String[] args) throws Exception { 
        File f = new File(args[0]); 
        final Map<String, Integer> map = new HashMap<String, Integer>(); 
        BufferedReader br = null; 
        try { 
            br = new BufferedReader(new InputStreamReader(new FileInputStream(f))); 
            String line = ""; 
            while ((line = br.readLine()) != null) { 
                String[] words = line.toLowerCase().trim().replaceAll("[^a-z]", " ").split("\\s+"); 
                for (String word : words) { 
                    word = word.trim(); 
                    if (!word.matches("the|and|of|to|a|o|i|i[tns]|or|")) { 
                        if (!map.containsKey(word)) { 
                            map.put(word, new Integer(1)); 
                        } else { 
                            map.put(word, map.get(word) + 1); 
                        } 
                    } 
                } 
            } 
            List<string> sortedKeysByValue = new ArrayList<string>(map.keySet()); 
            Collections.sort(sortedKeysByValue, new Comparator<string>() { 
                @Override 
                public int compare(String key1, String key2) { 
                    return map.get(key2).compareTo(map.get(key1)); 
                } 
            }); 
            sortedKeysByValue = sortedKeysByValue.subList(0, 22); 
            List<string> sortedKeysByLength = new ArrayList<string>(sortedKeysByValue); 
            Collections.sort(sortedKeysByLength, new Comparator<string>() { 
                @Override 
                public int compare(String str1, String str2) { 
                    return Integer.valueOf(str2.length()).compareTo(Integer.valueOf(str1.length())); 
                } 
            }); 
            String longestString = sortedKeysByLength.get(0); 
            int maxWidth = 80 - 4 - longestString.length(); 
            double scale = 1 / ((double) map.get(sortedKeysByValue.get(0)) / (double) maxWidth); 
            double val = 0.0; 
            for (String key : sortedKeysByValue) { 
                val = ((double) map.get(key) / (double) maxWidth); 
                double len = val * scale * maxWidth; 
                System.out.print(" "); 
                for (int n = 0; n < len; n++) { 
                    System.out.print("_"); 
                } 
                System.out.println(); 
                System.out.print("|"); 
                for (int n = 0; n < len; n++) { 
                    System.out.print("_"); 
                } 
                System.out.println("| " + key + " "); 
            } 
        } finally { 
            if (br != null) { 
                br.close(); 
            } 
        } 
    } 
}

The 'cat WordFrequency.java | wc -m' gives me 2794 characters :p Anyway, I didn't bother to obfuscate my solution to make it shorter. It was all for fun. Enjoy! :)

Friday, July 2, 2010

Getting Started with Java Debugger

In this article I'm gonna explain how to use Java Debugger with Eclipse and NetBeans.
1. Run the application in command line.
java -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8888 \
-jar whatever.jar
The suspend=y means that the application will be suspended until the debugger has been attached to it.
2. In Eclipse, click 'Debug Configuration' in the Debug icon. Go to 'Remote Java Application' and specify the project that we want to debug, the connection (Socket Attach), hostname, and port number.
In NetBeans, click on 'Attach Debugger' and specify the connection type (SocketAttach), hostname, and port number.

Getting Started with Java Profiler

In this article, I'm gonna explain how to use Java Profiler.

==============
Using VisualVM
==============
Sun JDK ships with VisualVM. It's a profiler tool that is best on NetBeans platform. To use it, do the following:
1. Type this in command line to start the application.
java -Dcom.sun.management.jmxremote.port=9999 \
-Dcom.sun.management.jmxremote.authenticate=false \
-Dcom.sun.management.jmxremote.ssl=false -jar whatever.jar
2. Run VisualVM. From command line, type jvisualvm.
3. By default all the applications that run on the JVM will be listed there, but we can also add the JXM connection by right clicking on Local and choose 'Add JMX Connection' and specify in the port. In this case, it will be 'localhost:9999'. Right click then click 'Profile'.

==============
Using NetBeans
==============
Since VisualVM is based on NetBeans platform, the NetBeans profiler is somewhat similar to the VisualVM. To use it, do the following:
1. Type this in command line to start the application.
java "-agentpath:$NETBEANS_HOME/profiler/lib/deployed/jdk16/linux
/libprofilerinterface.so=$NETBEANS_HOME/profiler/lib,5140" -jar whatever.jar
2. Open NetBeans and click 'Attach Profiler'.

=============
Using Eclipse 
=============
1. Install the TPTP plugin in Eclipse.
2. Type this in command line to start the application.
java "-agentpath:$ECLIPSE_HOME/plugins
/org.eclipse.tptp.platform.jvmti.runtime_4.6.0.v201006071900/agent_files/linux_ia32
/libJPIBootLoader.so=JPIAgent:server=enabled;CGProf" -jar whatever.jar
Using agentpath only works in TPTP version 4.6 onwards. For lower version of TPTP, refer to the official guide.
2. Click 'Profile Configuration' in the Profile icon.
3. In the 'Attach to Agent'. Go to the 'Agent' tab and click that agent.
4. Click 'Profile'

This tutorial has been tested using VisualVM for JDK 1.6.0_20, Eclipse Helios, and NetBeans 6.9 on Linux. For Windows, use the appropriate library (.so file) for Windows.

Thursday, June 24, 2010

Getting Started with OSGi

Suppose we have a thirdparty-impl, one is version 1.0 and the other one is version 1.1 and in my class I need to invoke the method in version 1.0 as well version 1.1. In standard Java environment, this won't be possible since thirdparty-impl-1.0.jar and thirdparty-impl-1.1.jar are both in the same classpath. The one that comes earlier will take precedence. This is where OSGi comes to rescue.

In this example, we will be using Equinox. To start Equinox, run this in command line.
java -jar org.eclipse.osgi_3.5.2.R35x_v20100126.jar -console

Type 'ss' to get the list of bundles installed. To get more details on the bundle, type 'bundle [bundle_id]'.

==============
thirdparty-api
==============
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>myproject</groupId>
  <artifactId>thirdparty-api</artifactId>
  <version>1.0</version>
  <name>thirdparty-api</name>
  <dependencies>
    <dependency>
      <groupId>org.osgi</groupId>
      <artifactId>osgi_R4_core</artifactId>
      <version>1.0</version>
      <optional>true</optional>
    </dependency>
    <dependency>
      <groupId>org.osgi</groupId>
      <artifactId>osgi_R4_compendium</artifactId>
      <version>1.0</version>
      <optional>true</optional>
    </dependency>
  </dependencies>
  <build>
    <plugins>
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <configuration>
          <source>1.6</source>
          <target>1.6</target>
        </configuration>
      </plugin>
      <plugin>
        <groupId>org.apache.felix</groupId>
        <artifactId>maven-bundle-plugin</artifactId>
        <executions>
          <execution>
            <id>manifest</id>
            <phase>process-classes</phase>
            <goals>
              <goal>manifest</goal>
            </goals>
          </execution>
          <execution>
            <id>bundle</id>
            <phase>package</phase>
            <goals>
              <goal>bundle</goal>
            </goals>
          </execution>
        </executions>
        <configuration>
          <instructions>
            <Bundle-ManifestVersion>2</Bundle-ManifestVersion>
            <Export-Package>myproject.api</Export-Package>
            <Import-Package />
          </instructions>
        </configuration>
      </plugin>
    </plugins>
  </build>
  <repositories>
    <repository>
      <id>ops4j</id>
      <url>http://repository.ops4j.org/maven2/</url>
      <snapshots>
        <enabled>false</enabled>
      </snapshots>
    </repository>
  </repositories>
</project> 

We need to include the ops4j repository since as of this writing the OSGi libraries aren't available in Maven central repository.

The maven-bundle-plugin is used to create the OSGi bundle. The OSGi bundle contains MANIFEST.MF. The MANIFEST.MF contains metadata information, such as packages to import, export, symbolic name, bundle version, etc.

ThirdPartyService.java
package myproject.api;

public interface ThirdPartyService { 
    
    public void doSomething(); 
}

Run 'mvn install' and deploy the bundle into Equinox by typing 'install file:[path]/thirdparty-api-1.0.jar' in the Equinox console.

Type 'ss' in the Equinox console and you should see something like this.
osgi> ss

Framework is launched.

id State       Bundle
0 ACTIVE      org.eclipse.osgi_3.5.2.R35x_v20100126
2 INSTALLED   myproject.api_1.0.0

===============
thirdparty-impl
===============
Here we will deploy the first version of thirdparty-impl and we will deploy the second version of thirdparty-impl afterwards.

ThirdPartyServiceImpl.java (version 1.0)
package myproject.api.impl;

import myproject.api.ThirdPartyService;

public class ThirdPartyServiceImpl implements ThirdPartyService {

    @Override 
    public void doSomething() { 
        System.out.println("I'm version 1.0"); 
    } 
} 

ThirdPartyPublisher.java
package myproject.api.impl;

import myproject.api.ThirdPartyService;

import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceRegistration;

public class ThirdPartyPublisher implements BundleActivator {
    private ServiceRegistration sr;

    public void start(BundleContext context) throws Exception {
        sr = context.registerService(ThirdPartyService.class.getName(),
                new ThirdPartyServiceImpl(), null);
    }

    public void stop(BundleContext context) throws Exception {
        sr.unregister();
    }
}

pom.xml (version 1.0)
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>myproject</groupId>
  <artifactId>thirdparty-impl</artifactId>
  <version>1.0</version>
  <name>thirdparty-impl</name>
  <dependencies>
    <dependency>
      <groupId>myproject</groupId>
      <artifactId>thirdparty-api</artifactId>
      <version>1.0</version>
    </dependency>
    <dependency>
      <groupId>org.osgi</groupId>
      <artifactId>osgi_R4_core</artifactId>
      <version>1.0</version>
      <optional>true</optional>
    </dependency>
    <dependency>
      <groupId>org.osgi</groupId>
      <artifactId>osgi_R4_compendium</artifactId>
      <version>1.0</version>
      <optional>true</optional>
    </dependency>
  </dependencies>
  <build>
    <plugins>
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <configuration>
          <source>1.6</source>
          <target>1.6</target>
        </configuration>
      </plugin>
      <plugin>
        <groupId>org.apache.felix</groupId>
        <artifactId>maven-bundle-plugin</artifactId>
        <executions>
          <execution>
            <id>manifest</id>
            <phase>process-classes</phase>
            <goals>
              <goal>manifest</goal>
            </goals>
          </execution>
          <execution>
            <id>bundle</id>
            <phase>package</phase>
            <goals>
              <goal>bundle</goal>
            </goals>
          </execution>
        </executions>
        <configuration>
          <instructions>
            <Bundle-ManifestVersion>2</Bundle-ManifestVersion>
            <Import-Package>org.osgi.framework,myproject.api</Import-Package>
            <Bundle-Activator>myproject.api.impl.ThirdPartyPublisher</Bundle-Activator>
            <Bundle-Version>1.0</Bundle-Version>
          </instructions>
        </configuration>
      </plugin>
    </plugins>
  </build>
  <repositories>
    <repository>
      <id>ops4j</id>
      <url>http://repository.ops4j.org/maven2/</url>
      <snapshots>
        <enabled>false</enabled>
      </snapshots>
    </repository>
  </repositories>
</project> 

Run 'mvn install' and deploy the bundle into Equinox by typing 'install file:[path]/thirdparty-impl-1.0.jar' in the Equinox console.

Type 'ss' in the Equinox console and you should see something like this.
osgi> ss

Framework is launched.

id State       Bundle
0 ACTIVE      org.eclipse.osgi_3.5.2.R35x_v20100126
2 INSTALLED   myproject.api_1.0.0
3 INSTALLED   myproject.api.impl_1.0.0

Now let's build the second version of thirdparty-impl.

ThirdPartyServiceImpl.java (version 1.1)
package myproject.api.impl;

import myproject.api.ThirdPartyService;

public class ThirdPartyServiceImpl implements ThirdPartyService {

    @Override 
    public void doSomething() { 
        System.out.println("I'm version 1.1"); 
    } 
} 

pom.xml (version 1.1)
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>myproject</groupId>
  <artifactId>thirdparty-impl</artifactId>
  <version>1.1</version>
  <name>thirdparty-impl</name>
  <dependencies>
    <dependency>
      <groupId>myproject</groupId>
      <artifactId>thirdparty-api</artifactId>
      <version>1.0</version>
    </dependency>
    <dependency>
      <groupId>org.osgi</groupId>
      <artifactId>osgi_R4_core</artifactId>
      <version>1.0</version>
      <optional>true</optional>
    </dependency>
    <dependency>
      <groupId>org.osgi</groupId>
      <artifactId>osgi_R4_compendium</artifactId>
      <version>1.0</version>
      <optional>true</optional>
    </dependency>
  </dependencies>
  <build>
    <plugins>
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <configuration>
          <source>1.6</source>
          <target>1.6</target>
        </configuration>
      </plugin>
      <plugin>
        <groupId>org.apache.felix</groupId>
        <artifactId>maven-bundle-plugin</artifactId>
        <executions>
          <execution>
            <id>manifest</id>
            <phase>process-classes</phase>
            <goals>
              <goal>manifest</goal>
            </goals>
          </execution>
          <execution>
            <id>bundle</id>
            <phase>package</phase>
            <goals>
              <goal>bundle</goal>
            </goals>
          </execution>
        </executions>
        <configuration>
          <instructions>
            <Bundle-ManifestVersion>2</Bundle-ManifestVersion>
            <Import-Package>org.osgi.framework,myproject.api</Import-Package>
            <Bundle-Activator>myproject.api.impl.ThirdPartyPublisher</Bundle-Activator>
            <Bundle-Version>1.1</Bundle-Version>
          </instructions>
        </configuration>
      </plugin>
    </plugins>
  </build>
  <repositories>
    <repository>
      <id>ops4j</id>
      <url>http://repository.ops4j.org/maven2/</url>
      <snapshots>
        <enabled>false</enabled>
      </snapshots>
    </repository>
  </repositories>
</project> 

Run 'mvn install' and deploy the bundle into Equinox by typing 'install file:[path]/thirdparty-impl-1.1.jar' in the Equinox console.

Type 'ss' in the Equinox console and you should see something like this.
osgi> ss

Framework is launched.

id State       Bundle
0 ACTIVE      org.eclipse.osgi_3.5.2.R35x_v20100126
2 INSTALLED   myproject.api_1.0.0
3 INSTALLED   myproject.api.impl_1.0.0
4 INSTALLED   myproject.api.impl_1.1.0

================
simple-activator
================
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>myproject</groupId>
  <artifactId>simple-activator</artifactId>
  <version>1.0</version>
  <name>simple-activator</name>
  <dependencies>
    <dependency>
      <groupId>org.osgi</groupId>
      <artifactId>osgi_R4_core</artifactId>
      <version>1.0</version>
      <optional>true</optional>
    </dependency>
    <dependency>
      <groupId>org.osgi</groupId>
      <artifactId>osgi_R4_compendium</artifactId>
      <version>1.0</version>
      <optional>true</optional>
    </dependency>
    <dependency>
      <groupId>myproject</groupId>
      <artifactId>thirdparty-api</artifactId>
      <version>1.0</version>
    </dependency>
  </dependencies>
  <build>
    <plugins>
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <configuration>
          <source>1.6</source>
          <target>1.6</target>
        </configuration>
      </plugin>
      <plugin>
        <groupId>org.apache.felix</groupId>
        <artifactId>maven-bundle-plugin</artifactId>
        <executions>
          <execution>
            <id>manifest</id>
            <phase>process-classes</phase>
            <goals>
              <goal>manifest</goal>
            </goals>
          </execution>
          <execution>
            <id>bundle</id>
            <phase>package</phase>
            <goals>
              <goal>bundle</goal>
            </goals>
          </execution>
        </executions>
        <configuration>
          <instructions>
            <Bundle-ManifestVersion>2</Bundle-ManifestVersion>
            <Import-Package>org.osgi.framework,org.osgi.util.tracker,myproject.api</Import-Package>
            <Bundle-Activator>myproject.activator.SimpleActivator</Bundle-Activator>
            <Export-Package />
          </instructions>
        </configuration>
      </plugin>
    </plugins>
  </build>
  <repositories>
    <repository>
      <id>ops4j</id>
      <url>http://repository.ops4j.org/maven2/</url>
      <snapshots>
        <enabled>false</enabled>
      </snapshots>
    </repository>
  </repositories>
</project> 

SimpleActivator.java
package myproject.activator;

import myproject.api.ThirdPartyService;

import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;

public class SimpleActivator implements BundleActivator {

    @Override
    public void start(BundleContext context) throws Exception {
        ThirdPartyService service = (ThirdPartyService) context.getService(context.getBundle(3L)
                .getRegisteredServices()[0]);
        service.doSomething();

        service = (ThirdPartyService) context.getService(context.getBundle(4L)
                .getRegisteredServices()[0]);
        service.doSomething();
    }

    @Override
    public void stop(BundleContext context) throws Exception {
    }
}

Run 'mvn install' and deploy the bundle into Equinox by typing 'install file:[path]/simple-activator-1.0.jar' in the Equinox console.

Type ss in the Equinox console and you should see something like this.
osgi> ss

Framework is launched.

id State       Bundle
0 ACTIVE      org.eclipse.osgi_3.5.2.R35x_v20100126
2 INSTALLED   myproject.api_1.0.0
3 INSTALLED   myproject.api.impl_1.0.0
4 INSTALLED   myproject.api.impl_1.1.0
5 INSTALLED   myproject.activator_1.0.0

Now start all the bundles in Equinox console.
osgi> start 2

osgi> start 3

osgi> start 4

osgi> start 5
I'm version 1.0
I'm version 1.1

Pretty cool, huh? :)

Thursday, June 17, 2010

Setting Up Quota in Linux

Setting up a quota in Linux is easy.
1. Install quota.

In Ubuntu:
sudo apt-get install quota

In openSUSE:
sudo yast2 -i quota

2. Add usrquota and grpquota parameters to the mount options in the /etc/fstab, e.g.
/dev/sda2  /  ext4  acl,user_xattr,usrquota,grpquota 1 1

For file systems that support journaling, such as ext3 and ext4, we need to add additional options, e.g.
/dev/sda2 / ext4 acl,user_xattr,usrjquota=aquota.user,
grpjquota=aquota.group,jqfmt=vfsv0 1 1

3. Run quotacheck -amvug
This command is used to perform a scan in the file system for disk usage as well as to create aquota.group and aquota.user files. In our example, the aquota.group and aquota.user files are created in / directory.

4. Run edquota -u <username>
This command is used to set a quota for a particular user. The block here means how many blocks the user can use and the inode here means how many files the user can own.

5. Run repquota -av
This command is used to give a summary on the quotas.

Sunday, June 6, 2010

Create, Modify, and Delete User in Linux

These steps require root privilege.

To create a new user:
useradd -m -c "User1" -s /bin/bash user1
The -m option will automatically create a user's home directory, i.e. /home/user1.
The -s option is to specify the user's default shell.

To modify an existing user:
Suppose we want to add an additional group newgroup to user1.
In Ubuntu,
usermod -a -G newgroup user1
In openSUSE
usermod -A newgroup user1
Be aware that, if you specify only with -G, you need to include the group that you wish to add as well as the groups that have already been added. That's why in Ubuntu, it has additional switch, which is -a to append the group and -A in openSUSE.

To delete an existing user:
userdel -r user1

Saturday, June 5, 2010

Changing Default Editor in Linux

Some tools, such as visudo use nano editor by default. This is especially true in a distro like Ubuntu while openSUSE uses vi. As a vi user, I'm more comfortable editing a file using vi. To change it, add this line in the .bashrc.
export EDITOR=vim

Thursday, May 27, 2010

Creating New Partition and File System in Linux

Creating a new partition and file system, a.k.a formatting in Windows world is very easy. We can do that in just few clicks (right click, format, and select the file system). Creating a new partition and file system in Linux, however, is not that easy. Here I'm gonna show you how to do it.

You need to run these steps as root.
1. Run 'fdisk '
Example:
# fdisk /dev/sdb
If you're creating a new partition in the first SATA/SCSI disk, the device name will be /dev/sda, if it's a second SATA/SCSI disk, it'll be /dev/sdb and so on. If the device is the first master IDE disk, it'll be /dev/hda and so on.
2. Choose n to add a new partition.
3. Choose e for extended or p for primary.
4. Accept the default values for first and last cylinder if you wish to partition the whole disk.
5. Choose w to write the table to disk.
If suppose you've created the first partition in the second SATA/SCSI disk, you should be able to see that /dev/sdb1 has been created in your file system.
6. The next step is to create a file system on that partition. The command is 'mkfs -t '
Example:
# mkfs -t ext4 /dev/sdb1
The here can be ext2, ext3, ext4, etc. The here should be /dev/sdb1 and NOT /dev/sdb.
7. The last step is to mount this partition. To do that, execute 'mount -t '
Example:
# mkdir -p /mnt/newdisk
# mount -t ext4 /mnt/newdisk
To see if your partition has been mounted successfully. Run 'mount' or 'cat /etc/mtab'.
To mount it permanently, you need to create a new entry in the /etc/fstab as shown below.
/dev/sdb1            /mnt/newdisk         ext4       acl,user_xattr        1 2
The value last second option 1 specifies whether or not the file system should be dumped. 1 means dump. 0 means don't dump.
The value last option 2 specifies the order in which fsck should check the file system at reboot. For root partition, it should be 1, other partitions should be 2.

Tuesday, May 25, 2010

AspectJ Load-Time Weaving

AspectJ has two weaving models, build-time and load-time. Here I'm gonna talk about binary load-time weaving. The good thing about it is that we can create a separate Java project and AspectJ project and weave them at runtime. This can be achieved thanks to JVMTI. Hence, it requires Java 5 and above.

Let's say we have a Java project called my-java-project.

HelloWorld.java
package myproject;

public class HelloWorld {
    
    public void sayHello(String message) {
        System.out.println(message);
    }
}

Main.java
package myproject;

public class Main {

    public static void main(String[] args) {
        HelloWorld helloWorld = new HelloWorld();
        helloWorld.sayHello("Hello World");
    }
}

And an AspectJ project called my-aspectj-project.

HelloWorldAspect.java
package myproject;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;

@Aspect
public class HelloWorldAspect {

    @Around("execution(* myproject.HelloWorld.sayHello(..))")
    public Object aroundAspect(ProceedingJoinPoint joinPoint) {
        System.out.println("Before sayHello()");
        Object obj = null;
        try {
            obj = joinPoint.proceed();
        } catch (Throwable e) {
            e.printStackTrace();
        }
        System.out.println("After sayHello()");
        return obj;
    }
}

Here we also need to add META-INF/aop.xml inside this AspectJ jar.

aop.xml
<?xml version="1.0" encoding="UTF-8"?>
<aspectj>
    <aspects>
        <aspect name="myproject.HelloWorldAspect" />
    </aspects>
</aspectj>

To run, execute this from the commaand line.
java -javaagent:aspectjweaver-1.5.4.jar -cp my-aspectj-project-0.0.1-SNAPSHOT.jar:my-java-project-0.0.1-SNAPSHOT.jar:aspectjrt-1.6.7.jar myproject.Main

Monday, May 24, 2010

Mixin in Groovy and Java

Mixin is the ability to mix certain functionalities into other classes, i.e. to add new methods into other classes.

Because of the dynamic nature of Groovy, it's very straightforward to use mixin in Groovy. All we need to do is to call mixin in a class that we want to mix in as shown in the example below.

class Laughable {
    def laugh() {
        println "Hahaha"
    }
}

class Person {
}

Person.mixin(Laughable)

Person person = new Person()
person.laugh()

In Java, however, it's not that straightfoward to implement mixin. Luckily, we have AspectJ that can help to do that. Here I'm gonna show you how to use mixin using @AspectJ syntax.

Laughable.java
package myproject;

public interface Laughable {

    public void laugh();
}

LaughableImpl.java
package myproject;

public class LaughableImpl implements Laughable {

    @Override
    public void laugh() {
        System.out.println("Hahaha");
    }
}

Person.java
package myproject;

public class Person {
}

All we need here is to declare @DeclareMixin annotation.

LaughableAspect.java
package myproject;

import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.DeclareMixin;

@Aspect
public class LaughableAspect {

    @DeclareMixin("myproject.Person")
    public Laughable identifiable() {
        return new LaughableImpl();
    }
}


The ugly part here is that we need to typecast the person object into Laughable interface.
Main.java
package myproject;

public class Main {

    public static void main(String[] args) {
        Person person = new Person();
        Laughable laughable = (Laughable) person;
        laughable.laugh();
    }
}

Sunday, May 23, 2010

Create ISO Images on Linux

Creating ISO images on Linux is very straightforward. Unlike in Windows, you don't need any additional tool to install. You just need to type this in your terminal.

dd if=/dev/cdrom of=image.iso

Most Linux distros nowadays have a graphical utility to let you mount the ISO image. If you prefer to do it in command line, here is how to do it.

mkdir -p /mnt/myimage
mount -o loop -t iso9660 cd.iso /mnt/myimage

Friday, May 21, 2010

Escaping HTML String using Groovy

As all of you know, I use SyntaxHighlighter for formatting the source code. If let's say you have HTML or XML source, you need to escape it first in order for SyntaxHighlighter to display it correctly.

I created a small utility for that purpose. Make sure you include Apache commons-lang in your classpath.

import org.apache.commons.lang.StringEscapeUtils

validateArgs(this.args)

new File(this.args[0]).eachLine {
    println StringEscapeUtils.escapeHtml(it)
}

def validateArgs(def args) {
    if (args.size() != 1) {
        printUsage()
        System.exit(1)
    }

    if (!new File(args[0]).isFile()) {
        printUsage()
        System.exit(1)
    }
}

def printUsage() {
    println "Usage: HtmlEscape.groovy <file>"
}

Wednesday, May 19, 2010

AspectJ Inter-type Declaration

AspectJ is a very powerful AOP framework. It's most commonly used for logging, security, transaction, etc. Besides that, AspectJ has a powerful feature that allows you to add a method or a field to a class, or declare a type that implement an interface. I will show you how it can be done in AspectJ.

Here, we intend to add a new method, called sayBye(String message) into HelloWorld.
HelloWorld.java
package myproject;

public class HelloWorld {

    public void sayHello(String message) {
        System.out.println(message);
    }
}

We create a new interface called ByeWorld and make HelloWorld class to implement it.
ByeWorldAspect.aj
package myproject;

public aspect ByeWorldAspect {

    declare parents: HelloWorld implements ByeWorld;

    public void ByeWorld.sayBye(String message) {
        System.out.println(message);
    }

    before(ByeWorld byeWorld)
        : execution(* ByeWorld+.*(..))
            && !execution(* ByeWorld.*(..))
            && this(byeWorld) {
    }

    private static interface ByeWorld {
    }
}

Main.java
package myproject;

public class Main {

    public static void main(String[] args) {
        HelloWorld helloWorld = new HelloWorld();
        helloWorld.sayHello("Hello World!");
        helloWorld.sayBye("Bye World!"); // There is no sayBye() in HelloWorld class!
    }
}

Make sure you add failOnError to true in the maven-compiler-plugin.
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>myproject</groupId>
    <artifactId>aspectj-project</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>
    <dependencies>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.6.7</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.6<source>
                    <target>1.6</target>
                    <failOnError>false</failOnError>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>aspectj-maven-plugin</artifactId>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                        </goals>
                        <configuration>
                            <source>1.6</source>
                            <target>1.6</target>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <executions>
                    <execution>
                        <goals>
                            <goal>java</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <mainClass>myproject.Main</mainClass>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

Although this feature is very powerful, my advice is not to overuse it because it can lead your code into an unmaintainable state. Remember, great power comes with great responsibility :)

Metro and Apache CXF Tutorial

Metro and Apache CXF are both popular web service frameworks. Both implement JAX-WS specification. Apache CXF uses Spring, so the configuration can be a bit complex, but it offers more features, such as JAX-RS as compared to Metro. Since Apache CXF is based on Spring, it has a lot of dependencies with other libraries. On the other hand, Metro is a lightweight web service framework. So, it can be a good choice if you prefer something simple and lightweight. Metro is the default web service implementation in GlassFish.

Let's build a typical hello world application.

HelloWorld.java
package myproject;

import javax.jws.WebService;

@WebService
public interface HelloWorld {

    String sayHello(String msg);
}

HelloWorldImpl.java
package myproject;

import javax.jws.WebService;

@WebService
public class HelloWorldImpl implements HelloWorld {

    @Override
    public String sayHello(String msg) {
        return msg;
    }
}

The beauty of JAX-WS specification is that you can switch to different implementations with minimal changes as shown below.

For Apache CXF
cxf-myproject.xml (place this file in src/main/webapp/WEB-INF)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jaxws="http://cxf.apache.org/jaxws"
    xmlns:soap="http://cxf.apache.org/bindings/soap"
    xsi:schemaLocation="
         http://www.springframework.org/schema/beans
         http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
         http://cxf.apache.org/jaxws
         http://cxf.apache.org/schemas/jaxws.xsd">

    <import resource="classpath:META-INF/cxf/cxf.xml" />
    <import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />
    <import resource="classpath:META-INF/cxf/cxf-servlet.xml" />

    <jaxws:endpoint id="helloWorld" implementor="myproject.HelloWorldImpl"
        address="/helloWorld">
    </jaxws:endpoint>
</beans>

web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="2.4"
    xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
    <display-name>webservices-project</display-name>
    <servlet>
        <servlet-name>CXFServlet</servlet-name>
        <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>CXFServlet</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/cxf-myproject.xml</param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
</web-app>

pom.xml
<dependency>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-bundle</artifactId>
    <version>2.2.6</version>
</dependency>
<dependency>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-rt-frontend-jaxws</artifactId>
    <version>2.2.6</version>
</dependency>

For Metro (the sun-jaxws.xml is optional if you deploy it in GlassFish)
sun-jaxws.xml (place this file in src/main/webapp/WEB-INF)
<?xml version="1.0" encoding="UTF-8"?>
<endpoints xmlns="http://java.sun.com/xml/ns/jax-ws/ri/runtime"
    version="2.0">

    <endpoint name="helloWorld" implementation="myproject.HelloWorldImpl"
        url-pattern="/helloWorld" />
</endpoints>

web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="2.4"
    xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
    <display-name>webservices-project</display-name>
    <listener>
        <listener-class>com.sun.xml.ws.transport.http.servlet.WSServletContextListener</listener-class>
    </listener>
    <servlet>
        <servlet-name>MetroServlet</servlet-name>
        <servlet-class>com.sun.xml.ws.transport.http.servlet.WSServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>MetroServlet</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>
</web-app>

pom.xml
<dependency>
    <groupId>com.sun.xml.ws</groupId>
    <artifactId>jaxws-rt</artifactId>
    <version>2.1.4</version>
</dependency>
<dependency>
    <groupId>org.jvnet</groupId>
    <artifactId>mimepull</artifactId>
    <version>1.3</version>
</dependency>

jaxws-rt depends on mimepull 1.2 and at this time of writing, mimepull 1.2 is not available in central repo, so I use mimepull 1.3 instead.

To run the sample application, just type 'mvn clean jetty:run'.

Wednesday, May 12, 2010

How to Export to HTML in soapUI Open Source Edition

soapUI Pro has a feature to export the test result in HTML format. The open source edition can only export the test result in XML and text. I wrote a small utility to export the test result in HTML format.

I make use of Groovy templating engine here. Feel to modify it according to your needs.

To run the script, you need to include the commons-lang in the classpath and place the report.template in the same directory as the RunSoapUiTest.groovy. Enjoy :)

RunSoapUiTest.groovy
import groovy.text.GStringTemplateEngine
import org.apache.commons.lang.StringEscapeUtils

final TEMPLATE_FILE = "report.template"

class TestSuite {
    def name
    def numTestCases
    def numFailures
    def testCases
}

class TestCase {
    def name
    def status
    def type
    def time
}

def createReport(def template, def testSuite, def outputFile) {
    def templateFile = new File(template)
    def outFile = new File(outputFile)
    if (outFile.exists()) {
        outFile.renameTo(new File(outFile.path + "." + new Date(outFile.lastModified()).format("yyyyMMdd-HHmmss")))
    }
    def binding = ["testSuite":testSuite]
    def t = new GStringTemplateEngine().createTemplate(templateFile).make(binding)
    def writer = new PrintWriter(outputFile)
    try {
        t.writeTo(writer)
    } finally {
        writer?.close()
    }
}

def getTestSuite(def testResultDir) {
    def testSuite = new TestSuite()
    testSuite.numTestCases = 0
    testSuite.numFailures = 0
    testSuite.testCases = []
   
    def dir = new File(testResultDir)
    dir.eachFile { file ->
        if (file.name.endsWith(".txt")) {
            def splitStr = file.name.split('-')
            testSuite.name = splitStr[0]
            testSuite.numTestCases += 1
            testSuite.numFailures += (splitStr[splitStr.size()-1].startsWith("OK")) ? 0 : 1
           
            def testCase = new TestCase()
            testCase.name = ''
            testCase.type = '
'
           
            (1..splitStr.size()-3).each {
                testCase.name += splitStr[it] + ' '
            }
            testCase.status = (splitStr[splitStr.size()-1].startsWith("OK")) ? 'Success' : 'Failure'
            def content = false
            file.eachLine { line ->
                if (line.startsWith('Time Taken')) {
                    testCase.time = line.split(':')[1].trim()
                } else if (line.contains('- Request -')) {
                    content = true
                }   
               
                if (content) {
                    testCase.type += StringEscapeUtils.escapeHtml(line) + '\n
'
                }
            }
            testCase.type += '
' testSuite.testCases += testCase } } return testSuite } def printUsage() { println "Usage: groovy RunSoapUiTest.groovy <test_suite_name> <soapUI_project> <test_results_dir> [host:port]" } def validateArgs(def args) { if (args.size() >= 3 && args.size() <= 4) { if (!new File(args[1]).isFile()) { return false } if (!new File(args[2]).isDirectory()) { return false } } return true } def cleanUp(def dir) { new File(dir).eachFile { file -> if (file.name.endsWith(".xml") || file.name.endsWith(".txt")) { file.delete() } } } if (System.env['SOAPUI_HOME'] == null) { println "Please set the SOAPUI_HOME environment variable" System.exit(1) } println "SOAPUI_HOME=${System.env['SOAPUI_HOME']}" if (!validateArgs(this.args)) { printUsage() System.exit(1) } def testSuiteName = args[0] def soapUiProject = args[1] def testResultsDir = args[2] def host = (args.size() == 4) ? "-h" + args[3] : "" def cmd = "\"${System.env['SOAPUI_HOME'] + File.separator + "bin" + File.separator + "testrunner"}\" " + "${host} -s\"${testSuiteName}\" -a -j -f \"${testResultsDir}\" -I \"${soapUiProject}\"" println "Command=${cmd}" cmd.execute().waitForProcessOutput(System.out, System.err) def testSuite = getTestSuite(testResultsDir) createReport(TEMPLATE_FILE, testSuite, testResultsDir + File.separator + testSuiteName + "_TestReport-" + Calendar.getInstance().format("yyyyMMdd") + ".html") cleanUp(testResultsDir) println "***** Done *****"

report.template
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html xmlns:stringutils="xalan://com.eviware.soapui.support.StringUtils" xmlns:lxslt="http://xml.apache.org/xslt">
<head>
<META http-equiv="Content-Type" content="text/html; charset=US-ASCII">
<title>soapUI Test Results</title>
<style type="text/css">
body {
font:normal 68% verdana,arial,helvetica;
color:#000000;
}
table tr td, table tr th {
font-size: 68%;
}
table.details tr th{
font-weight: bold;
text-align:left;
background:#a6caf0;
}
table.details tr td{
background:#eeeee0;
}

p {
line-height:1.5em;
margin-top:0.5em; margin-bottom:1.0em;
}
h1 {
margin: 0px 0px 5px; font: 165% verdana,arial,helvetica
}
h2 {
margin-top: 1em; margin-bottom: 0.5em; font: bold 125% verdana,arial,helvetica
}
h3 {
margin-bottom: 0.5em; font: bold 115% verdana,arial,helvetica
}
h4 {
margin-bottom: 0.5em; font: bold 100% verdana,arial,helvetica
}
h5 {
margin-bottom: 0.5em; font: bold 100% verdana,arial,helvetica
}
h6 {
margin-bottom: 0.5em; font: bold 100% verdana,arial,helvetica
}
.Error {
font-weight:bold; color:red;
}
.Failure {
font-weight:bold; color:purple;
}
.Properties {
text-align:right;
}
</style>
</head>
<body>
<a name="top"></a>
<h1>soapUI Test Results</h1>
<hr size="1">
<h2>Summary of ${testSuite.name}</h2>
<table width="95%" cellspacing="2" cellpadding="5" border="0" class="details">
<tr valign="top">
<th>TestCases</th><th>Failures</th><th>Success Rate (%)</th>
</tr>
<tr valign="top" class="Failure">
<td>${testSuite.numTestCases}</td><td>${testSuite.numFailures}</td><td>${((testSuite.numTestCases - testSuite.numFailures) / testSuite.numTestCases) * 100}</td>

</tr>
</table>
<table width="95%" border="0">
<tr>
<td style="text-align: justify;">
Note: <i>failures</i> are anticipated and checked for with assertions while <i>errors</i> are unanticipated.
</td>
</tr>
</table>
<p></p>
<hr align="left" width="95%" size="1">
<h3>${testSuite.name}</h3>
<table width="95%" cellspacing="2" cellpadding="5" border="0" class="details">
<tr valign="top">
<th>TestCase</th><th>Status</th><th width="80%">Type</th><th nowrap>Time (s)</th>
</tr>
<% testSuite.testCases.each { testCase -> %>
<tr valign="top" class="${testCase.status == 'Failure' ? 'Error' : ''}">
<td>${testCase.name}</td><td>${testCase.status}</td><td>${testCase.type}</td><td>${testCase.time}</td>
</tr>
<% } %>
</table>
<p></p>
<a href="#top">Back to top</a>
</body>
</html>