User scoped OSGi services

This post describes a possible way to handle and access stateful user scoped OSGi services in an vaadin environment. Of course, we would like to use the powerful dynamics provided by OSGi DS (declarative services). The provided classes and services should be highly extendable and it should be really easy to change the behaviour of the system.

But if you are dealing with user sessions you have to ensure, that some of the used services are session or user based. Which means, that exactly one service instance per user session or user exists. If you want to affect the instantiation process of OSGi services, you have to deal with ComponentFactories.

A ComponentFactory gives you the opportunity to create many instances of the same service with different properties. So we have a tooling to do exactly what we want to do.

But what kind of architecture would be the best one? For the last two weeks i tried different approaches and I’d like to present that one i like most.

To keeps things simple, i am going create a “shopping cart” example. Each user should have it’s own shopping cart associated by its user id. Many web sessions can share one shopping cart for one user id.

Defining the service

So lets start.
I am going to prepare an interface for the shopping cart which should be provided as an user scoped service.

IShoppingCart

public interface IShoppingCart {

/**
* Returns the user id this cart is responsible for.
*
* @return
*/
String getUserid();

/**
* Adds an item to the shopping cart.
*
* @param item
*/
void addItem(IItem item);

/**
* Returns the total value in the base currency of the user.
*
* @return
*/
float getTotalValue();

}

ShoppingCart implementation

In the next step i am creating an implementation for that interface and provide it as a “Component Factory Service”.

public class ShoppingCart implements IShoppingCart {

private String userId;

/**
* Called by OSGi DS on service activate
*/
void startup(Map<String, Object> properties) {
userId = (String) properties.get("userId");
}

@Override
public String getUserid() {
return userId;
}

@Override
public void addItem(IItem item) {
// to be implemented
}

@Override
public float getTotalValue() {
// to be implemented
return 0;
}

}

Component Factory Definition

As explained above, i am going to create a ComponentFactory which is used to create many instances of that service.

Since the created IShoppingCart service will be associated with an user id, i am adding a property called user id to service definition.

Attention: This property will be changed before the session based service is created. So i gave it a value called “ToBeDefinedAtRuntime” which should visualize that this property exists, but its value has to be redefined.

The xml tag “activate” from the ComponentFactoryDefinition targets a method called startup. This method will be called after the session based service has been created. As an argument the properties which have been used to create that service are passed. And for sure, these properties are containing the user id of the user this shopping cart is associated with.

Writing a scoped service manager

Well, we have created a shopping cart service and a component factory definition. But for sure, we want to access our shopping cart instance by user id. If an instance for a given user id exists, we want to reuse it. Otherwise we prepare a new instance of the shopping cart service and configure it with the user id property.

IShoppingCartManager

So let’s write a ShoppingCartManager. This manager is an OSGi service and we use it to cache all created shopping cart services. This manager offers a method to access a shopping cart by an user id and a method to dispose the created service.

public interface IShoppingCartManager {

/**
* Returns a shopping cart for the user.
*
* @param userId
* @return
*/
IShoppingCart getShoppingCart(String userId);

/**
* Has to be called if the shopping cart should
* be destroyed. For instance if the user
* account was deleted.
*
* @param userId
*/
void disposeCart(String userId);

}

ShoppingCartManager implementation

public class ShoppingCartManager implements IShoppingCartManager {

private ComponentFactory factory;
private Map<String, ComponentInstance> shoppingCarts =
      Collections.synchronizedMap(
      new HashMap<String, ComponentInstance>());

/**
* Called by OSGi DS
*
* @param factory
*/
void bindShoppingCartFactory(ComponentFactory factory) {
     this.factory = factory;
}

/**
* Called by OSGi DS
*
* @param factory
*/
void unbindShoppingCartFactory(ComponentFactory factory) {
     this.factory = null;
}

@Override
public IShoppingCart getShoppingCart(String userId) {
    if (!shoppingCarts.containsKey(userId)) {
    // create a new instance
    //
    shoppingCarts.put(userId,
    newInstance(userId));
}

    // return the instance
    //
    ComponentInstance instance =
       shoppingCarts.get(userId);
    return (IShoppingCart) instance.getInstance();
}

private ComponentInstance newInstance(String userId) {
    // create properties with user id
    //
    Hashtable<String, Object> properties =
       new Hashtable<String, Object>();
       properties.put("userId", userId);

    // create a new instance
    //
    ComponentInstance instance =
      factory.newInstance(properties);
    return instance;
}

@Override
public void disposeCart(String userId) {
    if (shoppingCarts.containsKey(userId)) {
      ComponentInstance instance =
        shoppingCarts.remove(userId);
      instance.dispose();
     }
}
}

ShoppingCartManager service definition

And the service component definition.

How does it work?

The following images show you how this manager uses the component factory to create new instance of the shopping cart service.

Summary

So far we have prepared a service and a service manager.

  • The services for the shopping carts are
    • scoped by user id
    • cached by the shopping cart service manager
  • The manager allows you to access the cached services by the user id
  • If no service exists yet, the manager creates a new instance configured with the user id

Consume the service

The last thing you have to do, is to use the service manager inside your code.

public class MyService implements IMyService {

/**
* Called by OSGi DS
*
* @param manager
*/
@SuppressWarnings("unused")
void bindShoppingCartManager(IShoppingCartManager manager) {
  IShoppingCart cart1 = manager.getShoppingCart("user1");
  IShoppingCart cart2 = manager.getShoppingCart("user2");
}

/**
* Called by OSGi DS
*
* @param manager
*/
void unbindShoppingCartManager(IShoppingCartManager manager) {

}
}

Service Overview

This image shows an overview about the services created above and their relations.

Conclusion

If you have any suggestions please let me know. Any comments are highly welcome.

Advertisements

3 Responses to “User scoped OSGi services”

  1. Niels Nuyttens Says:

    Great article, very detailed. Will follow up on your blog!

    Thanks!

  2. Florian Pirchner Says:

    Thanks a lot!

    Florian

  3. Hendy Irawan Says:

    If we got a bunch of these scoped services, it gets unwieldy very quickly.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s


%d bloggers like this: