Skip to main content
Jump to: navigation, search

Examples/Eclipse Business Expense Reporting Tool/User State Service

This page is under development

Managing the user state in a standalone application is relatively easy. EBERT runs as both a standalone application (RCP, ERCP) and as a multiple-user application (RAP). To support both environments, we have to be careful how we store user state.

EBERT defines an Equinox/OSGi Service of type IUserContextService.java along with two different implementations. Implementations of this type are responsible for one thing: answer, when asked, the user state (an instance of IUserContext) for the current user.

Note that a notion of user state is being explored by the e4 effort.

The notion of "current user" is pretty simple in the standalone case: there is only one user. The StandaloneUserContextService simply returns an instance of StandaloneUserContext that it holds in a field. The activator for the bundle containing these types registers the service on bundle startup and deregisters it on shutdown.

The notion of "current user" is more interesting in the multiple-user case of RAP. RapUserContextService uses the HTTP Session state, accessible through APIs on RAP's RWT type. This returns for us an instance of RapUserContext that is specific to the user tied to the current thread. As is the case with the standalone variant, this service is registered and deregistered by the bundle activator.

The user context service is obtained by clients using an OSGi ServiceTracker. An example of this can be found in the AbstractView class (which is the superclass for all the EBERT views) as follows:

	protected void startUserContextServiceTracker() {
		userContextServiceTracker = new ServiceTracker(ExpenseReportingUI.getDefault().getContext(), IUserContextService.class.getName(), null) {
			/**
			 * We keep track of the first service we find and ignore the
			 * rest. This is a great example of where declarative services
			 * would be helpful: you can declare that you want exactly one
			 * instance of a service and that's what you get. 
			 */
			protected IUserContextService userContextService;
 
			/**
			 * This method is called when a matching service is found or
			 * added. This finds both pre-existing and new instances of the
			 * service.
			 */
			public Object addingService(ServiceReference reference) {
				Object service = super.addingService(reference);
				if (userContextService == null) {
					userContextService = (IUserContextService)service;
					/*
					 * Do the part where we get the user context in a
					 * syncExec block. This will make sure that it runs
					 * in the user interface thread for the current user.
					 * This doesn't matter too much on RCP/eRCP, but the
					 * thread that we're running in is pretty critical
					 * in RAP.
					 */
					syncExec(new Runnable() {
						public void run() {
							userContext = userContextService.getUserContext();
							connectToUserContext(userContext);
						}
					});
				}
				return service;
			}
 
			/**
			 * This method is called when the service is being removed, or the 
			 * tracker is being closed.
			 */
			public void removedService(ServiceReference reference, Object service) {
				if (service == userContextService) {
					disconnectFromUserContext(userContext);
					userContext = null;
					userContextService = null;
					// TODO Do we try to match up with a hypothetical second service in this case?
				}
				super.removedService(reference, service);
			};
 
 
		};
		userContextServiceTracker.open();
	}

Back to the top