Tuesday, November 17, 2009

TCCL in OSGi(Spring DM)

In order to better understand how TCCL works in OSGi i have done a sample POC.

I created 3 bundles.

Bundle A - calls Bundle B (imports Bundle B)

Bundle B -calls Bundle C (imports Bundle A)

Bundle C - instantiates class in Bundle A (No imports)

In normal cases Bundle C will fail to instantiate class in Bundle A, but this can be achieved using TCCL as follows.

ITest test=(ITest)Thread.currentThread().getContextClassLoader().loadClass("com.bundleA.external.ActionClass").newInstance();

Conclusion: Spring Dm sets TCCL to the bundle classloader where the thread initiated.

Now what if we want to instantiate class of bundle B in Bundle C , but the thread flow doesnt change. ?

This can be easily done by resetting the TCCL in bundle B's method as follows

Thread.currentThread().setContextClassLoader(Bundle2.class.getClassLoader());

BootClassPath and Sytem ClassPath in OSGI(Spring DM)

System Package

java.* can be directly used without any imports. This is neither part of boot delegation nor system packages as they are part of jdk itself.For loading these fundamental types, the OSGi platform uses parent delegation instead of the networking model; that is, it uses the class loader that started the OSGi framework to load classes instead of an OSGi class space.

None of the other public packages available in the JDK such as the javax.net or javax.xml are parent delegated which means they have to be resolved within the class space that is, bundles need to import the packages (which means there needs to be a provider) since they are not implied. The OSGi spec allows the Framework (through its system bundle) to export
any relevant packages from its parent class loader as system packages using the org.osgi.framework.system.packages property.

As repacking the hosting JDK as a bundle isn't a viable option, one can use this setting to have the system bundle (or the bundle with id 0) export these packages itself.

Extension Bundles

Another possible option is to enhance the system bundle through extension bundles. These act as fragments; they are not bundles of their own but rather are attached to a host.
Extension bundles are a special kind of fragments that get attached only to the System bundle in order to deliver optional parts of the Framework

Boot Delegation

This allows the user to create 'implied' packages that will always be loaded by the framework parent class loader, even if the bundles do not provide the proper imports:

This option is mainly created to accommodate various corner cases especially in the JDK classes that expect parent class loading delegation to always occur or that assume that every class loader on the system has full access to the entire boot path.There are cases where boot delegation is quite handy; a good candidate being instrumentation, such as profiling or code coverage.

Ref:http://blog.springsource.com/2009/01/19/exposing-the-boot-classpath-in-osgi/

I have done samples to better understand how classloader comes into picture for loading classes at different levels by printing parent classloaders upto 4 levels.

Class loader for one of the classes of bundle in pick up.

bundle classloader ServerBundleClassLoader: [bundle=ClassPathTest_1.0.0]
bundles parent java.net.URLClassLoader@126e85f
bundles parent sun.misc.Launcher$AppClassLoader@7d772e
bundles parent sun.misc.Launcher$ExtClassLoader@11b86e7


Class loader hierarchy for org.osgi.framework.BundleActivator

**************System bundle 0*******************
bundle classloader java.net.URLClassLoader@126e85f
bundles parent sun.misc.Launcher$AppClassLoader@7d772e
bundles parent sun.misc.Launcher$ExtClassLoader@11b86e7
I bundles parent null


Created a dummy class and added this in the classpath of dm server startup.bin.Also modified to include this java6-server.profile as org.osgi.framework.system.packages = \
(I did import this package in my test bundle)

*************SYSTEM PATH**************
bundle classloader sun.misc.Launcher$AppClassLoader@7d772e
bundles parent sun.misc.Launcher$ExtClassLoader@11b86e7
bundles parent null
I parent null


Created a dummy class and added this in the classpath of dm server startup.bin.Also modified to include this java6-server.profile as org.osgi.framework.bootdelegation = \
(Did not import this package in my test bundle)


*************BOOT PATH**************

bundle classloader sun.misc.Launcher$AppClassLoader@7d772e
bundles parent sun.misc.Launcher$ExtClassLoader@11b86e7
I bundles parent null
I parent null

Sunday, November 15, 2009

Configuring Component based logging in OSGi using Log4j

Initialize reading of the log4j xml in any of the core bundles. Ensure that this bundle doesnt get bounced often.Configure the values to include log4j xml and reload delay.The init method can look something similar to the one below.

public void initialize() {
try {
try {
String logConfig = "D://temp//poc_log_config.xml";
long configReloadDelay = (long) (1 * 30 * 1000);
if (logConfig != null && (new java.io.File(logConfig).exists())) {
if (logConfig.endsWith("xml")) {
org.apache.log4j.xml.DOMConfigurator.configureAndWatch(
logConfig, configReloadDelay);
} else {
org.apache.log4j.PropertyConfigurator
.configureAndWatch(logConfig, configReloadDelay);
}
} else {
org.apache.log4j.BasicConfigurator.configure();
}
} catch (Exception initErr) {
initErr.printStackTrace();
}
} catch (Exception err) {
}

}


The log4j xml will look as follows

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">

<appender name="PRODUCT1" class="com.test.anyappender">
<param name="File" value="D:\\temp\\product1\\product1log.log"/>
<param name="MaxFileSize" value="100000000"/>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d{HH:mm:ss,SSS} - %m%n"/>
</layout>
<param name="Threshold" value="DEBUG"/>
</appender>

<appender name="PRODUCT2" class="com.test.anyAppender">
<param name="File" value="D:\\temp\\product2\\product2.log"/>
<param name="MaxFileSize" value="100000000"/>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d{HH:mm:ss,SSS} - %m%n"/>
</layout>
<param name="Threshold" value="DEBUG"/>
</appender>

<root>
<level value="OFF" />
</root>

<logger name="test1">
<appender-ref ref="PRODUCT1"/>
<level value="INFO" />
</logger>

<logger name="test2">
<appender-ref ref="PRODUCT2"/>
<level value="DEBUG" />
</logger>

</log4j:configuration>


Now in any bundle code you can access any component logger as follows

static final Logger TEST_LOGGER = Logger.getLogger("test1");

use TEST_LOGGER.debug("any debug message");

By the above approach we can just have a single log4j bundle in the repository and not only avoid issues occuring from static variables but also can take full benefit of log4j API's.

Extending OSGi telnet for custom commands

OSGi telnet command provided by equinox can be easily extended to implement custom commands.

First we need to write a class with which implements the custom commands.the methods should start with _ to be identified as osgi telnet commands.The getHelp command needs to be overwritten ,which will display the command when help is hit on the telnet console.

import org.eclipse.osgi.framework.console.CommandInterpreter;
import org.eclipse.osgi.framework.console.CommandProvider;

public class CustomCommands implements CommandProvider {
@Override
public String getHelp() {
return "***********CUSTOM COMMANDS*******************\n";
}

public void _testcommand(CommandInterpreter ci) {
ci.println("arg 1" + ci.nextArgument());
ci.println("arg 2" + ci.nextArgument());
}
}


Next step is to write a BundleActivator and register the command Provider service.

@Override
public void start(BundleContext ctx) throws Exception {
customCmd = new CustomCommands();
//created a instance of the implementation of our custom commands
ctx.registerService(CommandProvider.class.getName(),customCmd, null);

}

Dont forget to add the bundle activator class to the manifest. Manifest should include the following.

Import-Package: org.eclipse.osgi.framework.console;version="[1.0.0,1.0.0]",
org.osgi.framework;version="[1.4.0,1.4.0]"
Bundle-Activator: com.command.extender.CommandBundleActivator

JMX using Spring

JMX aims to create standard API's for managing and monitoring java applications.

Managing - changing values at runtime.

Monitoring - Reporting certain conditions.

Mbean -is a java bean with a management interface.

Mbean can expose attributes(through getters and setters) or any opertaions.Management interface can be defined statically or at runtime through interfaces.MBean Server acts as a broker between interacting beans or beans and remote clients.

Mbean server maintains a key to all registered means which is called as object name.The default protocol used is RMI , but can be changed to SOAP or Hessian.

Using raw JMX APi's is difficult , spring simplifies this.

Use the following configuration to create a MBeanServer. This will be required normally only if we are running standalone code.Else most of the containers like tomcat or spring dm provide the default MBeanServer which can be used to register the Mbeans.

<bean id="MBeanServer" class="org.springframework.jmx.support.MBeanServerFactoryBean">
<property name="locateExistingServerIfPosssible" value="true">
</bean>

Configuration to register a bean as MBean to MBeanServer.

<bean id="jmsSample" class="com.pg.MonitorService"></bean>

<bean class="org.springframework.jmx.export.MBeanExporter">
<property name="beans">
<util:map>
<entry key="service:name=jmsSample" value-ref="jmsSample" />
</util:map>
</property>

By this all methods and attributes of this can be accesed using jconsole. Sometimes this may not be what we want .we would want to control the set of operations which are visible through JMX.

The Spring MbeanExporter allows the following stratergies

1. ObjectnamingStratergy - this will help in identifying the mbean in the jconsole.

  • a. KeyNamingStratergy(Default) - key passed will become the name
  • b. IdentityNamingStratergy -generates a object name dynamically
  • c. MetaDataNamingStratergy - reads object name from source level meta-data (annotations)

2. MBeanInfoAssembler

When we want to control what we are exposing through JMX this is the better approach

  • SimpleReflectiveMbeanInfoAssembler -Default -Exposes all public methods and properties
  • MethodNameBasedMbeanInfoAssembler Methods can be configured through spring xmls which will be exposed through JMX.
  • MetadataMBeanInfoAssembler what needs to be exposed is controlled using source level annotations.
  • InterfaceBasedMBeanInfoAssembler
a. Using managed Interfaces property

<bean class="org.springframework.jmx.export.MBeanExporter">
<property name="beans">
<util:map>
<entry key="service:name=jmsSample" value-ref="jmsSample" />
</util:map>
</property>
<property name="assembler">
<bean
class="org.springframework.jmx.export.assembler.InterfaceBasedMBeanInfoAssembler">
<property name="managedInterfaces">
<value>com.pg.IService</value>
</property>
</bean>
</property>

</bean>

Here the jmsSample bean need not implement teh managedInterface.

b Using interfaceMapping property

<bean class="org.springframework.jmx.export.MBeanExporter">
<property name="beans">
<util:map>
<entry key="service:name=jmsSample" value-ref="jmsSample" />
</util:map>
</property>
<property name="assembler">
<bean
class="org.springframework.jmx.export.assembler.InterfaceBasedMBeanInfoAssembler">
<property name="interfaceMappings">
<util:map>
<entry key="service:name=jmsSampleService" value="com.pg.IService"></entry>
</util:map>
</property>

</property>

</bean>

Thea bean jmsSample must implement the interface mentioned in interfaceMapping property

This API helps in monitoring application using JMX.

http://jamonapi.sourceforge.net/

Thursday, November 12, 2009

Spring Training -Day 6

http://www.infoq.com/presentations/colyer-keynote-springone2gx

Telnet console 2401 – ssh shell -2402 in DM2 .

2.0 release will have both osgi console and dm shell (osgi console will be disabled by default)

Dm Kernel – lightweight – no web profile.

No plans to support EJB

Bundles in usr directory is not exposing service because – not in active state – need to get some code to move it to active state.

Bundles in pick up move to active state by default.

Import – org.osgi.framework – will not work , as jar is in lib directory .Work Around is to copy paste it into repository/bundles/usr.

Spring dm automatically creates a property for service by name – bean name.

Also application context itself is exposed as service – can be verified with osgi console

Logback – for logging in dm 2.0

http://logback.qos.ch/

In JVM failure - can be achieved using ranking attribute

Load time weaving – more complex when many bundles are there , so hardcoded in DM server - just below boot Classloader (this is not possible with aspectj being a bundle)

Require Bundle(used for Split packages)

Require bundle – all exported packages are imported

Deployment pipeline – Like wars and non osgi headers still works under spring DM because –this is processed by DM server to convert it into osgi complaint bundle - End result is visible in work directory

When Dm server starts all bundles are not seen in osgi console – They move to resolved state , only when required(looking to find required imports) they move to resolved state.

Osgi reference has bean name attribute which can be used as default filter.

Bundles have version , services have ranking – Service ranking is part of OSGi spec

Custom osgi commands – implement Command Provider – equinox specific

2.0 modules – has attribute - sticky for service.

Spring integration

Enterprise Integration Pattern –implementers are SI and Camel

http://www.ibm.com/developerworks/library/os-ecl-osgiconsole/index.html

Spring Training -Day 5

Create new platform (may be equinox 3.5) – eclipse may be using 3.4

Eclipse takes ini file – which when bundles added to a folder adds it to the file –Eclipse can do it easily.

Uses conflict may be ClassCastExceptions or Linkage Exceptions

http://blog.springsource.com/2008/11/22/diagnosing-osgi-uses-conflicts/

Uses is whatever the bundle is resolved to .(Ex If struts has import;log4j and in any Export blabla;uses log4j – no issues because it will resolves to log4j in applications classpath just forcing us to use the same log4j.. When uses is with version becomes very critical)

Ideally 3rd party guys should not be ideally using uses –unless they want to restrict , but still relaxed using import in struts lib without version – so whatever 3rd party resolves to in our lib.

Import without version no – resolves to latest.

Class loader performance may increase -steps reduced for loading from the conventional model.

Osgi command – packages - searches for the package in all bundles.

BundleContext- used to install/uninstall bundles, deal with events and services programmatically

When multiple services have same ranking – will be solved based on service id – least one – the first one registered.

Extender bundle

Spring extender bundle creates bundle context for bundles which are in ACTIVE state . May be this is the reason bundles in usr lib cannot exposes services.(because they don’t move to active state)

Application context for bundles are created asynchronously when DM server starts up. It can be changed to get a sync behaviour.

When synchronous, it does not spawn a new thread for creating context and waits until context is created.(If any bundle is picked up before by the extender that may run in a separate thread)

Appcontext fails – it uninstalls the bundle – hence cannot verify errors from osgi console.

Service mandatory wait is asynchronous.

Extender Bundle – is like SpringContextLoadListener in web Apps

Extender bundles creates appContext for all bundles which are in ACTIVE state.

Accessing BundleContext – implements BundleContextAware

Either put appcontext file in META-INF / spring or use Spring-Context header in manifest file.

Mandatory Service

Timeout

Cardinality - 1:1 ,0:1, 0:n,1:n – renamed as availability attribute in Dm 2.0

osgi: set and osgi:list

Consumers can listen to bind-unbind events

Publishers can register to register and unregister events.

RFC 124 – Moving towards component and service than spring specific osgi:service and bean .

 
Free Domain Names @ .co.nr!