Common-gcore-resources
common-gcore-resources
contains an object-based implementation of the gCube Resource Model, i.e. a set of classes that represent known resource types.
The object model improves over its counterpart in the gCube Core Framework
(gCF) in that:
- it has no dependency on the
gCore
stack, or in fact any other 3rd party library. - it offers simpler APIs for the construction and inspection of resources.
For its independence from gCore
, the model can be easily used in a variety of client environments. Target clients may be external to gCube but also include the next-generation of gCube services. In this sense, common-gcore-resources
is part of a new stack of gCube components that support featherweight gCube clients.
Model Classes
The model includes the following top-level resource classes:
-
Software
: describes services, libraries, plugins and other gCube components (corresponds toGCUBEService
in the older model) -
GCoreEndpoint
: describes endpoints of gCore services (corresponds toGCUBERunningInstance
in the older model) -
ServiceEndpoint
: describes endpoints of non-gCore services (corresponds toGCUBERuntimeResource
in the oder model) -
ServiceInstance
describes instances of (stateful) gCore services (not direct counterpart in the older model). -
HostingNode
: describes gHNs (corresponds toGCUBEHostingNode
in the older model) -
GenericResource
: describes gCube resources which are not described by any of the previous classes (corresponds toGCUBEGenericResource
in the older model).
Each class relies on static inner classes to model the complex properties of the corresponding resource type.
Collectively, resourcel classes and their inner classes are referred to as model classes.
All resources classes have the following property:
- serialisation: their instances can be serialised and deserialised to and from XML with the RI implementation of JAXB which is part of the Java platform since version 1.6.
All resource classes except ServiceInstance have also the following property:
- validation: the serialisation of their instances can be validated against the schema definition of the corresponding resource type. There is otherwise little or no validation logic within the model. Note that there is also no schema definition for ServiceInstances.
Finally, all model classes have the following property:
- equivalence: their instances can be compared for equivalence (implement
equals()
) and can serve as keys in hash-based structures (implementhashCode()
).
The API of model classes are designed to support the following instance lifecycle:
- clients create instances and publish them with distinguished services of the gCube Information System.
- clients inspect instances retrieved from distinguished services of the gCube Information System.
- clients may update retrieved instances and re-publish them with distinguished services of the gCube Informaton System.
Clients that create or update instances are referred to as publishing clients. They interact with the write API of model classes.
Clients that inspect instances are referred to as discovery clients. They interact with the read API of model classes.
It is understood that publishing and discovery clients interact with clients libraries dedicated to resource publication and discovery.
Publishing libraries will serialise instances and discovery libraries will deserialise them. Publishing libraries may also validate them prior to publication.
While these libraries will have dependencies on the model, the model is totally independent from such libraries and the services of the gCube Information System.
Serialisation, Deserialisation, and Validation
Resource class instances can be serialised and deserialised using standard JAXB idioms. To illustrate, a GenericResource
may be serialised to an in-memory character stream as follows:
GenericResource generic = … JAXBContext ctx = JAXBContext.newInstance(GenericResource.class); Marshaller marshaller = ctx.createMarshaller(); StringWriter writer = new StringWriter(); marshaller.marshal(generic,writer);
Consult the JAXBContext and Marshaller APIs for various configuration and output options.
For convenience, the Resources
class defines static methods that encapsulate this idiom under a reduced API:
-
<T extends InputStream> InputStream marshal(Object,T)
-
<T extends Writer> marshal(Object,T)
-
<T extends Result> Result marshal(Object,T)
Refactoring the previous example:
Resources.marshal(generic,writer);
During testing, a common destination for instance serialisations is the standard output stream. The Resources
class includes a print() method for the purpose:
-
void print(Object)
Note: marshal()
methods return the input destination, for cases in which doing so is convenient (e.g. round-tripping tests).
Similarly, the GenericResource
may be deserialised from the previous character stream as follows:
Marshaller unmarshaller = ctx.createUnmarshaller(); StringReader reader = new StringReader(writer.toString()); GenericResource deserialised = (GenericResource) unmarhsaller.unmarshal(reader);
Again, consult the Unmarshaller APIs for different input options. For convenience, the Resources class defines static methods that encapsulate this idiom under a reduced API:
-
<T> T unmarshal(Class<T>,InputStream)
-
<T> T unmarshal(Class<T>,Reader)
-
<T> T unmarshal(Class<T>,Source)
Refactoring the previous example:
GenericResource deserialised = Resources.unmarshal(generic,reader);
'Note: since serialisation and deserialisation failures are on average unlikely and unrecoverable errors, marshal()
and unmarshal()
methods throws them as unchecked exceptions (RuntimeException
s).
Note also that the following holds true in all cases above:
generic.equals(unmarshalled);
Resource instance serialisations can then be validated using the following static method of the Resources
class:
-
validate(Object resource) throws IllegalArgumentException, Exception
The method throws an IllegalArgumentException
if it receives a resource without a known schema. It throws a generic Exception
if the resource has a known schema but it is not valid with respect to that schema.
Validation may be performed during testing, or before the instance serialisation is stored or transmitted over the network. Dedicates libraries for resource publishing will normally performed it a pre-condition to publication.
Read API
The read API of model classes is designed to support the inspection of resource class instances which have been deserialised from XML representations, where the representations are typically obtained from querying distinguished services of the gCube Information System.
Note: the deserialisation may be performed by the discovery APIs on behalf of their clients, or it may be directly performed by the clients with the Resource#unmarshal()
methods discussed above. In all cases, the read API assumes that the state of the instances is valid with respect to the corresponding schema definitions. In what follows, we refer to this assumption as to the validity assumption.
The inspection of model class instances relies on classic accessor methods, even though accessor names diverge from standard conventions for increased legibility of deep traversals.
For example, a deeply nested property of ServiceEndpoint
is accessed as follows:
ServiceEndpoint endpoint = .... short version = endpoint.profile().platform().version();
Based on the validity assumption, the design of accessors changes depending on whether properties are or optionals or mandatory in the corresponding schema definition. For optional properties in particular:
- if the property is atomic-valued, accessors return always a
null<code>-able object (e.g. a primitive wrapper). Clients are expected to perform <code>null
checks on the returned value. - if the property is object-valued, accessors are always paired with a method that checks for the existence of a property value (e.g.
hasPlatform
). Clients may use this method to improve over the legibility of explicitnull
checks. - if the property is collection-valued, accessors return empty collections rather than
null
. Clients may thus avoid existence checks altogether.
In the example above, clients may dispense from existence checks, as profile, platform, and platform version are mandatory properties of ServiceEndpoint
s. On the other hand, platforms are optional properties for GCoreEndpoint
s and build versions are in turn optional properties of platforms. Accordingly, a client would navigate a GCoreEndpoint
as follows:
GCoreEndpoint endpoint = .... if (endpoint.profile().hasPlatform()) { Short version = endpoint.profile().platform().buildVersion(); if (version==null { ... } }
For collection-valued properties of type T
, accessors returns Collection<T>
s or Group<T>
s, depending on whether the type T
is mutable or immutable. Group<T>
is an extension of Collection<T>
that simplifies that task of creating collection values, as discussed in the next Section. For inspecting purposes, clients can work in both cases with the Collection<T>
API, e.g.:
ServiceEndpoint endpoint = .... for (Function f: endpoint.profile().functions())) { ... }
Finally, some object-valued properties are unconstrained in the corresponding schema definitions (i.e. can be represented as free-form well-formed XML elements). In these cases, the accessor return always the non-null
Element
root of DOM document which has the contents of the properties as its children. As an example, if the body of a GenericResource
contains the following XML fragment:
<ns:a xmlns:ns="http://acme.org"> <ns:b>...</ns:b> <ns:c>...</ns:c> <ns:c>...</ns:c> </ns:a>
the following code:
GenericResource generic = .... Element body = generic.endpoint().body();
return the synthetic root of a DOM document which has the a
element as its only child.
The resulting document may be then be traversed from its root using the DOM API, or with any other XML API available to the client, including JAXB itself, if the document can be bound to objects known to the client. In many cases, inspection is most conveniently handled with XPath APIs, which are also bundled in the standard Java platform. In these cases, common-gcore-resources
includes XPathHelper
, a simple utility that dispenses clients from configuring the standard Java API for Xpath inspection. To illustrate, the body of the GenericResource
above, may be inspected as follows:
the following code:
GenericResource generic = .... Element body = generic.endpoint().body(); XPathHelper helper = new XPathHelper(body); helper.setNamespace("ns","http://acme.org"); List<String> cs = helper.evaluate("a/c")); for (String c : cs) { ..... }
Note that clients may also use XPathHelper#evalutateForNodes()
if they prefer results as NodeList
.