Common-scope
common-scope is a client library that provides facilities to manage the scope of calls to gCube services, including:
- facilities to propagate scope across client components and across threads in which those components execute;
- facilities to inspect scopes;
- facilities to discover endpoints of key services that operate in given scopes;
common-scope
is available in our Maven repositories with the following coordinates:
<groupId>org.gcube.core</groupId> <artifactId>common-scope</artifactId> <version>...</version>
The library depends on common-scope-maps
, a bundle of configuration files that provide common-scope
with the addresses of of key middleware services that operate in given scopes.
Introduction
Endpoints of gCube services may operate on behalf of different groups of users. Since they may leave different traces for different groups, clients must mark their requests with an indication of the scope in which the endpoints should satisfy them. And since clients are often service endpoints in turn, there is a general requirement for propagating scope information across chains of service calls, starting from an original client request:
For example, a user logs in the portal to operate in the scope of a given group. He then triggers through the GUI a request for a front-end service (portlet), which discovers and calls a back-end service that operates in the same scope. This service may discover and call in turn another back-end service that operates in that scope, and so on until the original request is satisfied.
The requirement of scope propagation risks to intrude heavily in the design of all clients involved in a call chain. In the worst case, it may require clients to pass scope information across all the local components that are involved in the chain. common-scope
offers facilities to limit this intrusion. The idea is that the first client component that becomes aware of the scope for the next outgoing requests places it in a shared context, as the current scope. Components that execute thereafter can ignore the existence of a current scope, except for components that needs to retrieve it from the shared context and mark outgoing requests with it. '
Overall, the idea is to require explicit scope handling only at the boundaries of the client's runtime. Components that operates at such boundaries are scope-aware, and may not know each other: those that set the current scope do so in the assumption that others will later retrieve it; those that retrieve it assume that others will have set it earlier. Components that operate instead inside the runtime's boundaries are instead scope-free, i.e. can ignore the existence of a scope and its requirements.
Scope-aware components can and usually are provided by general-purpose client libraries. For example, common-gcore-stubs provides components that set the current scope on outgoing requests made through the Featherweight Stack. Similarly, the gCube Application Framework
(gCF) provides components that take the scope that marks incoming request to gCore services and sets is as the current scope.
Accordingly, many clients can remain by and large scope-free. In what follows, we address the subset of scope-aware components and discuss how common-scope
helps managing scope.
Scope
A scope can be:
- an entire e-Infrastructure. Syntactically, we represent it with a name, e.g. research-infrastructures.d4science.eu;
- a Virtual Organisation (VO) within an e-Infrastructure. Syntactically, we represent it with the name of the e-Infrastructure and the name of the VO, separating the two with a forward slash, e.g. research-infrastructures.d4science.eu/FARM;
- a Virtual Research Environment (VRE) within a VO. Syntactically, we represent it with the name of the e-Infrastructure of the VO, the name of the VO, and the name of the VRE, e.g. . research-infrastructures.d4science.eu/FARM/VRE.
Names are agreed upon and, clearly, cannot contain forward slashes.
'Note: the conditions under which clients and services operate in given scopes are independent from scope management requirements, and we will not discuss them here.
In code, common-scope
models scopes as plain String
s, e.g.:
String scope = “a/b/c”;
Scope-aware components that need to analyse scopes can wrap String
s in ScopeBean
s and use their APIs:
import org.gcube.common.scope.impl.ScopeBean; import org.gcube.common.scope.impl.ScopeBean.Type; ScopeBean scope = new ScopeBean(“a/b/c”); assert(scope.is(Type.VRE)); assert(scope.type()==Type.VRE); assert(scope.name().equals(“c”)); assert(scope.enclosingScope().name().equals(“b”)); assert(scope.toString().equals(“a/b/c”));
Scope Provider
common-scope
offers a ScopeProvider
as a repository for the current scope. Scope-aware components can set the current scope as follows:
import org.gcube.common.scope.api.ScopeProvider ScopeProvider.instance.set(“a/b/c”);
Similarly, scope-aware components can retrieve the current scopes as follows:
String scope =ScopeProvider.instance.get();
In rare cases, scope-aware components may want to delete the current scope:
ScopeProvider.instance.reset();
Default Scope Provider
ScopeProvider
is an interface. When the constant ScopeProvider.instance
is first accessed, common-scope
looks for an implementation of the interface on the classpath (using Java's ServiceLoder
mechanism). If it finds none, it sets the constant to an instance of a default implementation, DefaultScopeProvider
.
DefaultScopeProvider
binds scope to threads, i.e. treats it as thread-local information. In particular, it:
- stores the current scope in a
InheritableThreadLocal
. This means that the current scope propagates transparently to child threads. E..g:
final Scope scope = … ScopeProvider.instance.set(scope); new Thread() { public void run() { assert(scope.equals(ScopeProvider.instance.get()); } }
- retrieves the current scope from the system property
gcube.scope
if none is associated with the current thread or its ancestors. This means that clients that operate in a statically known scope can configure the runtime for it, with no need to designate a scope-aware component to set it.
Scoped Tasks
The DefaultScopeProvider
allows clients to remain scope-free even when they spawn new threads. Most commonly, however, clients will abstract over threads and send Callable
or Runnable
tasks to Java's ExecutorService
s that preallocate thread pools to their executions. Since pooled threads are not necessarily in a parent-child relationship with the threads from which clients submits their tasks, the inheritance mechanisms of the DefaultScopeProvider
cannot transparently propagate the scope.
Clients must then become scope-aware, though common-scope
offers facilities to limit this awarenes