|
|||||||||
PREV NEXT | FRAMES NO FRAMES |
See:
Description
Packages | |
---|---|
org.objectweb.fractal.julia | Provides mixins to implement the org.objectweb.fractal.api interfaces. |
org.objectweb.fractal.julia.asm | Provides some class generators based on ASM. |
org.objectweb.fractal.julia.control.attribute | Provides mixins to implement the AttributeController interface. |
org.objectweb.fractal.julia.control.binding | Provides mixins to implement the BindingController interface. |
org.objectweb.fractal.julia.control.content | Provides mixins to implement the ContentController interface. |
org.objectweb.fractal.julia.control.lifecycle | Provides mixins to implement the LifeCycleController interface. |
org.objectweb.fractal.julia.control.name | Provides mixins to implement the NameController interface. |
org.objectweb.fractal.julia.factory | Provides mixins to implement the [Generic]Factory interfaces. |
org.objectweb.fractal.julia.loader | Provides a loader to load or to generate classes on the fly. |
org.objectweb.fractal.julia.logger | Provides a mixin to assign a Logger to
components when they are started. |
org.objectweb.fractal.julia.type | Provides mixins to implement the TypeFactory interface. |
org.objectweb.fractal.juliak | Fractal provider for component-based control membranes. |
org.objectweb.fractal.juliak.asm | Provides some class generators based on ASM for component-based control membranes. |
org.objectweb.fractal.juliak.control.attribute |
Provides the definition of the
AttributeController
control component. |
org.objectweb.fractal.juliak.control.binding |
Provides the definition of the
BindingController
control component. |
org.objectweb.fractal.juliak.control.component |
Provides the definition of the
Component
control component. |
org.objectweb.fractal.juliak.control.content |
Provides the definition of the
ContentController
and the
SuperController
control components. |
org.objectweb.fractal.juliak.control.factory |
Provides the definition of the
Factory
control component. |
org.objectweb.fractal.juliak.control.interceptor | This package contains the definition of the interceptor control component. |
org.objectweb.fractal.juliak.control.lifecycle |
Provides the definition of the
LifeCycleController
control component. |
org.objectweb.fractal.juliak.control.membrane |
Provides the definition of the
MembraneController
control component. |
org.objectweb.fractal.juliak.control.name |
Provides the definition of the
NameController
control component. |
org.objectweb.fractal.juliak.factory | This package contains the implementations of Fractal factories. |
org.objectweb.fractal.juliak.membrane | This package contains the Fractal ADL definitions of control membranes. |
org.objectweb.fractal.juliak.platform | This package contains the code which is platform (e.g. |
1 Introduction
2 Design goals
3 Code structure
4 Main data structures
5 Mixin classes
6 Interceptor classes
7 Class generation framework
8 Support for constrained environments
9 Component-based control membranes
A Mixins reference
B Optimizations
The second goal is to provide a complete continuum from static configuration to dynamic reconfiguration: we want to provide a sufficiently rich and flexible set of control objects, so that the user can make the speed/memory tradeoffs he or she wants. In particular, this set of control objects must allow the user to instantiate non reconfigurable but very efficient components or, on the contrary, completely reconfigurable but less efficient components. It must also allow the user to mix these components, in order to use different optimization levels for the different parts of an application. The last requirement is to be able to dynamically deoptimize an optimized component, in order to make it reconfigurable, and to be able to dynamically reoptimize it after it has been reconfigured.
The third goal is to implement these control objects so as to minimize the time overhead due to these objects on user applications and to optimize the performances of the methods of the Fractal API. The memory overhead of the controller objects was not a priority (indeed, in many cases, the number of components is less than one hundred or a thousand of components).
The last goal is to implement a framework that can be used on any JVM and/or JDK, including very constrained ones such as the KVM and the J2ME profile (where there is no ClassLoader class, no reflection API, no collection API...).
In addition to the previous design goals, we also make two hypotheses in order to simplify the implementation:
Figure 1: an abstract component and a possible implementation in Julia
A Fractal component is generally represented by many Java objects, which can be separated into three groups (see Figure 1):
The objects that represent the controller part of a component can be separated into two groups: the objects that implement the control interfaces (in black in the above figure), and (optional) interceptor objects (in brown) that intercept incoming and/or outgoing method calls on functional (or business, or user) interfaces. These objects implement respectively the Controller and the Interceptor interfaces. Each controller object can contain references (in black) to other controller objects (since the control aspects are generally not independent - or "orthogonal" - they must generally communicate between each other, and must therefore have references to each other).
The objects that implement the content part of a component can be sub components (for composite components), or user objects (for primitive components). Note: Julia makes a distinction between the primitive components that contain no user objects (such as primitive template components), which are just called "primitive", and primitive components that contain a non empty rooted graph of user objects, which are called "container" (this name can lead to confusions with the fuzzy EJB and CCM container definitions, but we do not have a better name to express the fact that these components contain a non empty set of user objects).
These hypotheses also explain why the component interface objects are separated from controller and interceptors objects: this is to be able to change the controller and interceptor objects.
(*) the only exception is when user objects are bound directly to each other, in a "static" configuration (see section 2.4 in the Julia tutorial). In this case there may be unknowable references to content objects (but not to controller or interceptor objects). In this case the application must not disseminate these references, or must know and update them all when requested to do so (via BindingController), or else there may be problems when a user component is replaced by another.
In order to be able to add protection mechanisms to Julia, or to be able to provide distributed components on top of Julia (as Fractal RMI does), it was also decided that the previous interfaces, only used inside Julia but not visible from the outside, would be retrieved like any other component interface, i.e., via the getFcInterface method of the Component interface. The only difference is that these interfaces are hidden, i.e., they do not appear in the array returned by getFcInterfaces (hidden interfaces are distinguished from normal interfaces by the fact that their name starts with "/"). Therefore, one cannot get the reference to such an interface without knowing its name. And, although not yet implemented, it is also possible to perform access control checks in the getFcInterface method, to ensure that only Julia's classes can access these hidden interfaces.
(*) a reference between an interceptor and a controller object may not follow this principle, for performance reasons. For example, the lifecycle interceptor object directly uses fields defined in the lifecycle controller object. In practice this is not a problem, because the algorithms used in these two objects are strongly coupled (i.e., the interceptor can not be changed without changing the controller object, and vice versa).
In order to provide all these implementations, a first solution would be to use class inheritance. But this solution is not feasible, because it leads to a combinatorial explosion, and to a lot of code duplication. Consider for example the BindingController interface, and the "type system", "life cycle" and "composite" concerns. These three concerns give 23=8 possible combinations. Therefore, in order to implement these three concerns, eight classes (and not just three) must be provided. Moreover these eight classes can not be provided without duplicating code, if multiple inheritance is not available.
Another solution to this problem would be to use an Aspect Oriented Programming (AOP) tool or language, such as AspectJ, since the goal of these tools and languages is to solve the "crosscutting" problems. AspectJ, for example, could effectively be used to solve the above problem: three aspect classes are sufficient to implement the three concerns, without any code duplication. But using AspectJ would introduce a new problem, due to the fact that, in AspectJ, aspects must be applied at compile time, and that this process requires the source code of the "base" classes. It would then be impossible to distribute Julia in compiled form, in a jar file, because then the users would not be able to apply new aspects to the existing Julia classes (in order to add new control aspects that crosscut existing ones).
What is needed to really solve our modularity and extensibility problem is therefore a kind of AOP tool or language that can be used at load time or at runtime, without needing the source code of the base classes, such as JAC (Java Aspect Components). For performance reasons the current Julia version does not use JAC or other similar systems: it uses instead some kind of mixin classes. A mixin class is a class whose super class is specified in an abstract way, by specifying the minimum set of fields and methods it should have. A mixin class can therefore be applied (i.e. override and add methods) to any super class that defines at least these fields and methods. This property solves the above combinatory problem. The AspectJ problem is solved by the fact that the mixin classes used in Julia can be applied at runtime (unlike in most mixin based inheritance languages, where mixed classes are declared at compile time).
mixin A { inherited public void m (); public int count; public void m () { ++count; super.m(); } }is written in Julia in pure Java, as follows:
abstract class A { abstract void _super_m (); public int count; public void m () { ++count; _super_m(); } }In other words, the _super_ prefix is used to denote the inherited members in JAM, i.e., the members that are required in a base class, for the mixin class to be applicable to it. More precisely, the _super_ prefix is used to denote methods that are overridden by the mixin class. Members that are required but not overridden are denoted with _this_ (a mixin class cannot contain a _super_m method if it does not have a corresponding m method. Likewise, a mixin class cannot have both a _this_m method and a corresponding m method):
abstract class M implements I, Countable { int _this_f; abstract void _super_m (); abstract void _this_n (); public int count; public void m () { ++count; _this_n(); _super_m(); } public int getCount () { return count; } }Mixin classes can be mixed, resulting in normal classes. More precisely, the result of mixing several mixin classes M1, ... Mn, in this order, is a normal class that is equivalent to a class Mn extending the Mn-1 class, itself extending the Mn-2 class, ... itself extending the M1 class. Several mixin classes can be mixed only if each method and field required by a mixin class Mi is provided by a mixin class Mj, with j < i (each required method and field may be provided by a different mixin). For example, if N and O designate the following mixins:
abstract class N implements I { abstract void _super_m (); abstract void _super_n (); public void m () { System.out.println("m called"); _super_m(); } public void n () { System.out.println("n called"); _super_n(); } } abstract class O implements I { public int f; public void m () { System.out.println("m"); } public void n () { System.out.println("n"); } }then the mixed class O N M is equivalent to the following class (note that this class implements all the interfaces implemented by the mixin classes):
public class ONM implements I, Countable { // from O public int f; private void m$1 () { System.out.println("m"); } private void n$0 () { System.out.println("n"); } // from N private void m$0 () { System.out.println("m called"); m$1(); } public void n () { System.out.println("n called"); n$0(); } // from M public int count; public void m () { ++count; n(); m$0(); } public int getCount () { return count; } }while the mixed class O M N is the following class:
public class OMN implements I, Countable { // from O public int f; private void m$1 () { System.out.println("m"); } private void n$0 () { System.out.println("n"); } // from M public int count; private void m$0 () { ++count; n(); m$1(); } public int getCount () { return count; } // from N public void m () { System.out.println("m called"); m$0(); } public void n () { System.out.println("n called"); n$0(); } }The mixed classes are generated dynamically by the MixinClassGenerator class. Since this class can not mix constructors (this limitation could be removed in future versions), a mixin class must not rely on constructors to initialize its instances (in Julia, all mixin classes have a private empty constructor without argument, to show that mixin classes should not be instantiated directly). A mixed class can be declared as follows in the Julia configuration file:
(onm (org.objectweb.fractal.julia.asm.MixinClassGenerator ONM org.pkg.O org.pkg.N org.pkg.M ) )The first line after MixinClassGenerator is a symbolic name for the mixed class. The following lines are the names of the classes to be mixed. In order to ease debugging, the class generator keeps the line numbers of the mixin classes in the mixed class. More precisely, a line number l of the mixin class at index i (in the list of mixin classes, and starting from 1) is transformed into 1000*i + l. For example, if a new Exception().printStackTrace() were added in the N.m method, the stack trace would contain the following line:
at C55d992cb_0.m$0(ONM:2010)
meaning that the exception was created in method m$0 of the C55d992cb_0 class, whose source is the ONM mixed class (ONM comes from the first line after MixinClassGenerator in the above descriptor), at line 10 of mixin 2, i.e. at line 10 of the org.pkg.N mixin class.
Each "aspect" code generator can modify the code of each application specific method arbitrarily. For example, an "aspect" code generator A can modify the method void m () { impl.m() } into:
while another "aspect" code generator B will modify this method into:void m () { // pre code A try { impl.m(); } finally { // post code A } }
When an interceptor class is generated by using several "aspect" code generators, the transformations performed by these code generators are automatically composed together. For example, if A and B are used to generate an interceptor class, in this order, the result for the previous m method is the following:void m () { // pre code B impl.m(); // post code B }
The order in which the aspects are "woven" is generally important, and must be specified by the user of the code generator. For example, if A and B are used in the reverse order, the result for the previous m method is the following:void m () { // pre code A try { // pre code B impl.m(); // post code B } finally { // post code A } }
Note that, thanks to this (elementary) automatic weaving, which is very similar to what can be found in AspectJ or in Composition Filters, several aspects can be managed by a single interceptor object: there is no need, and there are no chains of interceptor objects, each object corresponding to an aspect (if one really wants interceptor chains, this can be done by implementing a new interceptor class generator).void m () { // pre code B // pre code A try { impl.m(); } finally { // post code A } // post code B }
Like the controller objects, the aspects managed by the interceptor objects of a given component can all be specified by the user when the component is created. The user can therefore not only choose the control interfaces he or she wants, but also the interceptor objects he or she wants. Julia only provides two aspect code generators: one to manage the lifecycle of components, the other to trace incoming and/or outgoing method calls. Julia also provides two abstract code generators, named SimpleCodeGenerator and MetaCodeGenerator, that can be easily specialized (i.e. without needing to know ASM), in order to implement custom code generators.
Note: each aspect code generator is given a Method object corresponding to the method for which it must generate interception code. Thanks to this argument, a code generator can generate code that adapts to the specific signature of each method. It can also generate code to reify the method's parameters if desired (although this is less efficient than the first method).
Since Julia must work even with very limited JVMs (see section 2), without application specific class loaders, and without the Java reflection API, the generators that generate the previous classes must be useable either dynamically or statically, before launching the application. A convention is therefore needed to name the generated classes. Indeed, in the static case, the "generated" classes must be loaded from the classpath and, to this end, their name must be known. And, therefore, the static class generator cannot give arbitrary names to the class it generates.
A solution to this problem is to name the generated classes with the parameters that were used to generate them. Indeed, in this case, when a generated class is needed at runtime, and since the parameters to generate it are of course known at this time, the class can either be dynamically generated with these parameters or, in the static case, loaded with Class.forName method, by using these parameters as a class name.
The problem of this solution is that the parameters used to generate a class are quite long, and this would result in very long, and may be too long, generated class names (some JVMs do not accept classes with a name of more than 256 characters). In order to solve this problem, the name of a generated class is the hashcode of the parameters that were used to generate this class, instead of being a bijective encoding of these parameters. Of course, two different sets of parameters can have the same hashcode, and so conflicts are possible. To solve this new problem, each generated class contains a getFcGeneratorParameters method, defined in the Generated interface, whose role it to return the exact parameters that were used to generate the class. The algorithm to load a generated class is then the following:
Notes:hashcode = hashcode(parameters needed to generate the desired class); int n = 0; while (true) { Class c = Class.forName("package." + hashcode + "_" + n); Generated g = (Generated)c.newInstance(); if (g.getFcGeneratorParameters().equals(above parameters)) { return c; } ++n; }
Since version 2.5, the control membrane of a component can be implemented with an assembly of so-called control components. This feature which were originally developed for AOKell, has been ported to Julia. This new feature does not modify any of the existing elements of the Julia framework: existing applications run unmodified and the various features of the framework up to advanced ones such as intra and inter components optimizations (see appendix B) are still supported without any modification.
The code related to the implementation of component-based control membranes (CBCM) can be found in the org.objectweb.fractal.juliak package and its sub packages. A new Fractal provider class, org.objectweb.fractal.juliak.Juliak, is available. When used, this class provides a component factory which instanciates application-level components with component-based membranes. This class can coexist with the original Fractal provider class org.objectweb.fractal.julia.Julia. Both provider classes can be instanciated and used jointly in a same JVM. The components created by the generic factories of these providers are fully-compatible (they implement the same control semantics) and can be assembled together in a same application.
The rationale for using component-based control membrane is to be able to (re)configure the control part of a Fractal component. This control part (the membrane) is a component (a so-called control component) which exports some control interfaces. Control components conform to the Fractal API. They can be introspected and dynamically reconfigured.
No restriction is put on the implementation of a control membrane: this can be a simple primitive component, or this can be a more complex composite component which contains several levels of sub-components with arbitrary bindings. The idea behind implementing the membrane as an assembly of control components stems from the fact that controllers are not isolated entities. For example, the standard Julia binding controller for composite components uses the services provided by the content controller, the lifecycle controller and the controller implementing the Component interface. Appendix A lists the dependencies which exist between standard Julia controllers: each dependency can be deduced by the presence of a mixin layer whose name starts with Use. By reifying these dependencies with a Fractal component-based architecture, we are able to dynamically (re)configure the control part of a component.
The general idea underlying CBCM is summarized in Figure 2. An application-level component can be equipped with an assembly of control components. Each control component implements the functionalities of a control interface. Note however that the use of component-based control membranes is not mandatory. Developers can still use regular object-based control layers. Furthermore, component-based and object-based membranes can coexist in a same assembly of application-level components.
Figure 2: component-based control membrane
The use of component-based control membrane does not modify the mixin mechanism which is used in Julia for generating controller implementations. Control components have a content class. This can be a plain old Java class or a class outputted by any generative programming mechanism such as the mixin algorithm.
A component-based version of the 15 standard control membranes (bootstrap
,
primitive
, composite
, parametricPrimitive
,
parametricComposite
,
primitiveTemplate
, compositeTemplate
, parametricPrimitiveTemplate
,
parametricCompositeTemplate
, autoBindingPrimitive
,
autoBindingComposite
,
flatPrimitive
, flatParametricPrimitive
, flatPrimitiveTemplate
,
flatParametricPrimitiveTemplate
) existing in Julia has been defined. The
Fractal-ADL architecture description of these membranes can be found in the
org.objectweb.fractal.juliak.membrane
package. The implementation of the control components used by these membranes is
the same as the one used for the object-oriented membranes: their code is
generated with the mixin layers defined in the
org.objectweb.fractal.julia
package and its sub packages.
This section illustrates the definition of a control membrane with Fractal ADL. We take the example of a primitive membrane which is the most commonly used membrane type.
<!-- o.o.f is a shortcut for org.objectweb.fractal -->
<definition name="o.o.f.juliak.membrane.Primitive"
extends="o.o.f.juliak.control.component.ComponentControllerType,
o.o.f.juliak.control.binding.BindingControllerType,
o.o.f.juliak.control.lifecycle.LifeCycleControllerType,
o.o.f.juliak.control.name.NameControllerType,
o.o.f.juliak.control.content.SuperControllerType,
o.o.f.juliak.control.interceptor.InterceptorControllerType,
o.o.f.juliak.control.membrane.MembraneControllerType" >
<component name="Comp"
definition="o.o.f.juliak.control.component.ComponentController" />
<component name="BC"
definition="o.o.f.juliak.control.binding.ContainerBindingController"
/>
<component name="LC"
definition="o.o.f.juliak.control.lifecycle.LifeCycleController"
/>
<component name="NC"
definition="o.o.f.juliak.control.name.NameController" />
<component name="SC"
definition="o.o.f.juliak.control.content.SuperController" />
<component name="IC"
definition="o.o.f.juliak.control.lifecycle.LifeCycleInterceptor" />
<component name="MC"
definition="o.o.f.juliak.control.membrane.MembraneController" />
<binding client="this.//component" server="Comp.//component" />
<binding client="this.//binding-controller" server="BC.//binding-controller"
/>
<binding client="this.//lifecycle-controller"
server="LC.//lifecycle-controller"
/>
<binding client="this.//name-controller" server="NC.//name-controller" />
<binding client="this.//super-controller" server="SC.//super-controller"
/>
<binding client="this.///interceptor-controller"
server="IC.///interceptor-controller"
/>
<binding client="BC.//component" server="Comp.//component" />
<binding client="BC.//super-controller" server="SC.//super-controller" />
<binding client="BC.//lifecycle-controller"
server="LC.//lifecycle-controller"
/>
<binding client="LC.//component" server="Comp.//component" />
<binding client="IC.//component" server="Comp.//component" />
<binding client="IC.//lifecycle-controller"
server="LC.//lifecycle-controller"
/>
<controller desc="mComposite" />
</definition>
This membrane contains 7 sub components, exports the server interfaces of these components, and defines 6 bindings between them. Of course, such an architecture stems from the chosen control semantics for primitive components. Two pieces of data in such a definition are specific to CBCM:
mComposite
controller description. This identifier
specifies that the component is a composite control component.
Similarly, the mPrimitive
controller description is available for
qualifying primitive control components.//
. This stems from a naming clash between the
application and control levels. To understand where the clash comes from, let
consider the binding controller. This controller must provide a
binding-controller interface which is used for binding application-level
components. As a control component, this controller also needs a
binding-controller interface to be bound to its peer controllers. Hence the
clash between these two binding-controller interface names. The issue has been
solved by using the //
prefix for interface names which correspond to the
services provided by a control component to its associated application-level
component. This convention is only used when defining the control
architecture. At the application level, the control interfaces are still
identified with regular names such as binding-controller.Four features are specific to CBCM:
CBCM are regular assemblies of Fractal components. They can be introspected with the Fractal API: component hierarchies can be traversed and modified, interfaces can be retrieved, and bindings can be queried, set and removed. By this way, one is able to dynamically modify at run-time the control policy attached to an application-level component. The consistency of the modification is under the responsibility of the developer: s/he must take care that the performed changes do not hinder the currently executing component.
In order to introspect a control membrane, the developer needs to retrieve the
reference of the
Component instance which plays this role. For that, every component created
by the
Juliak Fractal provider class is equipped with a control interface
named /membrane-controller
. This Fractal interface implements the
MembraneController Java interface which provides the following method:
public Component getFcMembrane();
This method returns the reference of the control membrane associated to the current application-level component. A typical sequence of code for introspecting a control membrane will then starts with:
Component myComp = ...
MembraneController mc = (MembraneController)
myComp.getFcInterface("/membrane-controller");
Component membrane = mc.getFcMembrane();
The component factory provided by the
Juliak Fractal provider class implements the standard
GenericFactory interface. This interface provides the
newFcInstance method
which returns a component instance. Three parameters must be provided: a
Type to describe the requested instance, a controller descriptor and a
content descriptor. In regular cases, the
Type is a
ComponentType, the controller descriptor is a string such as primitive
or composite
which designates the type of the requested membrane, and
the content descriptor is the fully-qualified name which corresponds to the class
implementing the content of the
component (may be null
when a composite component is requested).
In addition to this regular usage pattern of the Juliak Fractal provider class, the controller descriptor may be a Component instance. This Component instance is taken to be the control part for the created application-level component. This instance is a regular component which may be obtained by any mean provided by Fractal to generate a component. For example, it may be loaded from a Fractal-ADL description, created by the generic factory or a template, or assembled with the API from existing parts. Two conditions must be met by this Component instance to be a legal control membrane:
mPrimitive
or mComposite
,//Component
interface of type
Component.In addition, only server interfaces with a //
prefix are
considered to be control interfaces for the application-level component.
An interceptor controller is provided by CBCM to be able to dynamically add and remove interceptors to a component. The consistency of the adding and the removal of an interceptor is under the responsibility of the developer who performs the change. The interceptor controller implements the InterceptorController interface.
The interceptor controller is defined as follows:
<!-- o.o.f is a shortcut for org.objectweb.fractal -->
<definition name="o.o.f.juliak.control.interceptor.InterceptorController"
extends="o.o.f.juliak.control.component.UseComponentControllerType" >
<interface
name="///interceptor-controller"
signature="o.o.f.juliak.control.interceptor.InterceptorController"
role="server"
/>
<content
class="o.o.f.juliak.control.interceptor.InterceptorCompControllerImpl"
/>
<controller desc="mPrimitive" />
</definition>
An interceptor controller is a mPrimitive
control
component which exports a /interceptor-controller
interface and
which is implemented by the InterceptorCompControllerImpl
.
Note also that this
controller must be bound to the
Component controller.
The definition of the interceptor controller can be extended to specify
custom code generators for interceptors. This is the case of the
LifeCycleInterceptor
control component which is included in the
definition of a Primitive
membrane. Its definition follows:
<!-- o.o.f is a shortcut for org.objectweb.fractal -->
<definition name="o.o.f.juliak.control.lifecycle.LifeCycleInterceptor"
extends="o.o.f.juliak.control.interceptor.InterceptorController,
o.o.f.juliak.control.lifecycle.UseLifeCycleControllerType" >
<attributes
signature="o.o.f.juliak.control.interceptor.InterceptorDefAttributes">
<attribute
name="interceptors"
value="(o.o.f.julia.asm.InterceptorClassGenerator
o.o.f.julia.asm.LifeCycleCodeGenerator)"
/>
</attributes>
</definition>
The interceptors
attribute in the previous piece of Fractal-ADL
code specifies the class generator description which is attached to the
LifeCycleInterceptor
control component. The syntax of this description
follows the Lisp-like style which is used in julia.cfg
files. When
a control membrane containing such an interceptor control component is bound to
a content instance, interceptors are generated and attached to the interfaces of
the component.
As said previously, the generic factory associated with the org.objectweb.fractal.juliak.Juliak
Fractal provider class instanciates components equipped with a component-based
control membrane. The membrane is choosen according to the value of the
controller descriptor parameter sent to the
newFcInstance method. This parameter is a string such as primitive
or composite
.
As an additional feature, the generic factory associated with the org.objectweb.fractal.juliak.Juliak
Fractal provider class provides the ability to instanciate components equipped
with an object-oriented membrane. For that, a naming convention is adopted which
consists in inserting the /julia/
prefix in front of controller
descriptors. For example, the programmer may request a /julia/primitive
or a /julia/composite
component. In such cases, this factory
delegates the instanciation to the factory associated with the
org.objectweb.fractal.julia.Julia Fractal provider class.
As a matter of example, the following piece of code create one composite and two primitive components. The primitive ones are inserted into the composite and are bound together. The composite and the second primitive are equipped with a component-based membrane. The first primitive is equipped with an object-oriented membrane (see the second parameter of the call to newFcInstance on line 2).
Component root = cf.newFcInstance(rType,"composite",null);
Component cComp = cf.newFcInstance(cType,"/julia/primitive,"ClientImpl");
Component sComp = cf.newFcInstance(sType,"primitive,"ServerImpl");
Fractal.getContentController(root).addFcSubComponent(cComp);
Fractal.getContentController(root).addFcSubComponent(sComp);
Fractal.getBindingController(root).bindFc("m",cComp.getFcInterface("s"));
Fractal.getBindingController(cComp).bindFc("s",sComp.getFcInterface("s"));
By default, components with object-oriented and component-based membranes implement the same control semantics and are thus fully compatible.
Figure 3: mixins for the Component interface
Figure 4: mixins for the TypeFactory interface
Figure 5: mixins for the GenericFactory interface
Figure 6: mixins for the Factory interface
Figure 7: mixins for the BindingController interface
Figure 8: mixins for the ContentController interface
Figure 9: mixins for the SuperController interface
Figure 10: mixins for the LifeCycleController interface
Figure 11: mixins for the NameController interface
When object-oriented control membrane are used, it is always possible to merge the controller objects into a single one (which is not the case with component-based ones). The solution is the following:
In the non optimized case, a component with these three controller objects is instantiated in the following steps:public class IImpl implements Controller, I { // indicates a required interface of type J public J weaveableJ; // indicates an optional required interface of type L public L weaveableOptL; // other fields: public int foo; // implementation of the Controller interface public void initFcController (InitializationContext ic) { weaveableJ = (J)ic.getInterface("j"); weaveableOptL = (L)ic.getOptionalInterface("l"); } // other methods: public void foo (String name) { weaveableJ.bar(weaveableOptL, foo, weaveableC.getFcInterface(name)); } } public class JImpl implements Controller, J { public K weaveableK; // implementation of the Controller interface ... public void initFcController (InitializationContext ic) { weaveableK = (K)ic.getInterface("k"); } // other methods (not shown) ... } public class KImpl implements Controller, K { // other methods ... }
Notes:public class Cb234f2 implements Controller, I, J, K, ... { // fields copied from the IImpl class: public int foo; // fields copied from the JImpl class: none // fields copied from the KImpl class: none // methods copied from IImpl: public void foo (String name) { bar(null, foo, getFcInterface(name); } // methods copied from JImpl (not shown) ... // methods copied from KImpl (not shown) ... // merged initFcController method public void initFcController (InitializationContext ic) { (J)ic.getInterface("j"); (L)ic.getOptionalInterface("l"); (K)ic.getInterface("k"); } }
As explained in section 4.1, each interface of a component contains an "impl" link to an object that really implements the component interface. In the case of a server interface s, this link generally points to an interceptor object, which itself points to the server interface to which the complementary client interface of s is bound:
Figure 12: implementation of bindings with CompositeBindingMixin
Figure 13: implementation of bindings with OptimizedCompositeBindingMixin
|
|||||||||
PREV NEXT | FRAMES NO FRAMES |