AUTHOR : | |||
E. Bruneton | (France Telecom R&D) |
Version | 1.1 |
Released | September 12, 2003 |
This tutorial explains how the Fractal ADL and the Fractal ADL parser can be used to describe and instantiate Fractal applications. It is based on the example used in the Fractal tutorial, namely the HelloWorld example. The content of this tutorial is independent of any specific implementation of the Fractal API.
The ADL definitions described here can be found in the examples/helloworld directory of the Fractal distribution archive.
<component-type name="RootType"> <provides> <interface-type name="m" signature="Main"/> </provides> </component-type>
The type of the client component, which provides a similar interface, but which also has a client Service interface named s, can be specified like this:
<component-type name="ClientType"> <provides> <interface-type name="m" signature="Main"/> </provides> <requires> <interface-type name="s" signature="Service"/> </requires> </component-type>
It is also possible to define this type by extending the RootType definition:
<component-type name="ClientType" extends="RootType"> <requires> <interface-type name="s" signature="Service"/> </requires> </component-type>
This definition is equivalent to the previous one. In other words, ClientType inherits the m interface type from RootType. The type of the server component, which only provides a Service interface named s, can be specified like this:
<component-type name="ServerType"> <provides> <interface-type name="s" signature="Service"/> </provides> </component-type>
The server component also has an attribute controller interface, but this interface should not be specified in the type of this component: component types should only describe the functional interfaces of components, not their control interfaces.
<component-type name="..." extends="..."> <provides> <interface-type name="..." signature="..." contingency="..." cardinality="..."/> ... </provides> <requires> <interface-type name="..." signature="..." contingency="..." cardinality="..."/> ... </requires> </component-type>
The extends, signature, contingency and cardinality attributes are optional, as well as the <provides> and <requires> sub elements. The signature attribute can be omitted only when overriding an existing interface type definition. The contingency attribute must be equal to mandatory or optional. The default value is mandatory. The cardinality attribute must be equal to single or collection. The default value is single. The semantics of the inheritance mechanism is defined in Appendix A.
<primitive-template name="ClientImpl" implements="ClientType"> <primitive-content class="ClientImpl"/> </primitive-template>
The template of the primitive server component, which implements the ServerType type in the ServerImpl class, can be specified like this:
<primitive-template name="ServerImpl" implements="ServerType"> <primitive-content class="ServerImpl"/> <controller> <attributes signature="ServiceAttributes"> <attribute name="Header" value="-> "/> <attribute name="Count" value="1"/> </attributes> </controller> </primitive-template>
This definition is slightly more complex than the previous one because the server component has two attributes, named Header and Count, which can be controlled with the ServiceAttributes attribute controller interface.
<primitive-template name="..." implements="..." extends="..."> <primitive-content class="..."/> <controller> <attributes signature="..."> <attribute name="..." value="..."/> ... </attributes> <template-controller desc="..."/> <component-controller desc="..."/> </controller> </primitive-template>
The implements, extends, and signature attributes are optional, as well as the <primitive-content>, <controller>, <attributes>, <template-controller> and <component-controller> sub elements. The implements attribute can be omitted only when the extends attribute is defined. The <template-controller> and <component-controller> elements can be used to specify the controller descriptor of the template component, and of the components it instantiates. The semantics of the inheritance mechanism is defined in Appendix A.
<composite-template name="ClientServer" implements="RootType"> <composite-content> <components> <component name="client" type="ClientType"/> <component name="server" type="ServerType"/> </components> <bindings> <binding client="this.m" server="client.m"/> <binding client="client.s" server="server.s"/> </bindings> </composite-content> </composite-template>
Each <binding> element specifies a client interface and a server interface. Each interface is specified by the name of a sub component (or this to designate the composite template itself), followed by a dot, followed by the name of an interface of this component. The above definition just gives the type of the client and server sub components. In order to specify their implementation, one must extend the previous definition with a definition such as the following:
<composite-template name="ClientServerImpl" extends="ClientServer"> <composite-content> <components> <component name="client" implementation="ClientImpl"/> <component name="server" implementation="ServerImpl"/> </components> </composite-content> </composite-template>
This definition says that the client and server sub components are in fact the primitive components described by the ClientImpl and ServerImpl primitive templates. It is also possible to use composite components for the client and server sub components. For example, the following definitions describe a configuration where the client and server primitive components are wrapped inside intermediate composite components (as in section 2.2 of the Julia tutorial):
<composite-template name="WrappedClientImpl" implements="ClientType"> <composite-content> <components> <component name="client" type="ClientType" implementation="ClientImpl"/> </components> <bindings> <binding client="this.m" server="client.m"/> <binding client="client.s" server="this.s"/> </bindings> </composite-content> </composite-template>
<composite-template name="WrappedServerImpl" implements="ServerType"> <composite-content> <components> <component name="server" type="ServerType" implementation="ServerImpl"/> </components> <bindings> <binding client="this.s" server="server.s"/> </bindings> </composite-content> </composite-template>
<composite-template name="WrappedClientServerImpl" extends="ClientServer"> <composite-content> <components> <component name="client" implementation="WrappedClientImpl"/> <component name="server" implementation="WrappedServerImpl"/> </components> </composite-content> </composite-template>
It is possible to instantiate a template even if some sub component implementations are not defined, as long as all mandatory interfaces can be bound. For example, if the s interface in ClientType was optional, then it would be possible to instantiate the following composite template, where the implementation of the server component is not specified:
<composite-template name="ClientOptionalServerImpl" extends="ClientServer"> <composite-content> <components> <component name="client" implementation="ClientImpl"/> </components> </composite-content> </composite-template>
The result is a composite component that contains only one sub component.
<composite-template name="..." implements="..." extends="..."> <composite-content> <components> <component name="..." type="..." implementation="..."/> ... </components> <bindings> <binding client="..." server="..."/> ... </bindings> <sharing> <shared-component path="..." ref="..."/> ... </sharing> </composite-content> <controller> <attributes signature="..."> <attribute name="..." value="..."/> ... </attributes> <template-controller desc="..."/> <component-controller desc="..."/> </controller> </composite-template>
The implements, extends, type and signature attributes are optional, as well as the <composite-content>, <components>, <bindings>, <sharing> and <controller> sub elements. The implements attribute can be omitted only when the extends attribute is defined. The type attribute can be ommitted only when overriding a sub component definition. Bindings can only be defined if some sub components are defined in the current template, or in one of its super templates. Likewise for shared components, specified in the <sharing> element. In this element, a <shared-component> element means that the component designed by path (wich is a slash separated path relative to the current template) must be replaced by the component designed by ref (which is another path relative to the current template). The semantics of the inheritance mechanism is defined in Appendix A.
<external-template name="..." implements="..."> <reference name="..."/> </external-template>
All the attributes and sub elements are mandatory. The name attribute in the <reference> element is the name to be used to retrieve the already instantiated component. The org.objectweb.fractal.api.bootstrap name is reserved. It designates the fractal bootstrap component.
In order to instantiate a Fractal application with the Fractal ADL parser, one must first construct the Fractal ADL parser component itself. This can be done with the following code:
Parser parser = Launcher.getBootstrapParser();
The getBootstrapParser method constructs a bootstrap parser component with the Fractal API. This parser can then be used to load the template of the application:
Component tmpl = parser.loadTemplate("ClientServerImpl", true);
The application itself is then created by instantiating and starting this template component:
Component comp = Fractal.getFactory(tmpl).newFcInstance(); Fractal.getLifeCycleController(comp).startFc();
And that's all! It is even not necessary to use an intermediate template component, by using false in the second argument of the loadTemplate method:
Component comp = parser.loadTemplate("ClientServerImpl", false); Fractal.getLifeCycleController(comp).startFc();
It is also possible to launch an application without any Java code, directly from the command line, provided this application starts itself when constructed, or provides a run interface of type Runnable. In this case, the following command will directly start the application:
java ... org.objectweb.fractal.adl.Launcher template-name
If you want to use another parser component than the default bootstrap one, you can use the default parser to load the template of your own parser, instantiate this template, and use the resulting parser to load and instantiate your application as above:
Parser parser = Launcher.getBootstrapParser(); Component parserTmpl = parser.loadTemplate("MyParser"); Component parserComp = Fractal.getFactory(parserTmpl).newFcInstance(); parser = (Parser)parserComp.getFcInterface("parser"); Component tmpl = parser.loadTemplate("ClientServerImpl"); Component comp = Fractal.getFactory(tmpl).newFcInstance(); Fractal.getLifeCycleController(comp).startFc();
The type and template name hierarchy can be arbitrary but it is convenient to reuse the class name hierarchy for component type names and template names. For example, if a class foo.bar.IImpl implementing an interface foo.bar.I can be used as a Fractal component, then it is convenient to define a component type named foo.bar.I or foo.bar.IType, and a primitive template named foo.bar.IImpl for this Fractal component. Indeed, this way, the compiled classes and the corresponding XML type and template descriptors will be in the same directories:
foo/ bar/ I.class I.fractal IImpl.class IImpl.fractal
In order to get the compiled classes and the corresponding XML descriptors in the same directory, named for example build, it is convenient to define the .fractal files in the Java source tree, along with the .java files, and to copy them automatically in the build directory:
build/ foo/ bar/ I.class I.fractal // copy of src/foo/bar/I.fractal IImpl.class IImpl.fractal // copy of src/foo/bar/IImpl.fractal src/ foo/ bar/ I.java I.fractal IImpl.java IImpl.fractal
However it is not very practical to define each component type and each template in a separate file. The fractalc Ant task can be used to solve this problem: indeed it can split a file containing several component types and templates definitions, whose names and locations can be arbitrary, into several files containing only a single definition. This task takes as argument one or more files to be split, and the name of an output directory, and puts the splitted files, appropriately named, in appropriate sub directories of the given output directory. Thanks to this task it is possible to organize the source directory in the following way:
src/ foo/ bar/ I.java IImpl.java Components.fractal // contains the foo.bar.I and foo.bar.IImpl definitions
The build directory can then be obtained by calling the javac task and then the fractalc task:
<target name="compile"> <mkdir dir="${build}"/> <javac srcdir="${src}" destdir="${build}"> <include name="**/*.java"/> </javac> <fractalc srcdir="${src}" destdir="${build}"> <include name="**/*.fractal"/> </fractalc> </target>
The fractalc task can also verify the definitions after they have been copied into the output directory (with the verify="true" option). This can be useful to detect errors as soon as possible. Finally the fractalc task can also generate in the output directory the .kilim files corresponding to the Fractal type and template definitions (with the kilim="true" option).
<...> <a name="j" ...> ... </a> <a name="k" ...> ... </a> ... </...> |
<...> <a name="i" ...> ... </a> <a name="j" ...> ... </a> ... </...> |
e's sub elements | f's sub elements |
<...> <a name="i" ...> ... </a> <!-- copied from f as is --> <a name="j" ...> ... </a> <!-- merge of e's j sub element into f's j sub element --> <a name="k" ...> ... </a> <!-- copied from e as is --> ... </...> |
|
g's sub elements |
<...> <b ...> ... </b> <c ...> ... </c> ... </...> |
<...> <a ...> ... </a> <b ...> ... </b> ... </...> |
e's sub elements | f's sub elements |
<...> <a ...> ... </a> <!-- copied from f as is --> <b ...> ... </b> <!-- merge of e's b sub element into f's b sub element --> <c ...> ... </c> <!-- copied from e as is --> ... </...> |
|
g's sub elements |