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