SpeADL Minus Reference » Historique » Version 46
Anonyme, 15/10/2014 16:13
| 1 | 44 | Anonyme | h1. SpeADL⁻ Reference |
|---|---|---|---|
| 2 | 1 | Anonyme | |
| 3 | 2 | Anonyme | {{>toc}} |
| 4 | 1 | Anonyme | |
| 5 | 44 | Anonyme | In SpeADL, a subset of abstractions are provided to define traditional component-oriented architectures. |
| 6 | This subset of SpeADL is sometimes called SpeADL⁻ (pronounced SpeADL Minus). |
||
| 7 | 45 | Anonyme | With it, it is possible to define software components and compositions of components, called composites, implemented in Java. |
| 8 | A strong link between definition and implementation is kept by relying on an Eclipse plugin and automatic code generation. |
||
| 9 | 1 | Anonyme | |
| 10 | A component is made of two elements: a class definition using SpeADL and an implementation using Java. |
||
| 11 | 45 | Anonyme | The SpeADL definition acts a type declaration but can also describe a composition of components connected together. |
| 12 | From the SpeADL definition, an abstract Java class (which reflects the definition) is automatically generated. |
||
| 13 | This abstract Java class can be then extended to implement what is left in the component in a type-safe manner. |
||
| 14 | 1 | Anonyme | |
| 15 | 35 | Anonyme | h2. Terminology |
| 16 | |||
| 17 | The reader can refer to the [[MAY Terminology]] document to get an overview of the different terms used in SpeADL. |
||
| 18 | |||
| 19 | 1 | Anonyme | h2. Namespaces |
| 20 | |||
| 21 | 46 | Anonyme | A namespace plays the same role as a package in Java except that it is not tied to a particular directory hierarchy. |
| 22 | 5 | Anonyme | |
| 23 | 4 | Anonyme | h3. Keyword |
| 24 | |||
| 25 | 31 | Anonyme | Namespaces are declared using the keyword *namespace*. |
| 26 | 1 | Anonyme | |
| 27 | 4 | Anonyme | h3. Details |
| 28 | |||
| 29 | 1 | Anonyme | In a SpeADL file, there can be many as namespace (as well as nested ones) as wanted. |
| 30 | 31 | Anonyme | Hence a namespace does not have to follow the name of the directory it is located (as it is the case in Java). |
| 31 | 1 | Anonyme | |
| 32 | 4 | Anonyme | Each namespace declaration can contain any component as desired as we are going to see. |
| 33 | |||
| 34 | h3. Example |
||
| 35 | |||
| 36 | 1 | Anonyme | <pre> |
| 37 | namespace simple { |
||
| 38 | |||
| 39 | namespace things { |
||
| 40 | 19 | Anonyme | |
| 41 | 1 | Anonyme | } |
| 42 | } |
||
| 43 | |||
| 44 | 19 | Anonyme | namespace simple.things { |
| 45 | |||
| 46 | } |
||
| 47 | |||
| 48 | 1 | Anonyme | namespace simple.stuffs { |
| 49 | 19 | Anonyme | |
| 50 | 1 | Anonyme | } |
| 51 | </pre> |
||
| 52 | |||
| 53 | h2. Imports |
||
| 54 | |||
| 55 | 20 | Anonyme | As in Java, it is possible to import existing names to avoid referring to them with their fully qualified name (i.e., including their package or namespace). |
| 56 | 1 | Anonyme | |
| 57 | 5 | Anonyme | h3. Keyword and Role |
| 58 | |||
| 59 | As in Java, this is done with the keyword *import*. |
||
| 60 | |||
| 61 | h3. Details |
||
| 62 | |||
| 63 | The namespace of components are also considered to import component class definitions. |
||
| 64 | |||
| 65 | 33 | Anonyme | The imports can be automatically handled and reorganised in Eclipse using the *Ctrl-Shift-O* shortcut as with the Java editor. |
| 66 | 5 | Anonyme | |
| 67 | 33 | Anonyme | They are always situated at the top of a SpeADL file. |
| 68 | 5 | Anonyme | |
| 69 | h3. Example |
||
| 70 | |||
| 71 | 1 | Anonyme | The syntax is similar to Java: |
| 72 | <pre> |
||
| 73 | import java.util.Collection |
||
| 74 | import java.util.* |
||
| 75 | import simple.stuffs.* |
||
| 76 | </pre> |
||
| 77 | |||
| 78 | 33 | Anonyme | h2. Component Definition |
| 79 | 1 | Anonyme | |
| 80 | 33 | Anonyme | A software component is made of a definition and an implementation: an instance can then be created from the implementation. |
| 81 | A component definition has provided and required ports, as well as parts which are themselves components. |
||
| 82 | 1 | Anonyme | A part is structurally similar to a class member in Java. |
| 83 | 5 | Anonyme | For each required port of a part, there must be a binding declaring what is providing the required port. |
| 84 | 1 | Anonyme | |
| 85 | h3. Keywords |
||
| 86 | |||
| 87 | 33 | Anonyme | A component definition is declared with the keyword *component* followed by a name starting with a capital letter. |
| 88 | It must be unique in its namespace. |
||
| 89 | 1 | Anonyme | |
| 90 | 43 | Frédéric Migeon | The keywords *provides* and *requires* are used to declare, respectively, provided and required ports. They are followed by a name (unique in the component and without capital letter) and an interface name separated by the character *: *. |
| 91 | 1 | Anonyme | It takes the form _provides name: Interface_ or _requires name: Interface_, where _Interface_ is a Java type. |
| 92 | |||
| 93 | 31 | Anonyme | The keyword *part* is used to declare a part in a component. |
| 94 | 33 | Anonyme | It is followed by a name for the part (unique in the component and without capital letter) and a component class definition name separated by the character *: *. |
| 95 | 31 | Anonyme | It takes the form _part name: ComponentName_. |
| 96 | 5 | Anonyme | |
| 97 | 33 | Anonyme | For each part, bindings are used to declare for each of its required port what is fulfilling the requirement. |
| 98 | It is done using the keywords *bind* and *to* in the form _bind ...1 to ...2_ where _...1_ is the name of the required port of the part and _...2_ is a reference to a port available in the component containing the part. |
||
| 99 | 5 | Anonyme | Such a reference can either be: |
| 100 | 31 | Anonyme | * To another part's provided port, taking the form _bind req to name.port_. |
| 101 | * A provided or a required port of the current containing component, taking the form _bind req to port_. |
||
| 102 | 5 | Anonyme | |
| 103 | 31 | Anonyme | A delegation is used to declare for the provided port of a component what other port will provides its implementation. |
| 104 | 33 | Anonyme | It is done using the keyword * = * followed by a reference to a port available in the current containing component (as with bindings). |
| 105 | 31 | Anonyme | It takes the form _provides name: Interface = name.port_ or _provides name: Interface = port_. |
| 106 | 5 | Anonyme | |
| 107 | h3. Details |
||
| 108 | |||
| 109 | 31 | Anonyme | An interface is understood as a Java interface, i.e., a collection of methods, and must be visible in the classpath of the Java project. |
| 110 | 5 | Anonyme | |
| 111 | 33 | Anonyme | All the required of a part must be bound for the component definition to be valid. |
| 112 | 1 | Anonyme | |
| 113 | 5 | Anonyme | h3. Example |
| 114 | |||
| 115 | 6 | Anonyme | Component class definitions: |
| 116 | 1 | Anonyme | <pre> |
| 117 | import my.interfaces.* |
||
| 118 | |||
| 119 | namespace simple.stuffs { |
||
| 120 | |||
| 121 | component MySimpleComponent { |
||
| 122 | provides p1: AnotherJavaInterface |
||
| 123 | } |
||
| 124 | |||
| 125 | component MyBeautifulComponent { |
||
| 126 | provides portName: AJavaInterface |
||
| 127 | requires anotherPortName: AnotherJavaInterface |
||
| 128 | } |
||
| 129 | |||
| 130 | 5 | Anonyme | component MyComplexComponent { |
| 131 | |||
| 132 | provides p1: AnotherJavaInterface |
||
| 133 | provides p2: AnotherJavaInterface = s.p1 |
||
| 134 | requires p3: AnotherJavaInterface |
||
| 135 | 1 | Anonyme | |
| 136 | 5 | Anonyme | part b1: MyBeautifulComponent { |
| 137 | bind anotherPortName to s.p1 |
||
| 138 | } |
||
| 139 | 1 | Anonyme | |
| 140 | 5 | Anonyme | part b2: MyBeautifulComponent { |
| 141 | bind anotherPortName to p1 |
||
| 142 | } |
||
| 143 | |||
| 144 | part b3: MyBeautifulComponent { |
||
| 145 | bind anotherPortName to p3 |
||
| 146 | } |
||
| 147 | |||
| 148 | part s: MySimpleComponent |
||
| 149 | |||
| 150 | } |
||
| 151 | } |
||
| 152 | </pre> |
||
| 153 | |||
| 154 | Interface definition in Java: |
||
| 155 | 1 | Anonyme | <pre> |
| 156 | package my.interfaces; |
||
| 157 | |||
| 158 | public interface AJavaInterface { |
||
| 159 | public String aMethod(Integer param1); |
||
| 160 | } |
||
| 161 | </pre> |
||
| 162 | |||
| 163 | <pre> |
||
| 164 | package my.interfaces; |
||
| 165 | |||
| 166 | public interface AnotherJavaInterface { |
||
| 167 | public Integer test(); |
||
| 168 | } |
||
| 169 | </pre> |
||
| 170 | |||
| 171 | 33 | Anonyme | h2. Component Implementation |
| 172 | 6 | Anonyme | |
| 173 | 1 | Anonyme | To implement a component, one has to extend the abstract class generated automatically by the Eclipse plugin. |
| 174 | 33 | Anonyme | For example, for the previous example _simple.stuffs.MyBeautifulComponent_ defined in SpeADL, a Java class _simple.stuffs.MyBeautifulComponent_ is generated (in the *speadl-gen* folder, different than the *src* folder). |
| 175 | 31 | Anonyme | |
| 176 | 33 | Anonyme | It is not needed to look at the generated code to use it: when extending the class, some abstract methods will have to be implemented. |
| 177 | 1 | Anonyme | |
| 178 | When implementing a component, one only has to take care of implementing the provided port, and can exploit the required ports without assuming anything about their implementation and who provides it. |
||
| 179 | 12 | Anonyme | This is one thing that makes a component fundamentally different from an object. |
| 180 | 31 | Anonyme | |
| 181 | 12 | Anonyme | h3. Special Methods to Implement |
| 182 | 7 | Anonyme | |
| 183 | 33 | Anonyme | Each provided port *p* of interface *I* must be implemented by overriding a method called *I make_p()* which returns an instance of the implementation for the port. |
| 184 | This instance is used for the whole life of the component, i.e., the *make_p()* method is called only once to construct the port when the component is instantiated. |
||
| 185 | 6 | Anonyme | |
| 186 | Each part *p* of component class *C* has a corresponding abstract method *C make_p()* to override and which must return an instance of an implementation of *C*. |
||
| 187 | 7 | Anonyme | The bindings and other connections inside the components are totally taken care of by the generated code and the implementation only needs what is Java-specific. |
| 188 | 6 | Anonyme | |
| 189 | 8 | Anonyme | Furthermore, optionally, a method *void start()* can be override as explained [[SpeADL_Minus_Reference#Component-Initialisation|below]]. |
| 190 | |||
| 191 | 7 | Anonyme | h3. Special Methods to Exploit |
| 192 | 6 | Anonyme | |
| 193 | 31 | Anonyme | The *requires()* method (inherited from the extended generated class) gives access to each of the required ports (e.g., _requires().port()_). |
| 194 | A port being an implementation of an interface (and not of an operation), it is then necessary to call the desired method on it (e.g., _requires().port().method()_). |
||
| 195 | 6 | Anonyme | |
| 196 | 32 | Anonyme | The *provided()* method (inherited from the extended generated class) gives access to each of the provided ports in the same manner. |
| 197 | |||
| 198 | 31 | Anonyme | It is possible to access to the provided ports of the part from within the implementation of a composite by using the method *parts()* (e.g., _parts().partName().portName().method()_). |
| 199 | 6 | Anonyme | |
| 200 | h3. Examples |
||
| 201 | |||
| 202 | Implementing a component with a provided port: |
||
| 203 | 1 | Anonyme | <pre> |
| 204 | package testpackage; |
||
| 205 | |||
| 206 | import my.interfaces.AnotherJavaInterface; |
||
| 207 | import simple.stuffs.MySimpleComponent; |
||
| 208 | |||
| 209 | public class MySimpleComponentImpl extends MySimpleComponent { |
||
| 210 | |||
| 211 | @Override |
||
| 212 | protected AnotherJavaInterface make_p1() { |
||
| 213 | return new AnotherJavaInterface() { |
||
| 214 | @Override |
||
| 215 | public Integer test() { |
||
| 216 | return 10; |
||
| 217 | } |
||
| 218 | }; |
||
| 219 | } |
||
| 220 | |||
| 221 | } |
||
| 222 | </pre> |
||
| 223 | |||
| 224 | 6 | Anonyme | The same result can be obtained by implementing the port directly by the component implementation as follow: |
| 225 | 1 | Anonyme | <pre> |
| 226 | 6 | Anonyme | public class MySimpleComponentImpl extends MySimpleComponent implements AnotherJavaInterface { |
| 227 | |||
| 228 | @Override |
||
| 229 | public Integer test() { |
||
| 230 | return 10; |
||
| 231 | } |
||
| 232 | |||
| 233 | @Override |
||
| 234 | protected AnotherJavaInterface make_p1() { |
||
| 235 | return this; |
||
| 236 | } |
||
| 237 | } |
||
| 238 | </pre> |
||
| 239 | |||
| 240 | Exploiting a required port: |
||
| 241 | <pre> |
||
| 242 | 1 | Anonyme | package testpackage; |
| 243 | |||
| 244 | import my.interfaces.AJavaInterface; |
||
| 245 | import simple.stuffs.MyBeautifulComponent; |
||
| 246 | |||
| 247 | public class MyComponentImpl extends MyBeautifulComponent { |
||
| 248 | |||
| 249 | @Override |
||
| 250 | protected AJavaInterface make_portName() { |
||
| 251 | return new AJavaInterface() { |
||
| 252 | @Override |
||
| 253 | public String aMethod(Integer param1) { |
||
| 254 | return "" + param1 + " and " + requires().anotherPortName().test(); |
||
| 255 | } |
||
| 256 | }; |
||
| 257 | } |
||
| 258 | } |
||
| 259 | </pre> |
||
| 260 | |||
| 261 | 6 | Anonyme | Implementing a component with parts, calling a part's provided port: |
| 262 | 1 | Anonyme | <pre> |
| 263 | public class ComplexCompImpl extends MyComplexComponent { |
||
| 264 | |||
| 265 | @Override |
||
| 266 | protected MySimpleComponent make_s() { |
||
| 267 | return new MySimpleComponentImpl(); |
||
| 268 | } |
||
| 269 | |||
| 270 | @Override |
||
| 271 | protected AnotherJavaInterface make_p1() { |
||
| 272 | return new AnotherJavaInterface() { |
||
| 273 | @Override |
||
| 274 | public Integer test() { |
||
| 275 | return parts().s().p1().test(); |
||
| 276 | } |
||
| 277 | }; |
||
| 278 | } |
||
| 279 | |||
| 280 | @Override |
||
| 281 | protected MyBeautifulComponent make_b1() { |
||
| 282 | return new MyComponentImpl(); |
||
| 283 | } |
||
| 284 | |||
| 285 | @Override |
||
| 286 | protected MyBeautifulComponent make_b2() { |
||
| 287 | return new MyComponentImpl(); |
||
| 288 | } |
||
| 289 | |||
| 290 | @Override |
||
| 291 | protected MyBeautifulComponent make_b3() { |
||
| 292 | return new MyComponentImpl(); |
||
| 293 | } |
||
| 294 | |||
| 295 | } |
||
| 296 | </pre> |
||
| 297 | |||
| 298 | 39 | Anonyme | h2. Component Specialisation |
| 299 | 16 | Anonyme | |
| 300 | In SpeADL, a basic mechanism exists for specialisation of components. |
||
| 301 | |||
| 302 | h3. Keyword |
||
| 303 | |||
| 304 | When declaring a component class definition, after the name, the keyword *specializes*, followed by a reference to another component name, can be used. |
||
| 305 | 1 | Anonyme | |
| 306 | 16 | Anonyme | h3. Details |
| 307 | |||
| 308 | 33 | Anonyme | |
| 309 | An implementation of a specialising component can be used in place of the implementation of the specialised component. |
||
| 310 | Only the specialising component needs to be implemented: its implementation will contain all the provided ports of the specialisation hierarchy as well as the parts of the specialising component. |
||
| 311 | |||
| 312 | The specialisation rules are as follow: |
||
| 313 | 1 | Anonyme | * A component can specialise only a component without parts (i.e., only a pure definition with provided and required ports). |
| 314 | 31 | Anonyme | * A component can override provided ports (while respecting the interface or specialising it) to define delegation. |
| 315 | 28 | Anonyme | * A component can add provided ports. |
| 316 | 33 | Anonyme | * A component CAN'T add required ports (for obvious reason: the specialising component wouldn't be usable in a configuration where the specialised component is used because some of its ports won't be bound). |
| 317 | 30 | Anonyme | |
| 318 | 16 | Anonyme | h3. Examples |
| 319 | |||
| 320 | <pre> |
||
| 321 | namespace simple.stuffs { |
||
| 322 | 26 | Anonyme | component S specializes MySimpleComponent { |
| 323 | provides p2: AnotherJavaInterface |
||
| 324 | 16 | Anonyme | } |
| 325 | } |
||
| 326 | </pre> |
||
| 327 | |||
| 328 | 15 | Anonyme | h2. Type Parameters |
| 329 | |||
| 330 | 33 | Anonyme | As in Java, it is possible to exploit type parameters (also called generics) when declaring and referencing components, as well as in the interfaces of the ports. |
| 331 | 1 | Anonyme | |
| 332 | h3. Keywords |
||
| 333 | |||
| 334 | 34 | Anonyme | Type parameters are enclosed between *[ * and * ]* (contrary to Java where * <* and *> * are used). |
| 335 | 1 | Anonyme | |
| 336 | A type parameter can be declared only in a component definition, just after the name declaration. |
||
| 337 | 33 | Anonyme | It has a unique name, a capital first letter, and can be constrained using the keyword *extends* and the name of a Java class it must be a subclass of. |
| 338 | 1 | Anonyme | |
| 339 | 33 | Anonyme | A type parameter can be used as an argument when referencing a component by its name, in a part or in a specialisation declaration. |
| 340 | 1 | Anonyme | It must respect the type parameters declared in the referenced component. |
| 341 | |||
| 342 | 33 | Anonyme | A type parameter can also be used as an argument when referencing an interface by its name in a port declaration. |
| 343 | 1 | Anonyme | Again it must respect the type parameters declared in the interface. |
| 344 | |||
| 345 | h3. Details |
||
| 346 | |||
| 347 | The possibilities of expressiveness are equivalent to what can be done in Java. |
||
| 348 | |||
| 349 | The implementation can either keep the type parameters abstract as in the definition, or can concretise them as long as it respects the type parameter declaration. |
||
| 350 | |||
| 351 | Of course interface and component definition references can be parametrised with existing concrete classes. |
||
| 352 | |||
| 353 | h3. Example |
||
| 354 | |||
| 355 | <pre> |
||
| 356 | namespace simple.stuffs { |
||
| 357 | component ParameterisedComponent1[T extends Number] { |
||
| 358 | |||
| 359 | provides p1: java.util.concurrent.Callable[T] |
||
| 360 | provides p2: java.util.concurrent.Callable[String] |
||
| 361 | } |
||
| 362 | |||
| 363 | component ParameterisedComponent2[T1,T2 extends Number] { |
||
| 364 | |||
| 365 | part p1: ParameterisedComponent1[T2] { |
||
| 366 | |||
| 367 | } |
||
| 368 | |||
| 369 | part p2: ParameterisedComponent1[Integer] { |
||
| 370 | |||
| 371 | } |
||
| 372 | |||
| 373 | } |
||
| 374 | } |
||
| 375 | </pre> |
||
| 376 | 27 | Anonyme | |
| 377 | 39 | Anonyme | h2. Component Instantiation |
| 378 | 33 | Anonyme | |
| 379 | In order to instantiate a component from Java, one need an instance of an implementation of the component and to call the *newComponent()* method (present in the generated class) to get an instance of the component. |
||
| 380 | |||
| 381 | h3. Details |
||
| 382 | |||
| 383 | Only component without required port can be manually instantiated from Java: if a component has required ports, it must be composed with other components in a composite component. |
||
| 384 | |||
| 385 | Once we have an instance of a component, we can call the methods of its provided ports. |
||
| 386 | |||
| 387 | The same applies for composite components, the instantiation of the part of a composite is done automatically by the generated code. |
||
| 388 | |||
| 389 | h3. Example |
||
| 390 | |||
| 391 | <pre> |
||
| 392 | MySimpleComponent.Component c = new MySimpleComponentImpl().newComponent(); |
||
| 393 | System.out.println(c.p1().test()); |
||
| 394 | </pre> |
||
| 395 | |||
| 396 | 27 | Anonyme | h2. Component Initialisation |
| 397 | |||
| 398 | When the implementation of a component is instantiated (before calling *newComponent()*), its constructor is of course called but the component itself is not yet initialised: in particular its provided required ports and parts can't be called at that time. |
||
| 399 | |||
| 400 | h3. Details |
||
| 401 | |||
| 402 | In order to do some initialisation at the instantiation of a component (during the call to *newComponent()*), one can override the *void start()* method of the extended abstract class. |
||
| 403 | |||
| 404 | h3. Example |
||
| 405 | |||
| 406 | <pre> |
||
| 407 | public class MySimpleComponentImpl extends MySimpleComponent { |
||
| 408 | |||
| 409 | @Override |
||
| 410 | protected AnotherJavaInterface make_p1() { |
||
| 411 | return new AnotherJavaInterface() { |
||
| 412 | @Override |
||
| 413 | public Integer test() { |
||
| 414 | return 10; |
||
| 415 | } |
||
| 416 | }; |
||
| 417 | } |
||
| 418 | |||
| 419 | @Override |
||
| 420 | protected void start() { |
||
| 421 | // do some initialisation using the requires() or the parts(), create a GUI, etc... |
||
| 422 | } |
||
| 423 | } |
||
| 424 | </pre> |
||
| 425 | |||
| 426 | h2. Lifecycle of Component Initialisation at Instantiation |
||
| 427 | 1 | Anonyme | |
| 428 | 40 | Anonyme | When *newComponent()* is called on a component implementation, this is what happens: |
| 429 | 42 | Anonyme | # The component is instantiated (see below). |
| 430 | # The instance is started (see below). |
||
| 431 | 38 | Anonyme | |
| 432 | 22 | Anonyme | h3. Component Instantiation |
| 433 | |||
| 434 | 1 | Anonyme | # For each part *partX* in the order of declaration |
| 435 | 37 | Anonyme | ## The implementation is instantiated with the *make_partX()* method. |
| 436 | 38 | Anonyme | ## A component is instantiated from the implementation following the current procedure. |
| 437 | 23 | Anonyme | # For each provided port *portX* in the order of declaration (starting with the super-component in case of specialisation) |
| 438 | 36 | Anonyme | ## The interface implementation is instantiated with the *make_portX()* method. |
| 439 | |||
| 440 | 40 | Anonyme | h3. Component Instance Start |
| 441 | 22 | Anonyme | |
| 442 | # For each part *partX* in the order of declaration |
||
| 443 | 41 | Anonyme | ## The part is started following the current procedure. |
| 444 | 22 | Anonyme | # The implementation *start()* method is called. |