Featherweight Clients (Athens, 2012)
- 2. 2
outline
know thy clients
fat stacks
• gcore & gcore ‘redux
a sKINNY stack
• stubs, resources, IS clients
Skinny CLs
• framework support
Done-s &todo-s
Wednesday, 23 January 13
- 6. 6
main issues
• awkward
• installed & configured on file system
no declarative management (e.g. via maven)
no embedding (e.g. in web-app)
• needlessly faT
• about 140 jars, only a fraction relevant
no cherry picking: unclear dependency graph
service-side bias: high “clash-factor”
• obsolete
• 2004 and older
• age increases further “clash factor”
Wednesday, 23 January 13
- 7. 7
retrospective
• a service stack, really
• pure client support is an afterthought
• oK if cLIents =gcore services
• issues become non-issues (e.g. install,config)
• ...or are platform issues (e.g. obsolete)
• a toothache otherwise
• unpresentable to external clients
• won’t let us develop internal clients on new platforms
adoption & evolution at stake
Wednesday, 23 January 13
- 8. 8
gcore “redux” stack
AXIS subset
globus subset
gcore subset
JDK
ghn-client-runtime
client library
CLIENT
FAT
SKINNY
~10 MB
service STUBS
2012-
Wednesday, 23 January 13
- 9. 9
how it works
• a subset of the gcore stack
• prunes what clients don’t need
• fully mavenized
• in gcube-externals, as closure of ghn-client-runtime
• embeds a teeny-tiny ghn distro
• just config files, including all servicemaps
• unpacks it on demand
• in temp folder, when client says so (CLientruntime#start())
Wednesday, 23 January 13
- 10. 10
properties
• friendlier
• hides FS installation&configuration requirements
automated dependency management now possible
embedding now possible
• leaner
• about 40jars, all relevant
client-only bias reduces “clash-factor”
• but...
still obsolete, it’s just a subset
still large, cherry picking remains unlikely
usage is not transparent (client must bootstrap)
just a clever hack
Wednesday, 23 January 13
- 12. 12
why do we need...
• ...axis ?
• stubs depend on it
• implements ws-addressing
• ...Globus ?
• stubs depend on it
• gcore depends on it
• ....Gcore ?
• again, stubs depend on it
• knows how to manage scope
• knows how to make “gcore calls”
• knows how to model, discover, publish resources
Wednesday, 23 January 13
- 13. 13
Nov-Dec 2012
• fao shows clients don’t need axis stubs
• uses interfaces annotated with JAX-WS&JAXB
• JDK 6+implements them on-the-fly
• FAO proposes a new stack
• gcore-free, built on pure JDK support, lean
• CNR, CERN & ATHENS approve
• FAO&CNR deliver it in 2 weeks (“botox”)
• implement scope and call support outside gcore
• implement resources, discovery & publication outside
gcore
• add novelties in the process
Wednesday, 23 January 13
- 16. 16
properties
• “normal”
• no FS installation or configuration
• no bootstrapping requirements
• maven-based, easily embedded
• skinny & standard
• less than 10jars, almost no 3rd party deps
• jdk provides runtime, parser, binding, etc.
• zero “clash-factor”
• modular
• calls without discovery & publication
• discovery without publication
• publication without discovery
Wednesday, 23 January 13
- 19. 19
properties
• distribution
• a pure client-side artifact
• most likely embedded in cl, if one exists
• contents
• the “SEI”: stub interface, ann. with jax-ws
• i/o bean classes, ann. with jaxb
• handful of service-specific constants
• no SEI implementation, locators, ...
• development
• can be (largely) automated
• can be (fully) manual (more than doable)
• not a client concern !
Wednesday, 23 January 13
- 20. 20
the ACME service
WSDL
<definitions name="Acme"
targetNamespace="http://acme.org" ... >
! ......
" <portType name="AcmePortType">
...
" </portType>
...
<service name="s:AcmeService" xlmns:s="http://acme.org/service">
...
</service>
</definitions>
class AcmeConstants {
static final String serviceNS = "http://acme.org/service";
static final String porttypeNS = "http://acme.org";
static final QName name = new QName(serviceNS,"AcmeService");
static final String porttypeLN = "AcmePortType";
static final String gcube_class="VIPServices";
static final String gcube_name="Acme";
}
ACME
service
PROFILE
<Resource>
….
<Profile>
<Class>VIPServices</Class>
<Name>Acme</Name>
….
</Profile>
</Resource>
gCube
coordinates
web service
coordinates
constants
Wednesday, 23 January 13
- 21. 21
ACME SEI
@WebService(name=porttypeLN,
targetNamespace=porttypeNS)
public interface AcmeStub {
@SOAPBinding(parameterStyle=BARE)
String foo(String s);
...
}
WSDL
<definitions name="Acme"
targetNamespace="http://acme.org" … >
<types>
<xsd:schema targetNamespace="http://acme.org">
<xsd:element name="foo" type="xsd:string" />
<xsd:element name="fooResponse" type="xsd:string" />
...
</xsd:schema>
</types>
...
<portType name="AcmePortType">
<operation name="foo">
<input message="tns:foo"/>
<output message="tns:fooResponse"/>
</operation>
...
</portType>
...
</definitions>
JAX-WS SEI
MANUAL or AUTOMATic
GENERATION
complex I/O require
JAxB-ANNOTATED BEANS
WIKI TUTORIAL AVAILABLE
Wednesday, 23 January 13
- 22. 22
ACME descriptor
import static org.gcube.common.clients.stubs.jaxws.GCoreServiceBuilder.*;
class AcmeConstants {
static final String serviceNS = "http://acme.org/service";
static final String porttypeNS = "http://acme.org";
static final QName name = new QName(serviceNS,"AcmeService");
static final String porttypeLN = "AcmePortType";
static final String gcube_class="VIPServices";
static final String gcube_name="Acme";
static final GCoreService<AcmeStub> acme = service().
withName(name).
coordinates(gcube_class,gcube_name).
andInterface(AcmeStub.class);
}
constants
Wednesday, 23 January 13
- 25. 25
at a glance
SEI
STUBS
common-gcore-stubs
DESCRIPTOR ENDPOINT
address
URI or reference
returns STUB
IMPLEMENTATION
for client use
FETCHES WSDL
from service endpoint
(NET or cache)
obtains STUB
implementation
from jaws runtime
configures stubs
to make "gore calls"
to service endpoint
Wednesday, 23 January 13
- 26. 26
in code
import static …AcmeConstants.*;
import static org.gcube.common.clients.stubs.jaxws.StubFactory.*;
...
//some endpoint address
URI address = ...
//get a stub
MyStub stub = stubFor(acme).at(address);
//use stub
String outcome = stub.foo("...");
Wednesday, 23 January 13
- 30. 30
model classes
• HostingNode
• Software : services, libs, plugins
• ex. gcube service
• GcoreEndpoint
• ex. Running instance
• ServiceEndpoint : endpoints of non-gcore services
• ex. Runtime resource
• ServiceInstance : instances of (stateful) gcore services
• ex. resource property document
• GenericResource: any other resource
Wednesday, 23 January 13
- 31. 31
what’s new
• standard data binding
• now based on jaxb’s ri in JDK
• explicit alidation
�� now exposed to clients
• no longer implicit AT binding time
• more regularity
• consistent API across property types
• equals() & hashcode() throughout
• less ad-hoc provisions (for good or bad)
Wednesday, 23 January 13
- 32. 32
what’s new
• more FLUENCY
• builder-style construction
• inlined creation
• less temp vars, less new() operators
• minimality
• e.g. no scope checks
• e.g. no encryption
• ...
• no logic beyond access & binding
Wednesday, 23 January 13
- 33. 33
read example
import static org.gcube.common.resources.gcore.Resources.*;
//bind
GCoreEndpoint endpoint =
unmarshal(GCoreEndpoint.class,...one of many sources...);
print(endpoint);
Element data = endpoint.profile().specificData();
//helper
XPathHelper xpath = new XPathHelper(data);
for (String p : xpath.evaluate("/my/prop")
...
Wednesday, 23 January 13
- 34. 34
write example
import static org.gcube.common.resources.gcore.Resources.*;
GCoreEndpoint endpoint = new GCoreEndpoint();
endpoint.scopes().add("/some/scope");
//fluent building
endpoint.profile().version("345").
ghnId("nodeid").
serviceId("serviceid").
serviceName("name").
serviceClass("class");
//inlined creation
endpoint.profile().newDeploymentData().
activationTime(Calendar.getInstance());
//inlined element creation
endpoint.profile().endpoints().add().nameAndAddress("name",...);
validate (endpoint);
//bind
marshal(GCoreEndpoint.class,...one of many sinks...);
Wednesday, 23 January 13
- 38. 38
what’s new
• same core pattern
• prepare query beans, free-form or templated
• submit beans, get parsed results
• more modular
• queries don’t do parsing
• parser defined at submission
• can parse fine-grained results
• predefined parsers for all resource props
• more flexible templates
• results, variables, namespaces
• query templating not only for internal use
Wednesday, 23 January 13
- 40. 40
predefined queries
import static org.gcube.resources.discovery.icclient.ICFactory.*;
//pick a predefined query
Query query = queryFor(ServiceEndpoint.class);
//pick client that knows how to parse those resources
DiscoveryClient<ServiceEndpoint> client = clientFor(ServiceEndpoint.class);
//execute query in current scope
List<ServiceEndpoint> endpoints = client.submit(query);
//consume results
...
Wednesday, 23 January 13
- 41. 41
conditions and results
//pick a predefined query
SimpleQuery query = queryFor(ServiceEndpoint.class);
//customise query to return only database endpoints
query.addCondition("$resource/Profile/Category/text() eq 'Database'")
.setResult("$resource/Profile/AccessPoint/Interface/Endpoint/text()");
//client that does not parse
DiscoveryClient<String> client = client();
//execute query in current scope
List<String> endpoints = client.submit(query);
Wednesday, 23 January 13
- 42. 42
aux variables
//pick a predefined query
SimpleQuery query = queryFor(GCoreEndpoint.class);
//customise to return only some endpoints
query.addCondition("$resource/Profile/ServiceName/text() eq 'tree-manager-service'")
.addVariable("$entry","$resource/Profile/AccessPoint/RunningInstanceInterfaces/Endpoint");
.addCondition("$entry/@EntryName/string() eq 'gcube/data/tm/binder'");
.setResult("$entry/text()");
//client that does not parse
DiscoveryClient<String> client = client();
//execute query in current scope
List<String> entries = client.submit(query);
Wednesday, 23 January 13
- 43. 43
custom namespaces
//pick a predefined query
SimpleQuery query = queryFor(ServiceInstance.class);
//customise to return only some endpoints
URI uri = URI.create("http://gcube-system.org/namespaces/data/tm"));
query.addNamespace("tm",uri)
.addCondition("$resource/Data/tm:Plugin/name/text() eq '...'");
//client that parses instances
DiscoveryClient<ServiceInstance> client = clientFor(ServiceInstance.class);
//execute query in current scope
List<ServiceInstance> instances = client.submit(query);
Wednesday, 23 January 13
- 44. 44
property JAXB parsers
//pick a predefined query
SimpleQuery query = queryFor(ServiceEndpoint.class);
//customise to return only some endpoints
query.addCondition("$resource/Profile/Category/text() eq 'Database'")
.setResult("$resource/Profile/AccessPoint");
//client that parses access points
DiscoveryClient<AccessPoint> client = clientFor(AccessPoint.class);
//execute query in current scope
List<AccessPoint> apoints = client.submit(query);
Wednesday, 23 January 13
- 45. 45
custom JAXB parsers
//pick a predefined query
SimpleQuery query = queryFor(ServiceEndpoint.class);
//customise to return property selection
query.addCondition("$resource/Profile/Category/text() eq 'Database'")
.query.setResult("<perfect>" +
“<id>{$resource/ID/text()}</id>" +
"{$resource/Profile/AccessPoint}" +
"</perfect>");
DiscoveryClient<AccessPoint> client = clientFor(PerfectResult.class);
List<PerfectResult > results = client.submit(query);
---------------------------------------------------------------------------------
@XmlRootElement(name="perfect")
private static class PerfectResult {
@XmlElement(name="id") String id;
@XmlElementRef AccessPoint ap;
}
Wednesday, 23 January 13
- 46. 46
free-form queries
QueryBox query = new QueryBox(“...expression...”);
DiscoveryClient<String> client = client();
List<String> results = client.submit(query);
Wednesday, 23 January 13
- 51. 51
“pick a feather”
client library
GCORE stack
client
featherweight
stack
migration
version x.y.z version x.y+1
Wednesday, 23 January 13
- 55. 55
dones
• components
• all components
• cl framework binding
• documentation (almost) complete
• client migration
• tree manager library
• data transfer library
• search library
• species service library
• tree-uri-resolver
Wednesday, 23 January 13
- 56. 56
to-do
• known issues
• faults do not carry remote stacktrace
• cl FWk does not set timeouts, yet
• last few 3rd party deps not perfect
• components
• migrate encryption
• notifications ? how ?
Wednesday, 23 January 13
- 57. 57
conclusions
• gcore is quarantined
• only service-side
• calling gcube has become easy
• and leaves the smallest footprint
• gcube can evolve
• future services can target modern platforms
• enabling work can start to support them
• without painful, all-or-nothing migrations
Wednesday, 23 January 13