How to provision OSGi services per client
Asked Answered
D

3

7

We are developing a web-application (lets call it an image bank) for which we have identified the following needs:

  • The application caters customers which consist of a set of users.
  • A new customer can be created dynamically and a customer manages it's users
  • Customers have different feature sets which can be changed dynamically
  • Customers can develop their own features and have them deployed.
  • The application is homogeneous and has a current version, but version lifting of customers can still be handled individually.
  • The application should be managed as a whole and customers share the resources which should be easy to scale.

Question: Should we build this on a standard OSGi framework or would we be better of using one of the emerging application frameworks (Virgo, Aries or upcoming OSGi standard)?

More background and some initial thoughts:

We're building a web-app which we envision will soon have hundreds of customers (companies) with hundreds of users each (employees), otherwise why bother ;). We want to make it modular hence OSGi. In the future customers themselves might develop and plugin components to their application so we need customer isolation. We also might want different customers to get different feature sets.

What's the "correct" way to provide different service implementations to different clients of an application when different clients share the same bundles?

We could use the app-server approach (we've looked at Virgo) and load each bundle once for each customer into their own "app". However it doesn't feel like embracing OSGi. We're not hosting a multitude of applications, 99% of the services will share the same impl. for all customers. Also we want to manage (configure, monitor etc.) the application as one.

Each service could be registered (properly configured) once for each customer along with some "customer-token" property. It's a bit messy and would have to be handled with an extender pattern or perhaps a ManagedServiceFactory? Also before registering a service for customer A one will need to acquire the A-version of each of it's dependencies.

The "current" customer will be known to each request and can be bound to the thread. It's a bit of a mess having to supply a customer-token each time you search for a service. It makes it hard to use component frameworks like blueprint. To get around the problem we could use service hooks to proxy each registered service type and let the proxy dispatch to the right instance according to current customer (thread).

Beginning our whole OSGi experience by implementing the workaround (hack?) above really feels like an indication we're on the wrong path. So what should we do? Go back to Virgo? Try something similar to what's outlined above? Something completely different?!

ps. Thanks for reading all the way down here! ;)

Doby answered 7/2, 2011 at 22:56 Comment(1)
I edited the question to make it more concrete so it should be easier to accept an answer, which I really want to do! I'm quite new to stackoverflow so excuse me for being a bit clumsy...Doby
D
5

There are a couple of aspects to a solution:

First of all, you need to find a way to configure the different customers you have. Building a solution on top of ConfigurationAdmin makes sense here, because then you can leverage the existing OSGi standard as much as possible. The reason you might want to build something on top is that ConfigurationAdmin allows you to configure each individual service, but you might want to add a layer on top so you can more conveniently configure your whole application (the assembly of bundles) in one go. Such a configuration can then be translated into the individual configurations of the services.

Adding a property to services that have customer specific implementations makes a lot of sense. You can set them up using a ManagedServiceFactory, and the property makes it easy to lookup the service for the right customer using a filter. You can even define a fallback scenario where you either look for a customer specific service, or a generic one (because not all services will probably be customer specific). Since you need to explicitly add such filters to your dependencies, I'd recommend taking an existing dependency management solution and extending it for your specific use case so dependencies automatically add the right customer specific filters without you having to specify that by hand. I realize I might have to go into more detail here, just let me know...

The next question then is, how to keep track of the customer "context" within your application. Traditionally there are only a few options here, with a thread local context being the most used one. Binding threads to customers does tend to limit you in terms of implementation options though, as in general it probably means you have to prohibit developers from creating threads themselves, and it's hard to off-load certain tasks to pools of worker threads. It gets even worse if you ever decide to use Remote Services as that means you will completely loose the context.

So, for passing on the customer identification from one component to another, I personally prefer a solution where:

  1. As soon as the request comes in (for example in your HTTP servlet) somehow determine the customer ID.
  2. Explicitly pass on that ID down the chain of service dependencies.
  3. Only use solutions like the use of thread locals within the borders of a single bundle, if for example you're using a third party library inside your bundle that needs this to keep track of the customer.
Dobb answered 9/2, 2011 at 9:10 Comment(3)
It's nice to read your answer because it's pretty much in line with what I've originally thought. But what about security? If we continue on the road of all bundles equal on standard OSGi framework, won't it eventually be very hard to keep the separation? For instance we must really guard the "customer service token" if that is leaked it would be really easy to access another customers data.. Please also develop your ideas on extending another component framework.Doby
Regarding security, you at least have to put up a barrier for incoming HTTP requests. That aspect of securing an application has little to do with OSGi, you'd have to do it anyway. The second question is, do you want to enforce certain security constraints within the OSGi framework? Ultimately, if you do, you need to use the security part of the OSGi specification (not enabled by default, a separate extension for Apache Felix for example). Regarding the customer security token, if that's something you intend to expose outside of OSGi, it definitely needs to be guarded.Dobb
Regarding extending another component framework (I'm assuming you're talking about dependency management) I would for example take the Apache Felix Dependency Manager, which uses a Java API to declare component dependencies, and build a layer on top of that. You can use your own XML format, or annotations, or even another Java API that would automate much of the dependency generation (for example, adding filters on customer contexts). Note that I wrote that dependency manager, so obviously I'm biased. :)Dobb
I
1

I've been thinking about this same issue (I think) for some time now, and would like your opinions on the following analogy.

Consider a series of web application where you provide access control using a single sign-on (SSO) infrastructure. The user authenticates once using the SSO-server, and - when a request comes in - the target web application asks the SSO server whether the user is (still) authenticated and determines itself if the user is authorized. The authorization information might also be provided by the SSO server as well.

Now think of your application bundles as mini-applications. Although they're not web applications, would it still not make sense to have some sort of SSO bundle using SSO techniques to do authentication and to provide authorization information? Every application bundle would have to be developed or configured to use the SSO bundle to validate the authentication (SSO token), and validate authorization by asking the SSO bundle if the user is allowed to access this application bundle.

The SSO bundle maintains some sort of session repository, and also provides user properties, e.g. information to identify the data repository (of some sort) of this user. This way you also wouldn't pass trough a (meaningful) "customer service token", but rather a cryptic SSO-token that is supplied and managed by the SSO bundle.

Insignificance answered 9/2, 2011 at 22:2 Comment(1)
Yes, but the real question is how do you map this to the OSGi model? I see it as two regions. On one side your extensions to the framework i.e. controlling service visibility with ServiceHooks and proxying services. And on the other side the application bundles. You wan't the interface in between these regions to be as unencumbered and clean OSGi as possible. I've thought about many solutions to the "service provisioning" problem where in the end I realize I've more or less designed my own service registry and would have to handle a lot of the dynamics myself.Doby
O
1

Please not that Virgo is an OSGi container based on Equinox, so if you don't want to use some Virgo-specific feature, you don't have to. However, you'll get lots of benefits if you do use Virgo, even for a basic OSGi application. It sounds, though, like you want web support, which comes out of the box with Virgo web server and will save you the trouble of cobbling it together yourself.

Full disclosure: I lead the Virgo project.

Overcast answered 13/2, 2011 at 3:52 Comment(2)
Wow, you should be the perfect man to answer! Take a data provider service for instance. In the Virgo-model all it's classes would be loaded separately for each customer, when in reality (dynamic) configuration is the only thing differing between the service instances. Is there a way in Virgo to circumvent that and still get customer separation of service instances?Doby
I don't think Virgo, or OSGi for that matter, forces you to load all the classes separately for each customer. I'm no application developer, but I would have thought you could provide a general service factory and then have each customer instantiate a separate service based on their requirements.Overcast

© 2022 - 2024 — McMap. All rights reserved.