Difference between revisions of "Common-scope"

From Gcube Wiki
Jump to: navigation, search
(Created page with '{| align="right" || __TOC__ |} '''common-scope''' is a client library that provides facilities to manage the scope of calls to gCube services, including: * facilities to propag…')
 
(Scope)
 
(4 intermediate revisions by the same user not shown)
Line 3: Line 3:
 
|}
 
|}
  
'''common-scope''' is a client library that provides facilities to manage the scope of calls to gCube services, including:
+
'''common-scope''' is a client library that helps managing the scope of calls to gCube services. Most noticeably, it offers mechanisms to transparently propagate scope informations across client components, thereby minimising their explicit dependencies on scope.
 
+
* 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;
+
  
 
<code>common-scope</code> is available in our Maven repositories with the following coordinates:
 
<code>common-scope</code> is available in our Maven repositories with the following coordinates:
Line 18: Line 14:
  
 
</source>
 
</source>
 
The library depends on <code>common-scope-maps</code>, a bundle of configuration files that provide <code>common-scope</code> with the addresses of of key middleware services that operate in given scopes.
 
  
 
= Introduction =
 
= 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:
+
Endpoints of gCube services may execute 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 process them.  In turn, the endpoints may call endpoints of other services, and must mark the requests with the scope in which they have been invoked. Overall, 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.
+
[[Image:scope-call-chain.png|center]]
  
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. <code>common-scope</code> 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. '
+
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) endpoint, which discovers and calls a back-end service endpoint 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.
  
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|common-gcore-stubs]] provides components that set the current scope on outgoing requests made through the [[Featherweight_Stack|Featherweight Stack]]. Similarly, the <code>gCube Application Framework</code> (gCF) provides components that take the scope that marks incoming request to gCore services and sets is as the current scope.
+
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, making them all '''scope-aware''': 
  
Accordingly, many clients can remain by and large scope-free. In what follows, we address the subset of scope-aware components and discuss how <code>common-scope</code> helps managing scope.
+
[[Image:scope-aware.png|center]]
 +
 
 +
<code>common-scope</code> offers facilities to limit the number of scope-aware components. The idea is that the first client component that becomes aware of the scope of the next outgoing requests places it in a shared context provided by <code>common-scope</code>, as the '''current scope'''. Components that execute thereafter can ignore the existence of a current scope, unless they need to retrieve it from the shared context in order to mark outgoing requests with it. '
 +
 
 +
Thus, <code>common-scope</code> requires explicit scope handling only at the ''boundaries'' of the client's runtime.  Components that operates at such boundaries are scope-aware, and need 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.
 +
 
 +
[[Image:scope-free.png|center]]
 +
 
 +
Scope-aware components can be provided by general-purpose client libraries, and usually are. For example, [[Common-gcore-stubs|common-gcore-stubs]] provides components that set the current scope on outgoing requests made through the [[Featherweight_Stack|Featherweight Stack]]. Similarly, the <code>gCube Application Framework</code> (gCF) provides components that take the scope that marks incoming request to gCore services and sets is as the current scope.
 +
 
 +
Thus many clients remain by and large scope-free. In what follows, we address the smaller subset of scope-aware components, and discuss how <code>common-scope</code> helps managing scope.
  
 
= Scope =
 
= Scope =
Line 39: Line 42:
 
A scope can be:
 
A scope can be:
  
* an entire e-Infrastructure. Syntactically, we represent it with a name, e.g. research-infrastructures.d4science.eu;
+
* an entire e-Infrastructure. Syntactically, we represent it with a name, e.g. <code>d4science.research-infrastructures.eu</code>;
  
* 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 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. <code>d4science.research-infrastructures.eu/FARM</code>;
  
* 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.
+
* 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. <code>d4science.research-infrastructures.eu/FARM/AquaMaps</code>.
  
 
Names are agreed upon and, clearly, cannot contain forward slashes.  
 
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.  
 
'''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, <code>common-scope</code> models scopes as plain <code>String</code>s, e.g.:
 
In code, <code>common-scope</code> models scopes as plain <code>String</code>s, e.g.:
Line 97: Line 98:
 
<code>ScopeProvider</code> is an interface. When the constant <code>ScopeProvider.instance</code> is first accessed, <code>common-scope</code> looks for an implementation of the interface on the classpath (using Java's <code>ServiceLoder</code> mechanism). If it finds none, it sets the constant to an instance of a default implementation, <code>DefaultScopeProvider</code>.  
 
<code>ScopeProvider</code> is an interface. When the constant <code>ScopeProvider.instance</code> is first accessed, <code>common-scope</code> looks for an implementation of the interface on the classpath (using Java's <code>ServiceLoder</code> mechanism). If it finds none, it sets the constant to an instance of a default implementation, <code>DefaultScopeProvider</code>.  
  
<code>DefaultScopeProvider</code> binds scope to threads, i.e. treats it as thread-local information. In particular, it:
+
<code>DefaultScopeProvider</code> binds the current scope to threads, i.e. treats it as thread-local information. In particular, it:
  
 
* stores the current scope in a <code>InheritableThreadLocal</code>. This means that the current scope propagates transparently to child threads. E..g:
 
* stores the current scope in a <code>InheritableThreadLocal</code>. This means that the current scope propagates transparently to child threads. E..g:
Line 119: Line 120:
  
 
= Scoped Tasks =
 
= Scoped Tasks =
The <code>DefaultScopeProvider</code> allows clients to remain scope-free even when they spawn new threads. Most commonly, however, clients will abstract over threads and send <code>Callable</code> or <code>Runnable</code> tasks to Java's <code>ExecutorService</code>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 <code>DefaultScopeProvider</code> cannot transparently propagate the scope.
+
 
Clients must then become scope-aware, though <code>common-scope</code> offers facilities to limit this awarenes
+
The <code>DefaultScopeProvider</code> allows clients to remain scope-free even when they spawn new threads. Many clients, however, do not engage in explicit thread management, but submit <code>Callable</code> or <code>Runnable</code> tasks to Java's <code>ExecutorService</code>s. These services preallocate thread pools for the executions of tasks, where the pooled threads are not necessarily in a parent-child relationship with the threads from which clients submits tasks.
 +
 
 +
In these cases, the <code>DefaultScopeProvider</code> cannot transparently propagate the scope across threads, and clients must become scope-aware. <code>common-scope</code> minimise the awareness with facilities that take care of the lower-level details of scope propagation. In particular, <code>ScopedTasks</code> offers static <code>bind()</code> methods to wrap <code>Callable</code> and <code>Runnable</code> tasks in scope-aware versions, e.g.:
 +
 
 +
<source lang="java5">
 +
import org.gcube.common.scope.impl.ScopedTasks;
 +
 +
 
 +
final Scope scope = …
 +
ExecutorService service = ...
 +
 
 +
Runnable task = new Runnable() {…};
 +
 
 +
task = ScopedTasks.bind(task);
 +
 
 +
...service.submit(task)...;
 +
 
 +
</source>
 +
 
 +
Here <code>bind()</code> returns a <code>Runnable</code> that:
 +
 
 +
* memorises the current scope;
 +
* sets it on the pooled thread in which the <code>ExecutorService</code> will assign to its execution;
 +
* delegates execution to the original <code>Runnable</code>;
 +
* reset the current scope on the pooled thread.

Latest revision as of 11:21, 19 April 2013

common-scope is a client library that helps managing the scope of calls to gCube services. Most noticeably, it offers mechanisms to transparently propagate scope informations across client components, thereby minimising their explicit dependencies on scope.

common-scope is available in our Maven repositories with the following coordinates:

<groupId>org.gcube.core</groupId>
<artifactId>common-scope</artifactId>
<version>...</version>

Introduction

Endpoints of gCube services may execute 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 process them. In turn, the endpoints may call endpoints of other services, and must mark the requests with the scope in which they have been invoked. Overall, there is a general requirement for propagating scope information across chains of service calls, starting from an original client request:

Scope-call-chain.png

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) endpoint, which discovers and calls a back-end service endpoint 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, making them all scope-aware:

Scope-aware.png

common-scope offers facilities to limit the number of scope-aware components. The idea is that the first client component that becomes aware of the scope of the next outgoing requests places it in a shared context provided by common-scope, as the current scope. Components that execute thereafter can ignore the existence of a current scope, unless they need to retrieve it from the shared context in order to mark outgoing requests with it. '

Thus, common-scope requires explicit scope handling only at the boundaries of the client's runtime. Components that operates at such boundaries are scope-aware, and need 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-free.png

Scope-aware components can be provided by general-purpose client libraries, and usually are. 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.

Thus many clients remain by and large scope-free. In what follows, we address the smaller 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. d4science.research-infrastructures.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. d4science.research-infrastructures.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. d4science.research-infrastructures.eu/FARM/AquaMaps.

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 Strings, e.g.:

String scope = “a/b/c”;

Scope-aware components that need to analyse scopes can wrap Strings in ScopeBeans 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 the current 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. Many clients, however, do not engage in explicit thread management, but submit Callable or Runnable tasks to Java's ExecutorServices. These services preallocate thread pools for the executions of tasks, where the pooled threads are not necessarily in a parent-child relationship with the threads from which clients submits tasks.

In these cases, the DefaultScopeProvider cannot transparently propagate the scope across threads, and clients must become scope-aware. common-scope minimise the awareness with facilities that take care of the lower-level details of scope propagation. In particular, ScopedTasks offers static bind() methods to wrap Callable and Runnable tasks in scope-aware versions, e.g.:

import org.gcube.common.scope.impl.ScopedTasks;
…
 
final Scope scope = …
ExecutorService service = ...
 
Runnable task = new Runnable() {};
 
task = ScopedTasks.bind(task);
 
...service.submit(task)...;

Here bind() returns a Runnable that:

  • memorises the current scope;
  • sets it on the pooled thread in which the ExecutorService will assign to its execution;
  • delegates execution to the original Runnable;
  • reset the current scope on the pooled thread.