You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@camel.apache.org by ddewaele <dd...@gmail.com> on 2012/11/15 17:53:20 UTC

Camel as an OSGI bundle - OSGI detection strategy

Hi,

I'm deploying Camel as an OSGI bundle (part of an EBA) on Websphere using
the Spring OSGI extender.
When the bundle is started, the camelContext is started and the route is up
and running.

I did notice however that it is not detecting that it is running in an OSGI
environment and as such is not using the OsgiSpringCamelContext.

This results in issues like TypeConverter package scanning not working in
the OSGI container.

Camel requires that the camel-spring bundle is STARTED before any bundles
are started containing camel routes.

What is the preferred way of dealing with this start-order dependency ?





--
View this message in context: http://camel.465427.n5.nabble.com/Camel-as-an-OSGI-bundle-OSGI-detection-strategy-tp5722787.html
Sent from the Camel - Users mailing list archive at Nabble.com.

Re: Camel as an OSGI bundle - OSGI detection strategy

Posted by Donald Whytock <dw...@gmail.com>.
On Fri, Nov 16, 2012 at 1:17 PM, Donald Whytock <dw...@gmail.com> wrote:
> Sorry, my project site was recently hacked.  I'll have to find my
> archives.  Ping me in a week if I haven't responded, please.

Okay...here's some hastily-thrown-together, untested code samples that
might nevertheless tell you what I mean.  It's a very barebones
example.

WaitForStart and NotifyOnStart are general-purpose interfaces to set
up multiple bundles to wait for a single bundle to activate them.  An
implemented WaitForStart accepts an OSGI ServiceReference to a
NotifyOnStart service that should be called when it's time for things
to start; the interface is structured so that the NotifyOnStart
implementation doesn't care what the WaitForStart's start condition
is, and the WaitForStart doesn't care what the NotifyOnStart will do
once started.

StartBundle implements WaitForStart and registers itself as a service
that other bundles can find.  It also implements BundleListener and
registers itself to receive BundleEvents.  When the desired bundle is
started, StartBundle will set a flag and call notifyOnStart() for each
active NotifyOnStart reference it has on file.  When a bundle calls
StartBundle's waitForStart(), StartBundle will either call the
supplied notifyOnStart() if the flag is set, or else store the
NotifyOnStart in a list to be called when the desired bundle is
started.

SampleApp implements NotifyOnStart and registers itself as a service
that can be passed to other bundles.  It also implements
ServiceListener and registers itself to receive ServiceEvents related
to instances of WaitForStart.  When a WaitForStart service is
registered, SampleApp fetches the WaitForStart service and calls
waitForStart(), passing its own ServiceReference.

Note that this example is specifically made for multiple bundles
waiting on a single event, and isolating the nature of that event from
them into a single coordinating bundle.  You can also just implement
BundleListener on some single bundle that sets up all your routes for
you.  Just wanted to provide the flexibility.

---------------------------

package org.mypackage.waitforstart.service;

import org.osgi.framework.ServiceReference;

public interface WaitForStart
  {
  public void waitForStart(ServiceReference<NotifyOnStart> startListener);
  }

------------------------

package org.mypackage.waitforstart.service;

public interface NotifyOnStart
  {
  public void notifyOnStart();
  }

----------------------

package org.mypackage.startbundle;

import org.mypackage.waitforstart.service.WaitForStart;
import org.mypackage.waitforstart.service.NotifyOnStart;

import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleEvent;
import org.osgi.framework.BundleListener;
import org.osgi.framework.ServiceReference;

import java.util.ArrayList;

public class StartBundle implements BundleActivator, BundleListener,
WaitForStart
  {
  ArrayList<ServiceReference<NotifyOnStart>> notifyList = new
ArrayList<ServiceReference<NotifyOnStart>>();
  String BUNDLENAME = "bundle-name";
  BundleContext context = null;
  boolean started = false;

  public void start(BundleContext context)
    {
    this.context = context;
    context.addBundleListener(this);
    context.registerService(WaitForStart.class.getName(), this, null);
    }

  public void waitForStart(ServiceReference<NotifyOnStart> notifyonstart)
    {
    if (started)
      {
      NotifyOnStart notifier = context.getService(notifyonstart);
      if (notifier != null)
        notifier.notifyOnStart();
      }
    else
      notifyList.add(notifyonstart);
    }

  public void bundleChanged(BundleEvent event)
    {
    NotifyOnStart notifier = null;

    if (event.getBundle().getSymbolicName().equals(BUNDLENAME))
      if (event.getType() == BundleEvent.STARTED)
        {
        started = true;

        for (ServiceReference reference: notifyList)
          {
          notifier = (NotifyOnStart) context.getService(reference);
          if (notifier != null)
            notifier.notifyOnStart();
          }
        }
    }

  public void stop(BundleContext context)
    {
    }
  }

-----------------------------

package org.mypackage.sampleapp;

import org.mypackage.waitforstart.service.NotifyOnStart;
import org.mypackage.waitforstart.service.WaitForStart;

import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.ServiceEvent;
import org.osgi.framework.ServiceListener;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.ServiceRegistration;

public class SampleApp implements BundleActivator, NotifyOnStart,
ServiceListener
  {
  ServiceRegistration registration = null;
  BundleContext context = null;
  ServiceReference<NotifyOnStart> reference = null;
  boolean started = false;

  public void start(BundleContext context)
    {
    this.context = context;

    registration =
context.registerService(NotifyOnStart.class.getName(), this, null);
    reference = registration.getReference();

    context.addServiceListener(this, "(" + Constants.OBJECTCLASS + "="
+ WaitForStart.class.getName() + ")");
    }

  public void serviceChanged(ServiceEvent event)
    {
    if (event.getType() == ServiceEvent.REGISTERED)
      context.getService(event.getServiceReference()).waitForStart(reference);
    }

  public void notifyOnStart()
    {
    if (!started)
      /* start routes here */

    started = true;
    }

  public void stop(BundleContext context)
    {
    registration.unregister();
    }
  }

Re: Camel as an OSGI bundle - OSGI detection strategy

Posted by Donald Whytock <dw...@gmail.com>.
On Fri, Nov 16, 2012 at 9:13 AM, ddewaele <dd...@gmail.com> wrote:
> Donald Whytock wrote
>> The preferred way in OSGI is having a BundleListener listen for a
>> STARTED event.  You'd put this in the bundles that are trying to
>> create routes.
>
> As soon as my bundle containing the camel route is started, the Spring OSGI
> extender kicks in, and immediately refreshes my spring context (resulting in
> the creation of the non-osgi camel context if the camel-spring bundle wasn't
> started). I don't see a hook where I can influence this behavior.
>
> Putting the listener in my bundle means that the bundle start event for
> camel-spring will be delivered when my bundle is starting (=too late).

This depends on how you're starting your routes.  If you're starting
them with Spring XML, I personally can't help you, but others might.
But if you're starting them programatically, you can have the code
that starts the routes be triggered by a BundleListener event.  Your
bundle can start, and defer starting your routes until the event is
triggered.

> Donald Whytock wrote
>> If you have a number of bundles dependent in this way, you might want
>> to create a bundle that does the listening for the camel-spring bundle
>> and publishes a service the other bundles submit custom listeners to.
>> That reduces, or at least centralizes, your dependence on extenal
>> events.
>
> Do you have some sample code or a pointer to some documentation regarding
> this approach ?

Sorry, my project site was recently hacked.  I'll have to find my
archives.  Ping me in a week if I haven't responded, please.

> Most of the camel osgi samples are being run on equinox / karaf , where
> there is some level of control of how and when bundles are started (as they
> are started from the console).
>
> In the case of an EBA file, it's the container that loads them so there is
> lesser control (and these bundle start order dependencies should be avoided
> offcourse).

Bundle start order dependence is hard to avoid in osgi.  How I
understood it is that if you're dependent on an external bundle or
service you need to be sensitive to whether or not the dependency is
there or if it suddenly goes away.  I may have taken this a bit far in
some of my own code, but I got involved in OSGI and Apache Felix
specifically to give myself hot-swap capacity.

Don

Re: Camel as an OSGI bundle - OSGI detection strategy

Posted by ddewaele <dd...@gmail.com>.
Donald Whytock wrote
> The preferred way in OSGI is having a BundleListener listen for a
> STARTED event.  You'd put this in the bundles that are trying to
> create routes.

As soon as my bundle containing the camel route is started, the Spring OSGI
extender kicks in, and immediately refreshes my spring context (resulting in
the creation of the non-osgi camel context if the camel-spring bundle wasn't
started). I don't see a hook where I can influence this behavior.

Putting the listener in my bundle means that the bundle start event for
camel-spring will be delivered when my bundle is starting (=too late).


Donald Whytock wrote
> If you have a number of bundles dependent in this way, you might want
> to create a bundle that does the listening for the camel-spring bundle
> and publishes a service the other bundles submit custom listeners to.
> That reduces, or at least centralizes, your dependence on extenal
> events.

Do you have some sample code or a pointer to some documentation regarding
this approach ? 

Most of the camel osgi samples are being run on equinox / karaf , where
there is some level of control of how and when bundles are started (as they
are started from the console). 

In the case of an EBA file, it's the container that loads them so there is
lesser control (and these bundle start order dependencies should be avoided
offcourse).

At the moment I don't see how this can be handled in a proper way.


Donald Whytock wrote
> It's possible to do this using Camel without Spring also, but you'll
> need to listen for the publishing of the appropriate endpoint module
> services also.  Starting the Camel bundle isn't enough; some modules
> start after and register themselves with camel-core.

We're currently using Spring, and cannot remove this dependency.

One downside of not having the OSGI based Camel Context is the fact that the
TypeConverter package scanning doesn't work in OSGI. Other than that I
didn't see any issues so far. But I can imagine further down the line,
working with a non OSGI based Camel Context in an OSGI container will get us
into trouble.





--
View this message in context: http://camel.465427.n5.nabble.com/Camel-as-an-OSGI-bundle-OSGI-detection-strategy-tp5722787p5722864.html
Sent from the Camel - Users mailing list archive at Nabble.com.

Re: Camel as an OSGI bundle - OSGI detection strategy

Posted by Donald Whytock <dw...@gmail.com>.
On Thu, Nov 15, 2012 at 11:53 AM, ddewaele <dd...@gmail.com> wrote:
> Hi,
>
> I'm deploying Camel as an OSGI bundle (part of an EBA) on Websphere using
> the Spring OSGI extender.
> When the bundle is started, the camelContext is started and the route is up
> and running.
>
> I did notice however that it is not detecting that it is running in an OSGI
> environment and as such is not using the OsgiSpringCamelContext.
>
> This results in issues like TypeConverter package scanning not working in
> the OSGI container.
>
> Camel requires that the camel-spring bundle is STARTED before any bundles
> are started containing camel routes.
>
> What is the preferred way of dealing with this start-order dependency ?

The preferred way in OSGI is having a BundleListener listen for a
STARTED event.  You'd put this in the bundles that are trying to
create routes.

http://www.osgi.org/javadoc/r4v43/core/org/osgi/framework/BundleListener.html

If you have a number of bundles dependent in this way, you might want
to create a bundle that does the listening for the camel-spring bundle
and publishes a service the other bundles submit custom listeners to.
That reduces, or at least centralizes, your dependence on extenal
events.

It's possible to do this using Camel without Spring also, but you'll
need to listen for the publishing of the appropriate endpoint module
services also.  Starting the Camel bundle isn't enough; some modules
start after and register themselves with camel-core.

Don