You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@felix.apache.org by "Steven E. Harris" <se...@panix.com> on 2006/12/01 22:56:33 UTC

Building an application out of bundles: bootstrapping

It took me a day or so to realize that if one starts up Felix with no
felix.auto.install or felix.auto.start property specified, no bundles
can ever be added, as the only (other) way to install a bundle is with
a BundleContext, and the only way to get a BundleContext is with an
Activator's start() method. If we agree on that, then at least one
bundle must be added at startup for Felix to do anything useful.

I started experimenting with creating a "root" bundle that's the sole
thing specified in felix.auto.start, with this "root" bundle's job
being to capture a BundleContext and start up the rest of the
application. Several questions arose.

Using the felix.auto.(install|start) property causes a bundle to be
/installed/ from some URL into the cache. This means that each time
the application starts, some files are inspected and copied. If we're
talking about an application that will be run over and over, is it
sound to think of the cache as durable, meaning that once a bundle is
installed, it will be there the next time the program starts up? If
so, there's a difference between "seeding" a new cache and starting up
a program that's using an already-populated cache.

The first mode -- seeding a new cache, or re-seeding an existing cache
-- is what's shown in the sample Felix programs. It's easy to
understand, but it has a few problems. First, it keeps doing the same
inspecting and copying operations every time the program starts. It
requires that the startup bundles be sitting somewhere accessible --
either taking up space on the disk, or causing a network connection to
reach a remote resource -- even though the program has already
installed these bundles in its cache. Finally, it makes the program
keep on trying to install bundles with fixed names (including their
version number in the examples).

To avoid these problems, would it make sense to deliver a Felix-based
application with pre-populated cache? The set of bundles normally
specified in the felix.auto.(install|start) properties would be in the
cache, perhaps along with the their dependencies.

Maybe the best approach would be if the program could behave
differently depending on whether it's being started up with no cache
(creating one afresh) versus an existing cache. In the first case, it
would seed the cache with a few bundles, while in the second case it
wouldn't bother trying, assuming the necessary bundles are already
installed.

This could work if most of the bundles normally specified in the
felix.auto.(install|start) property were available on the disk as a
repository. The "root" bundle could start up and attempt to resolve
some initial requirements with a RepositoryAdmin and a Resolver,
deploying resources to satisfy any unmet requirements. Well, but in
order to even have a RepositoryAdmin, we'd need to have the OBR bundle
installed. Chicken-and-egg problem.

I'm just thinking out loud, but I'd like to hear anyone else's
thoughts on these matters.

-- 
Steven E. Harris

Re[2]: Building an application out of bundles: bootstrapping

Posted by Peter Kriens <Pe...@aQute.biz>.
In a dynamic system like OSGi one of the great errors you can make is
starting to control the initialization order. Due to update, things
can come and go at any time. All bundles should be written to assume
that their services could be there or not. The service binder, spring
or declarative services make this quite easy.

I have seen many projects desperately trying to control the
initialization order only to fall apart at the first update. It is
dangerous to try because it usually works in your development
environment but it becomes extremely brittle when you deploy and other
bundles start to interact as well.

A component model will only become truly reusable if components are
written in a robust way and not put unspecified requirements on the
framework.

Kind regards,

     Peter Kriens


SEH> "Richard S. Hall" <he...@ungoverned.org> writes:

>> The Felix.start() method accepts a list of BundleActivator
>> instances. These passed in activator instances effectively become
>> part of the system bundle and their start()/stop() methods will be
>> given the system bundle's context so that they can install bundles,
>> register services, etc.

SEH> Oh, I completely missed that. Thanks for pointing it out. Now Felix
SEH> makes a lot more sense to me, especially from the point of view of
SEH> embedding it in an application.

SEH> On the topic of providing an activator list to Felix.start(), I notice
SEH> that SystemBundleActivator's start() method starts up all the
SEH> BundleActivators in the order they fall in the m_activatorList list.

SEH> In SystemBundle's constructor, it /appends/ activators for
SEH> PackageAdmin, StartLevel, and URLHandler services to the list provided
SEH> by the caller to Felix.start(). This means that
SEH> SystemBundleActivator.start() starts up the user-provided bundles (or
SEH> just BundleActivators) before these three (for now) system-provided
SEH> BundleActivators.

SEH> While I understand that bundles must expect services they rely on to
SEH> come and go, it might make for an easier start up if the user-provided
SEH> BundleActivators were started /after/ the system-provided ones. That
SEH> is assuming that any user-provided bundles would rely on services or
SEH> capabilities provided by the PackageAdmin, StartLevel, or URLHandler
SEH> BundleActivators.

SEH> In code, prepending in reverse order:

SEH> ,----[ SystemBundle.SystemBundle(Felix, BundleInfo, List) ]
SEH> |         // Add the bundle activator for the URL Handlers service.
SEH> |         activatorList.add(0, new URLHandlersActivator(felix));
SEH> | 
SEH> |         // Add the bundle activator for the start level service.
SEH> |         activatorList.add(0, new StartLevelActivator(felix));
SEH> | 
SEH> |         // Add the bundle activator for the package admin service.
SEH> |         activatorList.add(0, new PackageAdminActivator(felix));
SEH> `----

SEH> Or, if the shuffling around of array elements is onerous, one could
SEH> use a LinkedList or write it this way, appending the provided list to
SEH> the system-built list:

SEH> ,----[ SystemBundle.SystemBundle(Felix, BundleInfo, List) ]
SEH> |         m_activatorList = new ArrayList(3 +
SEH> |                                         null == activatorList ? 0 :
SEH> |                                         activatorList.length);
SEH> | 
SEH> |         // Add the bundle activator for the package admin service.
SEH> |         m_activatorList.add(new PackageAdminActivator(felix));
SEH> | 
SEH> |         // Add the bundle activator for the start level service.
SEH> |         m_activatorList.add(new StartLevelActivator(felix));
SEH> | 
SEH> |         // Add the bundle activator for the URL Handlers service.
SEH> |         m_activatorList.add(new URLHandlersActivator(felix));
SEH> | 
SEH> |         if (activatorList != null)
SEH> |             m_activatorList.addAll(activatorList);
SEH> `----



-- 
Peter Kriens                              Tel +33467542167
9C, Avenue St. Drézéry                    AOL,Yahoo: pkriens
34160 Beaulieu, France                    ICQ 255570717
Skype pkriens                             Fax +1 8153772599


Re: Building an application out of bundles: bootstrapping

Posted by "Steven E. Harris" <se...@panix.com>.
"Richard S. Hall" <he...@ungoverned.org> writes:

> Yes, they could probably be appended, however, I don't really see
> this as critical, nor will it really resolve anything for you.

That's fine. I just happened to be browsing the code, looking at the
call paths for the BundleActivators passed in to Felix.start(), and
thought that this ordering may have been an accident. Perhaps leaving
it as will force programs even more strongly not to rely on
initialization order, as Peter suggested in a parallel reply.

-- 
Steven E. Harris

Re: Building an application out of bundles: bootstrapping

Posted by "Richard S. Hall" <he...@ungoverned.org>.
Steven E. Harris wrote:
> "Richard S. Hall" <he...@ungoverned.org> writes:
>
>   
>> The Felix.start() method accepts a list of BundleActivator
>> instances. These passed in activator instances effectively become
>> part of the system bundle and their start()/stop() methods will be
>> given the system bundle's context so that they can install bundles,
>> register services, etc.
>>     
>
> Oh, I completely missed that. Thanks for pointing it out. Now Felix
> makes a lot more sense to me, especially from the point of view of
> embedding it in an application.
>
> On the topic of providing an activator list to Felix.start(), I notice
> that SystemBundleActivator's start() method starts up all the
> BundleActivators in the order they fall in the m_activatorList list.
>
> In SystemBundle's constructor, it /appends/ activators for
> PackageAdmin, StartLevel, and URLHandler services to the list provided
> by the caller to Felix.start(). This means that
> SystemBundleActivator.start() starts up the user-provided bundles (or
> just BundleActivators) before these three (for now) system-provided
> BundleActivators.
>
> While I understand that bundles must expect services they rely on to
> come and go, it might make for an easier start up if the user-provided
> BundleActivators were started /after/ the system-provided ones. That
> is assuming that any user-provided bundles would rely on services or
> capabilities provided by the PackageAdmin, StartLevel, or URLHandler
> BundleActivators.
>   

Yes, they could probably be appended, however, I don't really see this 
as critical, nor will it really resolve anything for you. For example, 
URL Handlers can be disabled in the configuration file and I always 
thought that it would be a nice long-term goal if we could find a way to 
provide these services as framework extensions so that people could 
easily only deploy them if they need them. So, it is probably best to 
simply create your code to listen for the services appropriately.

-> richard


Re: Building an application out of bundles: bootstrapping

Posted by "Steven E. Harris" <se...@panix.com>.
"Richard S. Hall" <he...@ungoverned.org> writes:

> The Felix.start() method accepts a list of BundleActivator
> instances. These passed in activator instances effectively become
> part of the system bundle and their start()/stop() methods will be
> given the system bundle's context so that they can install bundles,
> register services, etc.

Oh, I completely missed that. Thanks for pointing it out. Now Felix
makes a lot more sense to me, especially from the point of view of
embedding it in an application.

On the topic of providing an activator list to Felix.start(), I notice
that SystemBundleActivator's start() method starts up all the
BundleActivators in the order they fall in the m_activatorList list.

In SystemBundle's constructor, it /appends/ activators for
PackageAdmin, StartLevel, and URLHandler services to the list provided
by the caller to Felix.start(). This means that
SystemBundleActivator.start() starts up the user-provided bundles (or
just BundleActivators) before these three (for now) system-provided
BundleActivators.

While I understand that bundles must expect services they rely on to
come and go, it might make for an easier start up if the user-provided
BundleActivators were started /after/ the system-provided ones. That
is assuming that any user-provided bundles would rely on services or
capabilities provided by the PackageAdmin, StartLevel, or URLHandler
BundleActivators.

In code, prepending in reverse order:

,----[ SystemBundle.SystemBundle(Felix, BundleInfo, List) ]
|         // Add the bundle activator for the URL Handlers service.
|         activatorList.add(0, new URLHandlersActivator(felix));
| 
|         // Add the bundle activator for the start level service.
|         activatorList.add(0, new StartLevelActivator(felix));
| 
|         // Add the bundle activator for the package admin service.
|         activatorList.add(0, new PackageAdminActivator(felix));
`----

Or, if the shuffling around of array elements is onerous, one could
use a LinkedList or write it this way, appending the provided list to
the system-built list:

,----[ SystemBundle.SystemBundle(Felix, BundleInfo, List) ]
|         m_activatorList = new ArrayList(3 +
|                                         null == activatorList ? 0 :
|                                         activatorList.length);
| 
|         // Add the bundle activator for the package admin service.
|         m_activatorList.add(new PackageAdminActivator(felix));
| 
|         // Add the bundle activator for the start level service.
|         m_activatorList.add(new StartLevelActivator(felix));
| 
|         // Add the bundle activator for the URL Handlers service.
|         m_activatorList.add(new URLHandlersActivator(felix));
| 
|         if (activatorList != null)
|             m_activatorList.addAll(activatorList);
`----

-- 
Steven E. Harris

Re: Building an application out of bundles: bootstrapping

Posted by "Richard S. Hall" <he...@ungoverned.org>.
Steven E. Harris wrote:
> It took me a day or so to realize that if one starts up Felix with no
> felix.auto.install or felix.auto.start property specified, no bundles
> can ever be added, as the only (other) way to install a bundle is with
> a BundleContext, and the only way to get a BundleContext is with an
> Activator's start() method. If we agree on that, then at least one
> bundle must be added at startup for Felix to do anything useful.
>   

This is not entirely accurate. The Felix.start() method accepts a list 
of BundleActivator instances. These passed in activator instances 
effectively become part of the system bundle and their start()/stop() 
methods will be given the system bundle's context so that they can 
install bundles, register services, etc.

> I started experimenting with creating a "root" bundle that's the sole
> thing specified in felix.auto.start, with this "root" bundle's job
> being to capture a BundleContext and start up the rest of the
> application. Several questions arose.
>
> Using the felix.auto.(install|start) property causes a bundle to be
> /installed/ from some URL into the cache. This means that each time
> the application starts, some files are inspected and copied. If we're
> talking about an application that will be run over and over, is it
> sound to think of the cache as durable, meaning that once a bundle is
> installed, it will be there the next time the program starts up? If
> so, there's a difference between "seeding" a new cache and starting up
> a program that's using an already-populated cache.
>
> The first mode -- seeding a new cache, or re-seeding an existing cache
> -- is what's shown in the sample Felix programs. It's easy to
> understand, but it has a few problems. First, it keeps doing the same
> inspecting and copying operations every time the program starts. It
> requires that the startup bundles be sitting somewhere accessible --
> either taking up space on the disk, or causing a network connection to
> reach a remote resource -- even though the program has already
> installed these bundles in its cache. Finally, it makes the program
> keep on trying to install bundles with fixed names (including their
> version number in the examples).
>   

I don't think this explanation (as I understand it) is completely 
accurate either. The installBundle() operation does not do any extra 
work (or at least not much) if a bundle is already installed. Installed 
bundles are indexed by their location, so on startup if the bundles in 
the autostart property are installed again, the only thing that happens 
is that the existing bundle object is discovered and returned.

The copying, etc. only happens when you create a new profile.

> To avoid these problems, would it make sense to deliver a Felix-based
> application with pre-populated cache? The set of bundles normally
> specified in the felix.auto.(install|start) properties would be in the
> cache, perhaps along with the their dependencies.
>   

Yes, I think it makes sense to deliver Felix-based applications with a 
pre-populated cache. However, this just depends. For some applications, 
you the user may be able to select different configurations when they 
start. The launcher in that case may select a set of bundles to 
autostart based on the selected configuration, etc. But both approaches 
could make sense depending on your application.

> Maybe the best approach would be if the program could behave
> differently depending on whether it's being started up with no cache
> (creating one afresh) versus an existing cache. In the first case, it
> would seed the cache with a few bundles, while in the second case it
> wouldn't bother trying, assuming the necessary bundles are already
> installed.
>   

This pretty much happens already, since nothing is done if you try to 
install a bundle a second time.

> This could work if most of the bundles normally specified in the
> felix.auto.(install|start) property were available on the disk as a
> repository. The "root" bundle could start up and attempt to resolve
> some initial requirements with a RepositoryAdmin and a Resolver,
> deploying resources to satisfy any unmet requirements. Well, but in
> order to even have a RepositoryAdmin, we'd need to have the OBR bundle
> installed. Chicken-and-egg problem.

In the end, you cannot get around the issue that something has to be 
installed in the framework. It can either be bundles using the autostart 
properties or it can be a custom launcher that uses some other mechanism 
to load bundles, such as the system bundle activator approach mentioned 
above.

-> richard