In this section, we will instanciate an SCA composite:
helloworld
inside the FraSCAti FScript interactive console to
discover navigation elements (nodes, axis) with a concrete example.
In order to issue FPath queries and see their results, we will use the interactive console application provided in the FraSCAti FScript module. The console is actually just a normal client application which uses the Java APIs provided by FScript, the same way your applications can. (If your goal is to use FPath and/or FScript from your Java programs, first read FScript tutorials).
To start the console application, go to the FraSCAti FScript module directory, and issue the following command:
% mvn -P console FScript console. Useful commands: - type ':help' for a list of available commands - type ':help <cmd>' for detailed help on a specific command - type ':quit' to exit FScript>
You should be greeted by the FraSCAti FScript console's prompt. As you can see from the welcome message, the console understands a few commands like :help and :quit. These are not part of the FPath language, but special commands to control the console application itself. We'll explain a few of these in this tutorial, but you can enter :help at the prompt to get a complete list.
In this tutorial, we will use the helloworld rmi application. The helloworld-rmi-standolone composite is composed of 2 composites : the client and the server. The server provides a service allowing to display "Hello World" with a prefix defined by an SCA property.
Before we can create an instance of HelloWorld to play with, we
need to add the JAR to the console's classpath. You can add a new entry
to the console's classpath using the :classpath_add
console
command:
FScript> :classpath_add file:///full/path/to/helloworld-rmi-server.jar Classpath updated. FScript> :classpath_add file:///full/path/to/helloworld-rmi-client.jar Classpath updated. FScript> :classpath_add file:///full/path/to/frascati-debug.jar Classpath updated.
You can find helloworld-rmi*.jar in the examples/helloworld-rmi folder and the frascati-debug.jar file in the lib (or intents/debug) folder.
The argument passed to the command can be any URL which Java
understands. For local files, use a file://
URL. Now that
all the required code and composite definitions are available, we can
create an instance of the HelloWorld composite:
FScript> c = sca-new('path/to/helloworld-rmi-standalone'); => #<scacomponent: helloworld-rmi-standalone> Success (took 524 ms).
This command invokes the sca-new() primitive operation with the name of the SCA composite definition to instantiate, and stores the result in a variable named c.
Note that this line is actually not an FPath expression, but an
FScript statement. The difference is that FPath expressions can not have
any side-effect: they can't modify the target architecture (only
introspect them), and they can't even bind new variables. Once it has
executed the statement above, creating a new instance of HelloWorld, the
console tells us how much time it took, and prints the value of the
statement. In this case, the value is #<scacomponent:
helloworld-rmi-standalone>
, which denotes a single SCA
component node named helloworld-rmi-standalone
. FPath uses
a generic notion of node to represent all the elements in a software
architecture. Different architectural models may define different kinds
of nodes, depending on the concepts they use. For example in the case of
SCA, there are following kinds of nodes: scacomponent nodes
(representing the SCA components/composites themselves), scaservice and
scareference nodes (their interfaces), scaproperty nodes (representing
the configuration properties defined by the component), scaintent nodes
(policies defined on services/references), scabinding nodes (components
representing either the stub or the skeleton used to import/export
interfaces).
We now have everything set up correctly to actually issue FPath
queries to introspect HelloWorld. The first thing we can try is to
look a the variable we have just defined above. To get the value of a
variable, simply prefix its name with a dollar sign
($
):
FScript> $c => #<scacomponent: helloworld-rmi-standalone>
This is a node which represents the top-level component of the HelloWorld application, the helloworld-rmi-standalone composite. Each kind of node in FPath defined a set of named properties, which have primitive values: strings, numbers, booleans, etc. In the case of SCA, the component nodes have two properties: a name, and a state. We can get the values of these properties by using the function of the same name:
FScript> name($c) => "helloworld-rmi-standalone" FScript> state($c) => "STOPPED"
In addition of the interface node defined in the fractal model, an scaservice node is defined to follow the SCA semantic.
FScript> $c/scaservice::* => a node-set with 2 element(s): #<scaservice: helloworld-rmi-standalone.r> #<scaservice: helloworld-rmi-standalone.rbis>
Here we see that the result is still a set of nodes, but this time the nodes represents the component's services. As before, we can select a specific service, for example the rbis service, by its name. We'll store it in a new variable so we can reference it later:
FScript> rbis = $c/scaservice::rbis; => a node-set with 1 element(s): #<scaservice: helloworld-rmi-standalone.rbis> Success (took 1 ms). FScript> s = $c/scachild::server/scaservice::*; => a node-set with 1 element(s): #<scaservice: server.s> Success (took 1 ms).
In the same way, we define the counter part: the reference node. The helloworld-rmi-standalone does not contain any reference, so we will introspect one of these children (the client component) by using the scachild axis defined in the next part of this tutorial.
FScript> print = $c/scachild::client/scareference::*; => a node-set with 1 element(s): #<scareference: client.printService> Success (took 4 ms).
We can also select a specific reference by using its name instead of the wildcard character.
The intent node is used to represent intents defined on SCA components, services, references. It extends the SCA component node as it the intent is itself an SCA component. So, an SCA intent can be manipulated/introspected in the same way as SCA components.
FScript> i = $s/scaintent::*; => a node-set with 1 element(s): #<scaintent: frascati-debug on s> Success (took 1 ms). FScript> state($i) => "STARTED"
We also introduced a binding node in order to be able to interact with SCA bindings. This node inherits from the Component node kind as SCA bindings are implemented as a couple of Fractal components: one for the stub (for SCA references), the other one for the skeleton (for SCA services). Here is an example:
FScript> printServiceBinding = $c/scachild::client/scareference::printService/scabinding::*; => a node-set with 1 element(s): #<scabinding: rmi-stub-primitive> Success (took 3 ms). FScript> $c/scachild::server/scaservice::s/scabinding::*; => a node-set with 1 element(s): #<scabinding: org.objectweb.fractal.bf.connectors.rmi.RmiSkeletonContent> Success (took 3 ms).
The Sca Binding node can be used to discover binding properties with the help of the attribute axis.
Using the previously defined
printServiceBinding
variable, we can use the
fractal
axis to
discover binding attributes. Then, we can easily get an attribute
value:
FScript> $printServiceBinding/attribute::*; => a node-set with 1 element(s): #<attribute: rmi-stub-primitive.serviceName> Success (took 2 ms). FScript> value( $printServiceBinding/attribute::serviceName); => "rmiservice" Success (took 3 ms).
An SCA property node represents configuration properties, as defined by the component in the SCA composite file. Let's select one and see properties it has:
FScript>p = $c/scaproperty::*; => a node-set with 1 element(s): #<scaproperty: helloworld-rmi-standalone.composite-property> Success (took 1 ms).
You can get the value of any SCA property with the value() instruction as following:
FScript> value($p) => "-->"
In the previous section, we've seen that FPath models software architectures using nodes to represent the elements of interest (components, services/references, properties, bindings, intents in the case of SCA). By themselves, nodes can only model individual elements, and do not give us any information on the actual structure of the system. To represent the structure, FPath connects the nodes using directed arcs. The result is that, from FPath's point of view, a software architecture is a directed graph. The vertices of this graph are the nodes we've already seen, and the edges are labelled arcs between these nodes, which represent their relationships.
The different kinds of relationships which can exist between nodes are called axes, and are the basic mechanism for navigation in FPath. We've actually already met various axes in the previous section such as scachild, scaservice, scaproperty, etc:
scachild labels arcs which connect composite nodes to the nodes representing their direct SCA sub-component
scaparent labels arcs which connect component nodes to the nodes representing their direct parents (if any)
scaservice labels arcs which connect component nodes to the nodes representing its SCA services
scareference labels arcs which connect component nodes to the nodes representing its SCA references
scaproperty labels arcs which connect component nodes to the nodes representing its configuration properties
scabinding labels arcs which connect SCA interfaces (service or reference) nodes to the nodes representing its SCA bindings
scaintent labels arcs which connect component (or SCA interfaces) nodes to the nodes representing its SCA intents.
With this information, we can now understand in more details what the queries from the previous section do. When we write $c/scachild::*, it actually means: “From the node(s) in variable $c, traverse all the arcs in the graph labelled scachild, and return the nodes at the other end.” Each step in a path “walks” along the corresponding axis in the graph, returning the destination nodes at the other end of the arcs.
FraSCAti Script provides several other axes to navigate in SCA architectures.
FScript> $c/scachild::* => a node-set with 2 element(s): #<scacomponent: client> #<scacomponent: server> FScript> client = $c/scachild::client; => a node-set with 1 element(s): #<scacomponent: client> Success (took 1 ms).
The expression $c/scachild::* is our first example of an actual
path expression. It selects all the (direct) children of component
$c
, and returns them in a node-set. In this case,
the result is a set of two components (more precisely two nodes
representing the components) named client
and
server
. These correspond to the client and server
components, which are indeed the two direct sub-components of the
HelloWorld top-level (see Figure 1, “The architecture of the
HelloWorld example.”).
FScript> $client/scaparent::* => a node-set with 2 element(s): #<scacomponent: helloworld-rmi-standalone>
The expression $c/scaparent::* selects all the (direct) parents
(if any) of component $c
lient, and returns them in
a node-set. In this case, the result is a set of one component named
helloworld-rmi-standalone
.
If instead of selecting the sub-components of $c we want to see its provided services, we can use the following query:
FScript> $c/scaservice::* => a node-set with 2 element(s): #<scaservice: helloworld-rmi-standalone.r> #<scaservice: helloworld-rmi-standalone.rbis>
Here, we can see that the whole composite provides 2 services named 'r' and 'rbis'.
In the same way, the following query allows us to discover SCA component references (i.e. required services):
FScript> $c/scachild::client/scareference::* => a node-set with 1 element(s): #<scareference: client.printService>
We wanted to know what are SCA references needed by the client composite (i.e. what are used/required services). The result shows us that there is one dependency to a printService.
In order to know a bit more about service/reference, we can use the scabinding axis described below.
The scawire
axis allows you to navigate
between components. It connects client interfaces to the server
interface(s) they are bound to. The connect
and disconnect
operations on this axis are
used to create and destroy (fractal) bindings. It is an alias of the
(fractal) binding axis available for Fractal components.
The scabinding axis connects a node representing an SCA reference to the node representing the SCA service it is bound to, if any. Note that the binding keyword refers to the SCA binding notion, i.e. a way to expose/import remote services (more simply, a communication protocol like WS, RMI, etc.).
It is almost always used in the form scabinding::*. For example:
FScript> psbinding = $c/scachild::client/scareference::printService/scabinding::*; => a node-set with 1 element(s): #<scabinding: rmi-stub-primitive> Success (took 2 ms).
This tells us that the interface named printService of the client (sub-)component is actually exported as a RMI service.
The scabinding node kind inherits from the (fractal) ComponentNode kind, so we can use classical (i.e. fractal) axis on it. Especially, we can use the attribute axis to discover SCA binding attributes. In this way, we are able to discover registry name, ws end-point and so on.
FScript> $psbinding/attribute::* => a node-set with 1 element(s): #<attribute: rmi-stub-primitive.serviceName> FScript> value( $psbinding/attribute::serviceName ); => "rmiservice" Success (took 10 ms).
SCA components may define SCA properties which are configuration elements. FraSCAti allows a bit more because you are able to dynamically reconfigure SCA properties on running components. To discover configuration capabilities of a component, simply use the following pattern:
FScript> header = $c/scachild::server/scaproperty::*; => a node-set with 1 element(s): #<scaproperty: server.header> Success (took 2 ms). FScript> value( $header ) => "-->"
Last but not least, SCA let you define intents (policies) on
services, references or components (then apply on all services and
references of the component). So, there is an
scaintent
axis that can be used on SCA services,
references or components nodes. This axis returns SCA Component
node(s) as intents are themselve implemented as SCA composites with
FraSCAti. So, all SCA-related axes defined here can be applied to
scaintent
axis results.
FScript> $c/scachild::server/scaintent::* => a node-set with 1 element(s): #<scaintent: frascati-debug on s>
In this example, we are discovering an intent named
frascati-debug
defined on the s
interface (service) of the server
component.
In addition to previous axes, there are other derivated axes:
Transitive axes: select nodes recursively by applying the axis on results
scadescendant
scaancestor
Composed axis: combination of two axes
scasibling
Reflexive axes: add the source component to selected node
scachild-or-self
scaparent-or-self
scadescendant-or-self
scaancestor-or-self
scasibling-or-self