Skip to main content
Jump to: navigation, search

Difference between revisions of "Examples/Eclipse Business Expense Reporting Tool/User State Service"

Line 10: Line 10:
 
The notion of "current user" is more interesting in the multiple-user case of RAP. [http://dev.eclipse.org/viewcvs/index.cgi/org.eclipse.examples/ebert/org.eclipse.examples.expenses.application.rap/src/org/eclipse/examples/expenses/context/rap/RapUserContextService.java?root=Technology_Project&view=markup RapUserContextService] uses the HTTP Session state, accessible through APIs on RAP's RWT type. This returns for us an instance of [http://dev.eclipse.org/viewcvs/index.cgi/org.eclipse.examples/ebert/org.eclipse.examples.expenses.application.rap/src/org/eclipse/examples/expenses/context/rap/RapUserContext.java?root=Technology_Project&view=markup 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 notion of "current user" is more interesting in the multiple-user case of RAP. [http://dev.eclipse.org/viewcvs/index.cgi/org.eclipse.examples/ebert/org.eclipse.examples.expenses.application.rap/src/org/eclipse/examples/expenses/context/rap/RapUserContextService.java?root=Technology_Project&view=markup RapUserContextService] uses the HTTP Session state, accessible through APIs on RAP's RWT type. This returns for us an instance of [http://dev.eclipse.org/viewcvs/index.cgi/org.eclipse.examples/ebert/org.eclipse.examples.expenses.application.rap/src/org/eclipse/examples/expenses/context/rap/RapUserContext.java?root=Technology_Project&view=markup 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 [http://dev.eclipse.org/viewcvs/index.cgi/org.eclipse.examples/ebert/org.eclipse.examples.expenses.views/src/org/eclipse/examples/expenses/views/AbstractView.java?root=Technology_Project&view=markup AbstractView] class (which is the superclass for all the EBERT views) as follows:
+
The user context provides a view model for the current user along with other useful information such as the user-specific locale. The current implementations are also responsible for managing CRUD operations on the domain model. This will change with the introduction of the notion of a persistence service.
  
<source lang="java">protected void startUserContextServiceTracker() {
+
The user context service is obtained by clients using an OSGi ServiceTracker. An example of this can be found in the <code>startUserContextServiceTracker()<code> method in the [http://dev.eclipse.org/viewcvs/index.cgi/org.eclipse.examples/ebert/org.eclipse.examples.expenses.views/src/org/eclipse/examples/expenses/views/AbstractView.java?root=Technology_Project&view=markup AbstractView] class (which is the superclass for all the EBERT views). ''This code will be greatly simplified when we change it to leverage OSGi Declarative Services.'' The <code>userContextServiceTracker</code> is notified, via the <code>addingService</code> method whenever a service is added or removed. Our implementation is greedy: it takes the first service that it finds. When this first service presents itself, the instance is notified via the <code>connectToUserContext(...)</code> method. Likewise, when the service that we greedily obtained is removed, the instance is notified via the <code>disconnectFromUserContext(...)</code> method. This allows the application to be very dynamic as the state of the services changes; the views dynamically configure themselves based on the availability of a user context service.
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) {
+
syncExec(new Runnable() {
+
public void run() {
+
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();
+
}</source>
+
 
+
This code will be greatly simplified when we change it to leverage OSGi Declarative Services.
+
 
+
The <code>userContextServiceTracker</code> is notified, via the <code>addingService</code> method whenever a service is added or removed. Our implementation is greedy: it takes the first service that it finds. When this first service presents itself, the instance is notified via the <code>connectToUserContext(...)</code> method. Likewise, when the service that we greedily obtained is removed, the instance is notified via the <code>disconnectFromUserContext(...)</code> method.
+
  
 
The <code>syncExec</code> blocks bear some discussion. This ServiceTracker will be invoked by threads other than the UI thread that owns the view instance. Since we may be running in RAP, we need to make sure that when we obtain the user context, we get the right one. By wrapping the call to <code>getUserContext</code> inside a <code>syncExec</code> block, we can be sure that the code that obtains the user context is running in the right user's UI thread. The <code>syncExec</code> method is a convenience method that obtains the receiver's SWT Display and invokes <code>syncExec</code> on it.
 
The <code>syncExec</code> blocks bear some discussion. This ServiceTracker will be invoked by threads other than the UI thread that owns the view instance. Since we may be running in RAP, we need to make sure that when we obtain the user context, we get the right one. By wrapping the call to <code>getUserContext</code> inside a <code>syncExec</code> block, we can be sure that the code that obtains the user context is running in the right user's UI thread. The <code>syncExec</code> method is a convenience method that obtains the receiver's SWT Display and invokes <code>syncExec</code> on it.

Revision as of 16:26, 26 February 2009

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 provides a view model for the current user along with other useful information such as the user-specific locale. The current implementations are also responsible for managing CRUD operations on the domain model. This will change with the introduction of the notion of a persistence service.

The user context service is obtained by clients using an OSGi ServiceTracker. An example of this can be found in the startUserContextServiceTracker()<code> method in the AbstractView class (which is the superclass for all the EBERT views). This code will be greatly simplified when we change it to leverage OSGi Declarative Services. The <code>userContextServiceTracker is notified, via the addingService method whenever a service is added or removed. Our implementation is greedy: it takes the first service that it finds. When this first service presents itself, the instance is notified via the connectToUserContext(...) method. Likewise, when the service that we greedily obtained is removed, the instance is notified via the disconnectFromUserContext(...) method. This allows the application to be very dynamic as the state of the services changes; the views dynamically configure themselves based on the availability of a user context service.

The syncExec blocks bear some discussion. This ServiceTracker will be invoked by threads other than the UI thread that owns the view instance. Since we may be running in RAP, we need to make sure that when we obtain the user context, we get the right one. By wrapping the call to getUserContext inside a syncExec block, we can be sure that the code that obtains the user context is running in the right user's UI thread. The syncExec method is a convenience method that obtains the receiver's SWT Display and invokes syncExec on it.

Copyright © Eclipse Foundation, Inc. All Rights Reserved.