SCA with Apache Tuscany 2.0 An Introduction by Ivan A Krizsan Version: March 18, 2010

Copyright 2009-2010 Ivan A Krizsan. All Rights Reserved.

1

Table of Contents Table of Contents..................................................................................................................................2 Purpose ................................................................................................................................................ 6 Licensing ............................................................................................................................................. 6 Disclaimers .......................................................................................................................................... 6 Introduction.......................................................................................................................................... 6 Thanks.................................................................................................................................................. 6 Prerequisites......................................................................................................................................... 7 Chapter 1: Setting Up the Development Environment.........................................................................7 1.1 Eclipse........................................................................................................................................7 1.2 Eclipse SCA plugin.................................................................................................................... 7 1.3 Eclipse m2eclipse Maven Plugin............................................................................................... 7 1.4 Apache Tuscany 2.0M4............................................................................................................. 8 1.5 soapUI........................................................................................................................................ 8 1.6 SCA Specification Documents...................................................................................................8 Chapter 2: Setting Up a SCA Project................................................................................................... 9 2.1 Creating the Eclipse Project.......................................................................................................9 2.2 Declaring the Dependencies.................................................................................................... 12 2.3 Creating the SCA Contributions Metadata Document.............................................................13 Chapter 3: Hello World Service..........................................................................................................14 3.1 Creating the Project................................................................................................................. 14 3.2 Creating the Service Interface and the Service Implementation..............................................14 3.3 Create and Configure the SCA Composite.............................................................................. 16 3.4 Adding the Composite to the SCA Contribution Metadata Document....................................17 3.5 Creating the Client Class......................................................................................................... 17 3.6 Running the Example...............................................................................................................19 Chapter 4: Creating Composites.........................................................................................................20 4.1 Introduction to Composites......................................................................................................20 4.2 Creating Components.............................................................................................................. 23 4.2.1 Creating the Addition Component................................................................................... 23 The @Service Annotation................................................................................................25 Component Declarations in a Composite........................................................................ 27 The Element................................................................................. 29 The Element..........................................................................30 The Element............................................................................................... 31 The Element............................................................................................31 The Element............................................................................................. 31 Graphical Representation of a SCA Component............................................................. 32 Implementing a Base Class for SCA Unit Tests.............................................................. 33 Implementing the Addition Component Unit Test...........................................................34 Creating the Unit Test Composite....................................................................................35 4.2.2 Creating the Subtraction Component............................................................................... 37 4.2.3 Creating the Calculator Component.................................................................................41 The Component Element in Detail..............................................................47 Chapter 5: Exposing a SOAP Web Service........................................................................................ 50 5.1 Making the Service Remotely Accessible............................................................................... 50 5.1.1 Remotable Interfaces........................................................................................................51 5.2 Declaring a Binding for a Component..................................................................................... 51 5.2.1 Using Composite Service Elements................................................................................. 51 2

5.2.2 The Composite Element in Detail................................................................... 53 5.2.3 Using Component Service Elements................................................................................58 5.3 Starting Up the Calculator Web Service.................................................................................. 61 5.4 Testing the Calculator Web Service......................................................................................... 62 5.4.1 Retrieving the WSDL of the Service................................................................................62 5.4.2 Testing Using soapUI....................................................................................................... 64 Chapter 6: Injecting Properties Into Components.............................................................................. 66 6.1 The PropertyPrinter Component Service Interface..................................................................67 6.2 The PropertyPrinter Component Implementation....................................................................67 6.3 The @Property Annotation...................................................................................................... 68 6.3.1 The name Attribute...........................................................................................................69 6.4 Create and Configure the SCA Composites.............................................................................69 6.5 Adding the Composite to the SCA Contribution Metadata Document....................................70 6.6 Creating the Client Class......................................................................................................... 70 6.7 Running the PropertyPrinter Client......................................................................................... 71 6.8 The Component Element...................................................................................... 72 6.8.1 Injecting Values Using the value Attribute.......................................................................75 6.8.2 Injecting Values Using Subelements..................................................................76 6.8.3 Injecting Values From the Contents of the Element.....................................77 6.8.4 Injecting Values from Composite Properties....................................................................80 6.8.5 Injecting the Contents of a File........................................................................................ 82 6.9 Constructor Injection............................................................................................................... 84 Chapter 7: Assembling Composites................................................................................................... 85 7.1 The Calculator Component...................................................................................................... 86 7.1.1 Calculator Interface..........................................................................................................86 7.1.2 Calculator Implementation...............................................................................................87 7.1.3 Calculator Component Composite................................................................................... 88 7.1.4 Calculator Service Composite..........................................................................................88 7.2 Expression Evaluator Component........................................................................................... 89 7.2.1 Expression Evaluator Interface........................................................................................ 89 7.2.2 Expression Evaluator Implementation............................................................................. 90 7.2.3 Expression Evaluator Component Composite................................................................. 92 7.2.4 Expression Evaluator Wiring........................................................................................... 93 7.3 The Composite Element in Detail............................................................................... 94 7.4 Assembling the Composites.....................................................................................................96 7.5 The Composite Element in Detail.......................................................................... 97 7.6 Adding the Assembly Composite to the SCA Contribution Metadata Document...................97 7.7 Creating the Client Class......................................................................................................... 98 7.8 Running the Expression Evaluator Client................................................................................98 Chapter 8: Composites as Component Implementation..................................................................... 99 8.1 Batch File................................................................................................................................. 99 8.2 Promoting the Expression Evaluator Service........................................................................ 100 8.3 BatchCalculator Component..................................................................................................101 8.3.1 BatchCalculator Service Interface................................................................................. 101 8.3.2 BatchCalculator Implementation................................................................................... 102 8.3.3 BatchCalculator Composite........................................................................................... 103 8.4 The Element in Detail............................................................ 104 8.5 Adding the BatchCalculator Composite to the SCA Contribution Metadata Document.......104 8.6 Creating the Client Class....................................................................................................... 105 8.7 Running the BatchCalculator Client...................................................................................... 105 3

Chapter 9: Injecting Properties and References Into Composites.................................................... 106 9.1 Presentation of the Example Program................................................................................... 106 9.2 The Composite Element in Detail.......................................................................108 9.3 The Composite Element in Detail..................................................................... 110 9.4 Creating the Project................................................................................................................112 9.5 Printer Component................................................................................................................. 112 9.5.1 Printer Service Interface.................................................................................................112 9.5.2 Printer Service Implementation......................................................................................113 9.5.3 Printer Composite...........................................................................................................114 9.6 ParagraphPrinter Component.................................................................................................115 9.6.1 ParagraphPrinter Service Interface.................................................................................115 9.6.2 ParagraphPrinter Service Implementation..................................................................... 115 9.6.3 ParagraphPrinter Composite.......................................................................................... 117 9.7 Assembly Composite............................................................................................................. 118 9.8 Adding the Assembly Composite to the SCA Contribution Metadata Document.................119 9.9 Creating the Client Class....................................................................................................... 119 9.10 Running the ParagraphPrinter Client................................................................................... 120 Chapter 10: Using an Existing Web Service.....................................................................................121 10.1 An Existing Web Service..................................................................................................... 121 10.1.1 Endpoint Implementation Class................................................................................... 121 10.1.2 Custom Exception Class.............................................................................................. 122 10.1.3 Testing the Existing Web Service.................................................................................123 10.2 Creating the Project............................................................................................................. 124 10.3 Generating the Web Service Client Artifacts....................................................................... 124 10.4 InvokeService Component...................................................................................................125 10.4.1 InvokeService Service Interface.................................................................................. 125 10.4.2 InvokeService Service Implementation....................................................................... 125 10.4.3 InvokeService Composite............................................................................................ 127 10.5 Adding the InvokeService Composite to the SCA Contribution Metadata Document........128 10.6 Creating the Client Class..................................................................................................... 128 10.7 Running the InvokeService Client....................................................................................... 129 10.7.1 Running with a Legal Parameter..................................................................................129 10.7.2 Running with an Illegal Parameter...............................................................................129 10.8 The Element In Detail...................................................................................131 10.8.1 The wsdlElement Attribute.......................................................................................... 134 10.8.2 The wsdlLocation Attribute......................................................................................... 134 10.8.3 The Child Element............................................................135 Chapter 11: Using Callbacks............................................................................................................ 136 11.1 Creating the Project..............................................................................................................136 11.2 ChecksumService Component............................................................................................. 136 11.2.1 ChecksumService Service Interface.............................................................................137 11.2.2 ChecksumService Callback Interface...........................................................................138 11.2.3 ChecksumService Implementation...............................................................................139 11.2.4 ChecksumService Composite.......................................................................................141 11.3 The @Callback Annotation..................................................................................................142 11.3.1 Callback Service References........................................................................................ 142 11.4 The @OneWay Annotation.................................................................................................. 143 11.5 BulkChecksum Component................................................................................................. 143 11.5.1 BulkChecksum Service Interface................................................................................. 143 11.5.2 BulkChecksum Implementation...................................................................................144 4

11.5.3 BulkChecksum Service Composite..............................................................................146 11.6 Adding the Composites to the SCA Contribution Metadata Document..............................146 11.7 Creating the Client Class..................................................................................................... 147 11.8 Running the Client............................................................................................................... 148 11.9 Callback Instance Management........................................................................................... 151 11.10 Component Context........................................................................................................... 152 11.10.1 Obtaining a Component Context................................................................................152 11.10.2 Component Context API............................................................................................ 153 Chapter 12: Dynamic Component Relationships............................................................................. 154 12.1 Creating the Project............................................................................................................. 154 12.2 Observer Component........................................................................................................... 154 12.2.1 ObserverService Interface............................................................................................154 12.2.2 ObserverService Implementation.................................................................................155 12.2.3 ObserverService Composite.........................................................................................158 12.3 Observable Component........................................................................................................159 12.3.1 ObservableService Interface........................................................................................ 159 12.3.2 ObservableService Implementation............................................................................. 160 12.3.3 ObservableService Composite..................................................................................... 162 12.4 Adding the Composites to the SCA Contribution Metadata Document..............................163 12.5 Creating the Client Class..................................................................................................... 163 12.6 Running the Client............................................................................................................... 165 Chapter 13: SCA Domain with Distributed Nodes...........................................................................167 13.1 Creating the Projects............................................................................................................ 167 13.1.1 Creating the TuscanyServerInterface Project...............................................................168 13.1.2 Creating the TuscanyServerNode Project.................................................................... 168 13.1.3 Creating the TuscanyClientNode Project..................................................................... 168 13.2 Service Interface.................................................................................................................. 169 13.3 Standalone Processor Service.............................................................................................. 170 13.3.1 Service Implementation............................................................................................... 170 13.3.2 The @EagerInit Annotation......................................................................................... 171 13.3.3 Service Composite....................................................................................................... 171 13.3.4 Adding the Composite to the SCA Contribution Metadata Document........................171 13.3.5 Node Configuration Document.................................................................................... 172 13.3.6 Adding the Composite to the SCA Contribution Metadata Document........................174 13.3.7 Service Node Starter.................................................................................................... 175 13.3.8 Running the Service Node........................................................................................... 176 13.4 Standalone Processor Client................................................................................................ 177 13.4.1 StandaloneProcessor Client Interface.......................................................................... 177 13.4.2 StandaloneProcessor Client Implementation............................................................... 178 13.4.3 StandaloneProcessor Client Composite....................................................................... 181 13.4.4 Node Configuration Document.................................................................................... 182 13.4.5 Adding the Composite to the SCA Contribution Metadata Document........................182 13.4.6 Client Node Starter.......................................................................................................183 13.4.7 Running Client Nodes.................................................................................................. 184 13.5 Using Hazelcast instead of Tribes........................................................................................187 13.5.1 Changing the Domain Registry....................................................................................187

5

Purpose This document contains some introductory examples of how to use SCA, the Service Component Architecture, with the Apache Tuscany 2.0 implementation.

Licensing This document is licensed under a Creative Commons Attribution-Noncommercial-No Derivative Works 3.0 license. In short this means that: • You may share this document with others. • You may not use this document for commercial purposes. • You may not create derivate works from this document.

Disclaimers Though I have done my best to avoid it, this document might contain errors. I cannot be held responsible for any effects caused, directly or indirectly, by the information in this document – you are using it on your own risk. Submitting any suggestions, or similar, the information submitted becomes my property and you give me the right to use the information in whatever way I find suitable, without compensating you in any way. All trademarks in this document are properties of their respective owner and do not imply endorsement of any kind. This document has been written in my spare time and has no connection whatsoever with my employer.

Introduction SCA, short for Service Component Architecture, is a programming model for building systems based on SOA, Service Oriented Architecture. To put it more clearly and perhaps overly simplified, SCA is like a dependency injection frameworks that, among other things, also allows for: • Components to be implemented in different programming languages. • Distribution of components on different computers. • Components to be implemented without having to consider issues such as communication between components and security. I have tried to follow the SCA 1.1 specifications as far as possible, but in the cases that the Apache Tuscany implementation I have used has lacked a feature described in the specification documents, this feature has not been mentioned in this document. Also, if any discrepancies between the Apache Tuscany implementation and the SCA 1.1 specification has been found, the focus has been on producing runnable code.

Thanks A big “thank you” to the kind people on the Apache Tuscany mailing lists. I also want to thank the people who invest time and effort developing Apache Tuscany and working on the SCA standard.

6

Prerequisites Readers are assumed to be familiar with the following subjects: • Developing Java software with Eclipse. • Installing plugins in Eclipse. • SOAP web services. • Developing SOAP web services using JAX-WS. • Testing SOAP web services with soapUI. There will be at least one fairly detailed example on how to test a web service using soapUI, so you should be fine even if you have little or no previous experience. • Writing unit tests using TestNG and Hamcrest. An internet connection is required, at least initially, to allow Maven to download the project dependencies.

1 Setting Up the Development Environment When writing this document, I have been working in the following environment: • Eclipse Galileo • The Eclipse SCA Plugin version 2.0.1. • The m2eclipse Maven Plugin. • Apache Tuscany 2.0M4 • soapUI 3.0

1.1 Eclipse Eclipse can be downloaded from http://www.eclipse.org. Choose the JavaEE version, which includes most of the dependencies required by the SCA plugin.

1.2 Eclipse SCA plugin At the time of writing, the Eclipse SCA plugin only supports versions of Apache Tuscany prior to 2.0. This manifest itself in the following ways: • Creating SCA Java projects will include references to old libraries. • XML schemas used will be those of SCA version 1.0, instead of SCA version 1.1. The main reason for installing the SCA plugin is that Eclipse will recognize SCA configuration files. There is a graphical composite diagram editor, but it only works with composites defined using the SCA 1.0 XML schemas. For more information about the SCA plugin and installation instructions, please visit http://www.eclipse.org/stp/sca/.

1.3 Eclipse m2eclipse Maven Plugin In this document, Maven is used to manage the dependencies. The m2eclipse plugin allows Eclipse projects to use the dependencies declared in a Maven pom.xml file. For more information about the m2eclipse plugin as well as installation instructions, please visit http://m2eclipse.sonatype.org/.

7

1.4 Apache Tuscany 2.0M4 The examples in this document does not require downloading Apache Tuscany - Maven will take care of that for us. More information on Apache Tuscany is available at http://tuscany.apache.org/.

1.5 soapUI There is a free version of the excellent web services testing tool soapUI. If you haven't already, go to http://www.soapui.org/ and download your own copy!

1.6 SCA Specification Documents You are encouraged to turn to the SCA specification documents for details. References will be given throughout this document. The SCA 1.1 specification documents can be downloaded at: http://docs.oasis-open.org/opencsa/

8

2 Setting Up a SCA Project In this chapter I will describe the steps required to create a new project in Eclipse and prepare it for developing an SCA application.

2.1 Creating the Eclipse Project Creating the Eclipse project in which one or more SCA components are to be developed is done in the following way: •

Create a new Maven project by choosing New -> Maven Project in the File menu.



Configure the new Maven project not to use any archetype, as shown in the picture below. The workspace location can be set as desired, as can the working set to which the project is added.

9



Enter the group ID and artifact ID of the project. All the projects in this document uses the group ID com.ivan.soaexamples. The artifact ID is specific to each project.

When done entering data, click the Finish button to create the project. (continued on next page)

10

With the project created, make sure that the Java compiler compliance level of the project is at least 1.5. The compiler compliance level can be set in the project properties, in the Java Compiler section, as seen in the picture below. Throughout this document, I will use compiler compliance level 1.6 and JRE 6.

Setting the Compiler Compliance Level in the Project Properties of the new Eclipse project.

Finally, make sure that the appropriate JRE system library is included on the project classpath. If you are using compiler compliance 1.5, then use the JRE 5 library and so on.

Setting the JRE system library in the Project Properties of the new Eclipse project.

The basic project setup is now finished and we can go on to declare the dependencies of the project.

11

2.2 Declaring the Dependencies Having created the project, the next step is to declare the dependencies. The basic pom.xml file declaring the Apache Tuscany dependencies looks like this: 4.0.0 com.ivan.soaexamples SCAHelloWorld 0.0.1-SNAPSHOT apache.incubator http://people.apache.org/repo/m2-incubating-repository org.apache.tuscany.sca tuscany-domain-node 2.0-M4 org.apache.tuscany.sca tuscany-host-jetty 2.0-M4 org.apache.tuscany.sca tuscany-binding-ws-axis2 2.0-M4 org.hamcrest hamcrest-all 1.1 test org.testng testng 5.9 test

12

2.3 Creating the SCA Contributions Metadata Document The SCA Contributions Metadata Document contains declarations of the runnable components of a SCA application as well as imported and exported definitions. This document is named “scacontribution.xml” and is to be located in the META-INF directory in the root of the source path. • In our project, in src/main/resources, create a new folder named “META-INF” • In the META-INF folder, create a new file named “sca-contribution.xml”. The result will look like this in the Eclipse Package Explorer:

The location of the sca-contribution.xml file in the Eclipse project.



Paste the following contents into the SCA Contributions file:



The project is now ready for development.

13

3 Hello World Service In this chapter, we will develop a Hello World service, that greets a person, and an accompanying client - this to show the very basics of SCA with Apache Tuscany. Explanations will be deferred to subsequent chapters - the main purpose of this exercise is to see some SCA action and make a first acquaintance with the process of developing an SCA project.

3.1 Creating the Project As the first step, create and set up a SCA project in Eclipse as described in chapter 2. I will use the project name and Maven artifact ID “SCAHelloWorld”.

3.2 Creating the Service Interface and the Service Implementation The service interface decides what operations are made available to clients of the service. The service implementation is responsible for supplying the implementation of those operations. • •

In src/main/java, create the package com.ivan.components. In the com.ivan.components package, create a new interface named HelloWorldService. The interface contains the following code:

package com.ivan.components; /** * This interface defines the functionality made available by * the HelloWorldService. * * @author Ivan A Krizsan */ public interface HelloWorldService { /** * Extends a greeting to the person with the supplied name. * The greeting will contain the time when the greeting was * issued. * * @param inName Name of person to greet. * @return Greeting. */ String sayHello(final String inName); }

• •

In src/main/java, create the package com.ivan.components.impl. In the com.ivan.components.impl package, create a class named HelloWorldServiceImpl that implements the HelloWorldService interface. The class contains the following code:

package com.ivan.components.impl; import java.util.Date; import org.oasisopen.sca.annotation.Service; import com.ivan.components.HelloWorldService; /** * This class provides the implementation of the Hello World service. * * @see HelloWorldService * @author Ivan A Krizsan */ @Service(HelloWorldService.class) public class HelloWorldServiceImpl implements HelloWorldService { /* (non-Javadoc) * @see com.ivan.components.HelloWorldService#sayHello(java.lang.String) */ public String sayHello(final String inName)

14

{ System.out.println("HelloWorldServiceImpl.sayHello called with name " + inName); Date theDate = new Date(); String theResult = "Hello " + inName + ", it is now " + theDate; return theResult; } }

With the above steps completed, the project should look like this in the Eclipse Package Browser:

The Eclipse project after having added the service interface and implementation.

15

3.3 Create and Configure the SCA Composite An SCA composite is used to assemble and group SCA elements. In this first example, there is only one single service, the Hello World service. • In src/main/resources, create a new file named “HelloWorld.composite”. • Paste the following contents into the composite file:

With the composite file added, the project should look like this in the Eclipse Package Browser:

The Eclipse project after having added the SCA composite file.

Note the colourful symbol of the composite file. This is the symbol given composite files by the Eclipse SCA plugin.

16

3.4 Adding the Composite to the SCA Contribution Metadata Document In order for SCA to be able to find our Hello World service composite, we need to add a reference to it in the SCA Contribution file. • Locate and open the sca-contribution.xml file. • Add a element to the file so it looks like this (heading comment removed to conserve space):

3.5 Creating the Client Class The client class in this example is responsible for starting the SCA node containing the Hello World service and invoking the service. • In src/main/java, create the package com.ivan.client. • In the com.ivan.client package, create a class named SCAHelloWorldClient. The class contains the following code: package com.ivan.client; import org.apache.tuscany.sca.node.Node; import org.apache.tuscany.sca.node.NodeFactory; import com.ivan.components.HelloWorldService; /** * This class creates and starts the SCA node containing the * Hello World service, retrieves the service and invokes it. * * @author Ivan A Krizsan */ public class SCAHelloWorldClient { public static void main(String[] args) { /* Create and start a new SCA node. */ Node theNode = NodeFactory.newInstance().createNode(); theNode.start(); /* Retrieve the Hello World service component. */ HelloWorldService theHelloWorldService = theNode.getService(HelloWorldService.class, "HelloWorldComponent"); /* Invoke the service. */ String theResponse = theHelloWorldService.sayHello("Ivan"); System.out.println("Response from the service: " + theResponse); } }

17

With the client class in place, the complete project should look like this in the Eclipse Package Explorer:

The Eclipse project after having added the client class.

18

3.6 Running the Example To run the example, run the SCAHelloWorldClient class as a regular Java application. It will produce the following console output: Nov 16, 2009 8:49:56 PM org.apache.tuscany.sca.node.impl.NodeImpl start INFO: Starting node: http://tuscany.apache.org/sca/1.1/nodes/default0 domain: tuscany.apache.org Nov 16, 2009 8:49:56 PM org.apache.tuscany.sca.node.impl.NodeFactoryImpl loadContributions INFO: Loading contribution: file:[file path removed] Nov 16, 2009 8:49:58 PM org.apache.tuscany.sca.core.assembly.impl.EndpointRegistryImpl addEndpoint INFO: Add endpoint - (@2048950396)Endpoint: URI = HelloWorldComponent#servicebinding(HelloWorldService/HelloWorldService) HelloWorldServiceImpl.sayHello called with name Ivan Response from the service: Hello Ivan, it is now Mon Nov 16 20:49:58 CST 2009

19

4 Creating Composites Composites are one of the central building-blocks of SCA. In this chapter we will learn more about developing composites, which includes developing and testing components.

4.1 Introduction to Composites References: • Service Component Architecture Assembly Model Specification, version 1.1, chapter 2 This document only gives a brief introduction to SCA. For a more detailed information, please refer to the reference above. This document uses the same graphical notation as used in the SCA specification documents. A composite is an assembly that can contain the following things: • Components. • Services. • References. • Property declarations. • Wiring connecting different elements of the composite. • Inclusions of other composite files. A SCA composite is declared in an XML file that has the extension “composite”, as we saw in the Hello World example earlier.

20

The following figure shows the structure of the element, which is the root element of composite files:

The structure of the element, which is the root element of a SCA composite file.

The different parts of the element will be described in detail as they are used.

21

A diagram showing the Calculator composite that we will develop in this chapter looks like in the figure below. The comments outside of the composite and the coloured dotted lines have been added to comment the parts of the figure and are normally not part of a composite figure.

The Calculator composite consisting of three components of which one exposes a service to the outside world.

From the above diagram, we can learn the following: • The Calculator composite contains three components; the Calculator component, the Addition component and the Subtraction component. • The Addition and Subtraction components offers services only available to components within the composite. • The Calculator component uses the services of the Addition and Subtraction components. • The Calculator component offers a service that is promoted to become a service offered by the composite.

22

4.2 Creating Components SCA components are the building blocks that supplies functionality of an application, service providers. If you have developed applications using the Spring framework, you will find that SCA components have many things in common with Spring beans. SCA allows for components to be implemented using, among others: • Java • Enterprise Java Beans • Script languages JavaScript, Ruby, Groovy etc. • BPEL • Spring framework • OSGi Bundles • XQuery • SCA Composites In this section we will learn how to create components that are implemented using Java. In preparation for this example, please set up a Eclipse project named “SCACalculator” with the Maven artifact ID being the same as the project name, as described in chapter 2.

4.2.1 Creating the Addition Component First we'll create the Addition component with a Java implementation. Remember that, according to the figure we have seen earlier, the Addition component exposes a service that only is available to other components in the same composite. This component will be created in the simplest possible way, using Java annotations where possible. The Addition Component Service Interface

Create an interface named AdditionService, which looks like this: package com.ivan.components; /** * This interface defines the operations made available by the * Addition service. * * @author Ivan A Krizsan */ public interface AdditionService { /** * Adds the supplied numbers and returns the result. * Does not check for numeric overflow. * * @param inNumber1 First number to add. * @param inNumber2 Second number to add. * @return Sum of the supplied numbers. */ long add(final long inNumber1, final long inNumber2); }

There is nothing special related to SCA in this interface so we move on to the next step.

23

The Addition Component Implementation

In src/main/java, create the implementation of the Addition service: package com.ivan.components.impl; import org.oasisopen.sca.annotation.Service; import com.ivan.components.AdditionService; /** * This class provides the implementation of the Addition service, * a service that adds long integer numbers. * * @author Ivan A Krizsan */ @Service(AdditionService.class) public class AdditionServiceImpl implements AdditionService { /** * Default constructor. */ public AdditionServiceImpl() { System.out.println("AdditionServiceImpl instance created."); } /* (non-Javadoc) * @see com.ivan.components.AdditionService#add(long, long) */ @Override public long add(final long inNumber1, final long inNumber2) { return inNumber1 + inNumber2; } }

Note that: • The class is annotated with the @Service annotation. This annotation is used to define the services offered by the implementation class. A more detailed discussion follows below. • The class contains a default constructor. The reason for this is that we want to see when instances of the class are created. A default constructor is not required by SCA. (continued on next page)

24

The @Service Annotation

References: • Service Component Architecture SCA-J Common Annotations and APIs Specification, version 1.1, section 10.21 • Service Component Architecture POJO Component Implementation Specification, version 1.1, chapter 2 As before, the @Service annotation is used to define the services offered by the class which is annotated. A service implementation class does not have to have a @Service annotation. The @Service annotation has the following attributes: • interfaces An array of class or interface objects that specify what functionality the service exposes. If this attribute is present, then the value attribute must not be used. The implementation class does not need to implement the interfaces listed, but all methods in the interfaces must to be present in the implementation class. Example: @Service(interfaces={ServiceOneClass.class, ServiceBInterface.class}) • names Specifies one or more names of the service. Default values are the names of the classes/interfaces specified by the interfaces attribute. If the names attribute is specified, then the interfaces attribute must also be specified and the number of names must be the same as the number of classes/interfaces. The name in the first position in the names-array is the service name corresponding to the class/interface in the first position in the interfaces-array etc. Example: @Service(names={"AdditionService"}, interfaces={AdditionService.class}) • value Convenience attribute to be used when there is only one single class or interface specifying the functionality exposed by the service. Again, the implementation class does not need to implement the interface, but all the methods in the interface must be present in the implementation class. Examples: @Service(AdditionService.class) @Service(value=AdditionService.class) Since we only use the simplest version of the @Service annotation, the name of the service defaults to “AdditionService”.

25

Declaring the Addition Component in a Composite

References: • Service Component Architecture Assembly Model Specification, version 1.1, chapter 4 and 5 Before being able to declare the component in the composite it is to belong to, we must create a composite file. • In src/main/resource, create a file named “Calculator.composite”. • Paste the following contents into the file:

Add a child element to the element, defining the Addition component. The resulting composite file will look like this:

Before we continue, we will take a look at the configuration options available when declaring a component in a composite.

26

Component Declarations in a Composite

Components are the smallest building-blocks in SCA. As we already have seen, they are declared in the composite to which they belong. The element can have a number of attributes and a number of child elements. All the attributes and child elements will be listed, but a more thorough description will be postponed until the attribute, or child element, is actually used. The structure of the element looks like this:

Structure of the element in a composite. This element is used to define components of the composite.

27

The attributes of the element are: Attribute Name

Description

name

Required attribute. The name of the component must be unique among the components of the composite.

autowire

Whether component references in the component are autowired or not. Default is false.

requires

A list of policy intents.

policySets

A list of policy sets.

constrainingType

The name of the constraining type, if any. A constraining type is like a mould or a template that shapes and constrains the component.

The child elements of the element: Element Name

Description

implementation

Specifies the implementation of the component. Note that the element is optional, so it is possible to declare components without implementation (though, they won't do much).

service

Declares the service made available by the component. Optional.

reference

Declares references to other components used by this component. These will be injected into this component. Optional.

property

Declares properties of this component and the values that are to be injected into these properties. Optional.

Let's take a closer look at the child elements of the element.

28

The Element

The implementation element specifies the implementation to be used by the component. The name of the element indicates the kind of implementation; for instance means that the component implementation is written in Java. Some other types of elements are: Implementation Element Name Implementation Language/Type implementation.java

Java

implementation.bpel

BPEL

implementation.cpp

C++

implementation.c

C

implementation.composite

SCA Composite

implementation.spring

Java components written using the Spring framework (a Spring application context)

implementation.ejb

Enterprise Java Bean

We will see some, but not all, element types in this document. The structure of the general element looks like this:

Structure of the basic element in a component declaration in a composite.

The element has two attributes: Attribute Name

Description

requires

A list of policy intents.

policySets

A list of policy sets.

29

The Element

The structure of the element looks like this:

Structure of the element in a component declaration in a composite. This element is used to specify the implementation of a component when the implementing code is written in Java.

The element extends the element, adding one single attribute: Attribute Name Description class

Fully qualified name of the Java class implementing the component functionality.

30

The Element

A component can have zero or more elements, defining the service(s) made available by the component. A component may, for instance, make its services available using RMI and SOAP, in which case it will have two elements. We'll look more closely at the element when we are to expose the services of a component. The Element

A component can use any number of other components. In order to be able to do that, references to these components must be injected into the component. We'll look more closely at the element in the Calculator example. The Element

A component can have any number of property values injected into it, in order to, for instance, enable configuration without having to change code. We'll look more closely at the element when we discuss injection of properties into components.

31

Graphical Representation of a SCA Component

Armed with the knowledge of the different aspects of a component, we'll take a look at the graphical representation of a SCA component.

The graphical representation of a SCA component.

Contributing the Calculator Composite to the SCA Node

References: • Service Component Architecture Assembly Model Specification, version 1.1, section 11.2.2 In order for the SCA implementation, Apache Tuscany in our case, to be able to find and load our composite we need to add it to the SCA Contributions Metadata document that was created when setting up the project. • Open the sca-contribution.xml file that is located in the META-INF directory in src/main/resources. • Modify the contents of the file so it looks like this (comments removed):

32

Writing a Test Case for the Addition Component

When testing a single component, we probably want to avoid starting up the entire SCA contribution. Additionally, the use of a special composite that is configured for the tests may also be desirable. Implementing a unit test for the Addition component, we will show how to start up a SCA node consisting of one single composite, a composite that is specially designed for the test. We will also see how certain common behaviour of SCA component unit tests can be extracted into a common base-class for such tests. Implementing a Base Class for SCA Unit Tests

In order to simplify the implementation of SCA tests, we first create a common base-class for our tests. • In src/test/java, create a Java class named SCAServiceTestBase. • Implement the class as follows: package com.ivan.components; import import import import import import

org.apache.tuscany.sca.node.Contribution; org.apache.tuscany.sca.node.ContributionLocationHelper; org.apache.tuscany.sca.node.Node; org.apache.tuscany.sca.node.NodeFactory; org.testng.annotations.AfterTest; org.testng.annotations.BeforeTest;

/** * Base class for unit tests testing SCA services. Subclasses need to * implement the retrieveService method and, in it, * retrieve the service that is to be tested and/or make any required * preparations for the test. * * The unit tests uses a special composite file, in order to avoid errors * occurring if, for instance, a component is exposed as a service that * is not reachable. * Note that the package in which the unit test composite file is located * and the name of the unit test composite file are hardcoded into this * class. * * @author Ivan A Krizsan */ public abstract class SCAServiceTestBase { /* Constant(s): */ /** Package in which the test composite file is located. */ protected final static String SCA_TEST_PACKAGE = "com/ivan/components/"; /** Name of the test composite. */ protected final static String SCA_TEST_COMPOSITE = "Calculator-Tests.composite"; /* Instance variable(s): */ protected Node mSCANode; /** * Prepares for the test by starting the SCA node and retrieving * the service component. */ @BeforeTest public void setup() { /* * Ask helper to create an URL specifying the location of the * unit test composite file. */ String theLoc = ContributionLocationHelper.getContributionLocation(SCA_TEST_PACKAGE + SCA_TEST_COMPOSITE); /*

33

* Create the SCA node used by unit tests. * Note that we need to supply the location (package + filename) * of the unit test composite file here aswell. */ mSCANode = NodeFactory.newInstance().createNode( SCA_TEST_PACKAGE + SCA_TEST_COMPOSITE, new Contribution("TestContribution", theLoc)); /* Start the SCA node used by unit tests. */ mSCANode.start(); /* Let subclasses make preparations for tests. */ retrieveService(); } /** * Retrieves the service under test from the SCA node. This method * is called from the test's setup method before any of the test * methods are run. */ protected abstract void retrieveService(); /** * Cleans up after the test by stopping the SCA node. */ @AfterTest public void cleanup() { if (mSCANode != null) { mSCANode.stop(); } } }

Implementing the Addition Component Unit Test

With the unit test base class in place, we can now implement the Addition service unit test. • In the same package, create a class named AdditionServiceTest. • Implement the class like this: package com.ivan.components; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; import org.testng.annotations.Test; /** * Tests the Addition service by starting the SCA node, retrieving * the component and invoking it. * * @author Ivan A Krizsan */ @Test public class AdditionServiceTest extends SCAServiceTestBase { /* Constant(s): */ /* Instance varible(s): */ private AdditionService mAdditionService; /* (non-Javadoc) * @see com.ivan.components.SCAServiceTestBase#retrieveService() */ @Override protected void retrieveService() { /* Retrieve the Addition service component. */ mAdditionService = mSCANode.getService(AdditionService.class, "AdditionComponent"); } /** * Tests adding numbers using the Addition service.

34

*/ public void testAddition() { long theResult; theResult = mAdditionService.add(1L, 2L); assertThat(theResult, is(3L)); theResult = mAdditionService.add(0L, 0L); assertThat(theResult, is(0L)); theResult = mAdditionService.add(1L, -2L); assertThat(theResult, is(-1L)); } }

Creating the Unit Test Composite

I have run into the problem that, under certain circumstances, unit tests of SCA components fail. This has been caused by, for instance, a component being exposed as a web service and the SCA runtime not being able to retrieve the proper address at which to invoke the service. Problems like this can be avoided by using a special composite file, in which the service is not exposed as a web service. In the same way as previously described, we create a new composite file and declare the Addition component in this composite as well. •

In src/test/resources, in the package com.ivan.components, create the composite file named “Calculator-Test.composite” with the contents as follows:





Run the test.

In the console, among the Tuscany log in red, the output from our constructor appears in black: ... INFO: Add endpoint - (@544096693)Endpoint: URI = AdditionComponent#servicebinding(AdditionService/AdditionService) AdditionServiceImpl instance created. AdditionServiceImpl instance created. AdditionServiceImpl instance created. Nov 20, 2009 8:31:59 PM org.apache.tuscany.sca.node.impl.NodeImpl stop ...

We notice that for each time the Addition service is used, a new instance is created. In the next section we will see how to change this behaviour.

35

Setting the Scope of a Component

We note that for each time we invoked the Addition service, a new instance of the implementation class was created. Later we will look at the life cycle of SCA components implemented in Java. We want one single instance of the implementation class to serve all requests. To accomplish this, we add an annotation to the AdditionServiceImpl class: ... @Scope("COMPOSITE") @Service(AdditionService.class) public class AdditionServiceImpl implements AdditionService { ...

Note! The @Scope annotation must be applied to the component implementation class – it cannot be applied on service interfaces. The @Scope annotation changes the scope of the component from STATELESS, which is the default scope, to COMPOSITE. The meaning of the two scopes are as follows: Scope Description STATELESS

The SCA runtime ensures that an implementation object is invoked by at most one thread at a time. Default scope.

COMPOSITE

The SCA runtime must ensure that all consumers of the component seem to be interacting with one single instance. Multiple threads may be executing on the instance and the SCA runtime must not perform any synchronization.

Note that these scopes apply only to components implemented in Java. If we now run the test, we note that the output from our constructor appears only once: INFO: Add endpoint - (@934817129)Endpoint: URI = AdditionComponent#servicebinding(AdditionService/AdditionService) AdditionServiceImpl instance created. Nov 22, 2009 9:27:59 AM org.apache.tuscany.sca.node.impl.NodeImpl stop

36

4.2.2 Creating the Subtraction Component In this section, we'll implement the Subtraction component. Writing a Test Case for the Subtraction Component

Using the common base-class for tests of SCA components we developed when creating the Addition component above, we are now able to rapidly implement the test case for the Subtraction component in a similar manner. • In src/test/java, create a Java class named SubtractionServiceTest. • Implement the class as follows: package com.ivan.components; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; import org.testng.annotations.Test; /** * Tests the Subtraction service by starting the SCA node, retrieving * the component and invoking it. * * @author Ivan A Krizsan */ @Test public class SubtractionServiceTest extends SCAServiceTestBase { /* Constant(s): */ /* Instance variable(s): */ private SubtractionService mSubtractionService; /* (non-Javadoc) * @see com.ivan.components.SCAServiceTestBase#retrieveService() */ @Override protected void retrieveService() { /* Retrieve the Subtraction service component. */ mSubtractionService = mSCANode.getService(SubtractionService.class, "SubtractionComponent"); } /** * Tests subtracting numbers using the service. */ public void testSubtraction() { long theResult; theResult = mSubtractionService.subtract(1L, 2L); assertThat(theResult, is(1L)); theResult = mSubtractionService.subtract(0L, 0L); assertThat(theResult, is(0L)); theResult = mSubtractionService.subtract(1L, -2L); assertThat(theResult, is(-3L)); } }

Naturally, the test fails at this stage – in fact, it does not even compile. Note also that the Subtraction component also need to be added to the unit test composite. We will do this later, when adding the Subtraction component to the regular composite.

37

The Subtraction Service Interface

In src/main/java, create an interface named SubtractionService, which looks like this: package com.ivan.components; /** * This interface defines the operations made available by the * Subtraction service. * * @author Ivan A Krizsan */ public interface SubtractionService { /** * Subtracts the first supplied number from the second supplied number. * Does not check for numeric underflow. * * @param inNumber1 Number to subtract. * @param inNumber2 Number to be subtracted * @return Difference of the two numbers. */ long subtract(final long inNumber1, final long inNumber2); }

Again, there is nothing special related to SCA in this interface so we move on to the component implementation. The Subtraction Component Implementation

In src/main/java, create the implementation of the Subtraction service: package com.ivan.components.impl; import org.oasisopen.sca.annotation.Service; import com.ivan.components.SubtractionService; /** * This class provides the implementation of the Subtraction service, * a service that subtracts long integer numbers. * * @author Ivan A Krizsan */ @Service(SubtractionService.class) public class SubtractionServiceImpl implements SubtractionService { /** * Default constructor. */ public SubtractionServiceImpl() { System.out.println("SubtractionServiceImpl instance created."); } /* (non-Javadoc) * @see com.ivan.components.SubtractionService#subtract(long, long) */ @Override public long subtract(final long inNumber1, final long inNumber2) { return inNumber2 - inNumber1; } }

38

Adding the Subtraction Component to the Composite

References: • Service Component Architecture Assembly Model Specification, version 1.1, chapter 4 and 5 As with the Addition component, we add a child element to the element, defining the Subtraction component. The resulting composite file will look like this:

We also need to add the Subtraction component to the unit test composite. The final result looks like this:

39

Running the Subtraction Component Unit Test

If we now run the unit test we developed earlier, the Subtraction component should now pass the test. As with the original Addition component, we see that three instances of the Subtraction component are created: INFO: Add endpoint - (@1977026981)Endpoint: URI = SubtractionComponent#servicebinding(SubtractionServiceImpl/SubtractionServiceImpl) SubtractionServiceImpl instance created. SubtractionServiceImpl instance created. SubtractionServiceImpl instance created. Nov 22, 2009 4:11:16 PM org.apache.tuscany.sca.node.impl.NodeImpl stop

As before, adding the @Scope annotation to the component implementation class changes this behaviour so that one single instance of the component serves all requests.

40

4.2.3 Creating the Calculator Component Finally, we can create the Calculator component, a component that uses the Addition and Subtraction components we have implemented in previous sections to evaluate expressions on Reverse Polish Notation (RPN). Writing a Test Case for the Calculator Component

As with the Subtraction component, we start by implementing a test case using the base class for tests of SCA components. As with the other tests, this test class is also located in src/test/java: package com.ivan.components; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; import org.testng.annotations.Test; /** * Tests the Calculator service by starting the SCA node, retrieving * the component and invoking it. * * @author Ivan A Krizsan */ @Test public class CalculatorServiceTest extends SCAServiceTestBase { /* Constant(s): */ /* Instance variable(s): */ private CalculatorService mCalculatorService; /* (non-Javadoc) * @see com.ivan.components.SCAServiceTestBase#retrieveService() */ @Override protected void retrieveService() { /* Retrieve the Calculator service component. */ mCalculatorService = mSCANode.getService(CalculatorService.class, "CalculatorComponent"); } /** * Tests the calculator by evaluating some legal expressions. */ public void testGoodExpressions() { String theResult; theResult = mCalculatorService.calculate("9 2 + 6 - -2 +"); assertThat(theResult, is("3")); theResult = mCalculatorService.calculate("0 0 1 2 3 - - - +"); assertThat(theResult, is("-2")); } /** * Tests the calculators behaviour when asked to evaluate an * illegal expression. */ public void testBadExpressions() { boolean theExceptionFlag = false; try { mCalculatorService.calculate("9 +"); } catch (final Exception theException) { theExceptionFlag = true; }

41

assertThat(theExceptionFlag, is(true)); } }

The Calculator Component Service Interface

In src/main/java, create an interface named CalculatorService: package com.ivan.components; /** * This interface defines the operations made available by the * Calculator service. * The Calculator service operates on RPN expressions. * * @author Ivan A Krizsan */ public interface CalculatorService { /* Constant(s): */ /** * Calculates the result of the supplied expression and returns * it as a string. * * @param inExpression String containing the RPN expression to be * evaluated. * @return Result of the evaluated expression. */ String calculate(final String inExpression); }

The Calculator Component Implementation

In src/main/java, create the implementation of the Calculator service. Some logging statements have been added, which later will help us see another glimpse of the life cycle of SCA components: package com.ivan.components.impl; import java.util.Stack; import org.oasisopen.sca.annotation.Reference; import org.oasisopen.sca.annotation.Scope; import org.oasisopen.sca.annotation.Service; import com.ivan.components.AdditionService; import com.ivan.components.CalculatorService; import com.ivan.components.SubtractionService; /** * This class provides the implementation of the Calculator service, * a service that evaluates RPN expressions. * * @author Ivan A Krizsan */ @Service(CalculatorService.class) @Scope("COMPOSITE") public class CalculatorServiceImpl implements CalculatorService { /* Constant(s): */ /* Instance variable(s): */ private AdditionService mAdditionService; private SubtractionService mSubtractionService; private Stack mNumbersStack = new Stack(); /** * Default constructor. */ public CalculatorServiceImpl() { System.out.println("Creating CalculatorServiceImpl instance"); } /**

42

* Sets the Addition service used by the calculator to perform * additions. * * @param inAdditionService Addition service. */ @Reference public void setAdditionService(final AdditionService inAdditionService) { System.out.println("Injection Addition service into Calculator"); mAdditionService = inAdditionService; } /** * Sets the Subtraction service used by the calculator to perform * subtractions. * * @param inSubtractionService Subtraction service. */ @Reference(name="subtractionServ", required=true) public void setSubtractionService( final SubtractionService inSubtractionService) { System.out.println("Injection Subtraction service into Calculator"); mSubtractionService = inSubtractionService; } /* (non-Javadoc) * @see com.ivan.components.CalculatorService#calculate(java.lang.String) */ @Override public String calculate(final String inExpression) { String theParts[]; /* Split the expression into parts; numbers and operands. */ theParts = inExpression.split(" "); for (int i = 0; i < theParts.length; i++) { theParts[i] = theParts[i].trim(); } for (int i = 0; i < theParts.length; i++) { System.out.println("The current part: " + theParts[i]); if (theParts[i].matches("[\\-]*[0-9]+")) { /* * The part is a number. Parse it and put it on the * stack. */ Long theNumber = Long.parseLong(theParts[i]); mNumbersStack.push(theNumber); } else if (theParts[i].matches("[\\+\\-]")) { /* The part is an operator. */ char theOperator = theParts[i].charAt(0); Long theNumber1 = mNumbersStack.pop(); Long theNumber2 = mNumbersStack.pop(); Long theResult; switch (theOperator) { /* * The calculator only has support for addition * and subtraction. */ case '+': theResult = mAdditionService.add(theNumber1, theNumber2); System.out.printf("Added %d and %d = %d\n", theNumber1, theNumber2, theResult); break; case '-': theResult = mSubtractionService .subtract(theNumber1, theNumber2); System.out.printf("Subtracting %d from %d = %d\n",

43

theNumber1, theNumber2, theResult); break; default: throw new IllegalArgumentException( "Unrecognized operand: " + theParts[i]); } mNumbersStack.push(theResult); } else { throw new IllegalArgumentException( "Illegal token in expression: " + theParts[i]); } } /* * Create the result string, which consists of the space-separated * values on the stack. */ String theResultString = ""; while (!mNumbersStack.isEmpty()) { theResultString += mNumbersStack.pop().toString(); if (!mNumbersStack.isEmpty()) { theResultString += " "; } } return theResultString; } }

Note that: • The @Scope annotation with the value COMPOSITE is annotating the class. As before, this is in order for one single instance of the class to serve all requests. • There are two setter-methods, both annotated with @Reference. These methods will be used by the SCA runtime to inject the components used by the Calculator. • The @Reference annotation has two attributes. See below for a detailed discussion on the @Reference annotation. The @Reference Annotation

The @Reference annotation can, as seen on the setSubtractionService method, have two attributes: • The name attribute specifies the name of the reference. This name will later be used in the composite file when we specify which component reference is to be injected. If no name is specified, the name is derived from the name of the annotated method. In the case of the setSubtractionService method, the default reference name is subtractionService. • The required attribute indicates whether the reference must be set or not. When set to true, the SCA runtime will make sure that the reference has been set when the component is used, or an error will be generated. If set to false, which is the default value, and no reference is injected, the program will fail with a NullPointerException at runtime if trying to use the reference. • If the @Reference annotation annotates an instance field, the instance field must not be final. • The @Reference annotation can annotate fields or parameters of the ServiceReference type – see the section on callback service references below for details!

44

Adding the Calculator Component to the Composite

References: • Service Component Architecture Assembly Model Specification, version 1.1, section 4.3 With the Calculator component, declaring the component in the SCA composite file is not enough. We also need to inject references to the Addition and Subtraction components into the Calculator component, something that also is done in the composite file. First we'll take a look at the finished result:

Note that: • The element declaring the Calculator component contains two elements. The structure and contents of the element will be discussed in detail below. • The value of the name attribute in the first element matches the default name given the reference. See the section on the @Reference annotation above! • The value of the name attribute in the second element matches the value of the name attribute in the @Reference annotation on the setSubtractionService method in the CalculatorServiceImpl class.

45

We also need to add the Calculator component to the unit test composite. Modify the file in src/test/resources in the package com.ivan.components. The result should look like this:

Note that after having added the Calculator component to the unit test composite, there will be no further changes made to it, as opposed to the regular composite file which later will be modified to make services available to the world outside of the composite etc.

46

The Component Element in Detail

References: •

Service Component Architecture Assembly Model Specification, version 1.1, section 4.3

The component element is used to inject references to other components into the component in which element the element appears. The structure of the element looks like this:

The structure of the element used in a component declaration to establish relations to other components.

47

The element has the following attributes: Attribute Name

Description

name

Name of the reference to set. For instance, as specified in the @Reference annotation by the name attribute.

autowire

Whether the reference is autowired or not. Default is false.

requires

A list of policy intents for the reference.

policySets

A list of policy sets for the reference.

multiplicity

Specifies the number of of services that can be referenced by the reference. Possible values are: 0..1, 1..1, 0..n, 1..n Default value is 1..1.

target

List of one or more target services to be injected into this reference. The number of targets depend on the multiplicity setting above.

wiredByImpl

If set to true, then the reference is to be established programmatically at runtime. Default value is false.

nonOverridable

Indicates whether this reference can be promoted by a composite reference. True means that the reference must not be promoted by a composite reference. Default value is false.

The child elements of the element: Element Name

Description

interface

Interface describing the operations available in the referenced component. Specifying an interface for a reference is optional.

binding

Zero or more bindings to be used when communicating with the referenced component. If no binding specified, then the binding of the referenced component is used. If a binding is specified, it overrides the binding of the referenced component.

callback

One optional element specifying the callback. If present, the element must contain at least one element.

In our Calculator component we only used the name and target attributes of the element. 48

Running the Calculator Component Test

Finally the stage is set and we can to run the Calculator component unit test. The console output will look something like below. As usual, the output from the SCA runtime is in red and the log from our components is in black. INFO: Add endpoint - (@811495456)Endpoint: URI = CalculatorComponent#servicebinding(CalculatorService/CalculatorService) Creating CalculatorServiceImpl instance Injection Subtraction service into Calculator Injection Addition service into Calculator The current part: 9 The current part: 2 The current part: + AdditionServiceImpl instance created. Added 2 and 9 = 11 The current part: 6 The current part: SubtractionServiceImpl instance created. Subtracting 6 from 11 = 5 The current part: -2 The current part: + Added -2 and 5 = 3 The current part: 0 The current part: 0 The current part: 1 The current part: 2 The current part: 3 The current part: Subtracting 3 from 2 = -1 The current part: Subtracting -1 from 1 = 2 The current part: Subtracting 2 from 0 = -2 The current part: + Added -2 and 0 = -2 The current part: 9 The current part: + Nov 25, 2009 19:34:46 AM org.apache.tuscany.sca.node.impl.NodeImpl stop INFO: Stopping node: http://tuscany.apache.org/sca/1.1/nodes/default0

From the above output we can observe the following: •

The Calculator component is injected with references to the Addition and Subtraction services after it has been instantiated.



The Addition component is not instantiated until it is to be used, that is, not until a “+” appears in the expression that is evaluated.



The Subtraction component is not instantiated until it is to be used, that is, not until a “-” appears in the expression that is evaluated.

49

5 Exposing a SOAP Web Service In the previous chapter we developed a Calculator component/service that perform additions and subtractions. In this chapter we will make the service available to the world outside our SCA node as a SOAP web service. Note that, depending on your environment, the Calculator component unit test may fail after having exposed the Calculator service. In my case, this is caused by the SCA implementation not being able to properly determine the address of my computer and thus failing to contact the service.

5.1 Making the Service Remotely Accessible References: • Service Component Architecture Assembly Model Specification, version 1.1, sections 7.1, 7.2. • Service Component Architecture POJO Component Implementation Specification Version 1.1, section 2.2. • Service Component Architecture SCA-J Common Annotations and APIs Specification Version 1.1, section 10.18. A service being remotely accessible means that it is accessible to clients running in an operating system process different from that of the service. In order to be able to expose the Calculator service as a SOAP web service, we need to denote its interface as remotely accessible. In the case of Java interfaces, this is done by annotating the service interface with the @Remotable annotation: package com.ivan.components; import org.oasisopen.sca.annotation.Remotable; /** * This interface defines the operations made available by the * Calculator service. * The Calculator service operates on RPN expressions. * * @author Ivan A Krizsan */ @Remotable public interface CalculatorService { /* Constant(s): */ /** * Calculates the result of the supplied expression and returns * it as a string. * * @param inExpression String containing the RPN expression to be * evaluated. * @return Result of the evaluated expression. */ String calculate(final String inExpression); }

50

5.1.1 Remotable Interfaces There are some things to consider when making a service interface remotable: • •

• •

Method or operation overloading must not occur in a remotable interface. Overloading may occur in local interfaces. All data exchange, regardless of whether the client is local or remote, is performed using pass-by-value semantics. Pass-by-reference semantics for local clients of a service with a remotable interface can be enabled by, in Java, using the @AllowPassByReference annotation. Method parameter types and return value types must be compatible with the marshalling technology used by the service binding. With bi-direcitonal services, both interfaces must be either remotable or local. Local and remotable interfaces cannot be mixed.

5.2 Declaring a Binding for a Component Using SCA vocabulary, a binding is a mechanism by which a service or reference is accessed. Declaring a binding for a SCA service is done in a SCA composite file. There are two options on how to do this; either use a composite service element or a component service element. We will now see examples of both these approaches.

5.2.1 Using Composite Service Elements First an example of the preferred way of declaring a binding for a service is shown, a way that uses a element external to the element of the component that is to be exposed as a service. This element is called the composite service element. The reason for this being the preferred way is that declaring a service outside of the component that is to be exposed as a service enables separation of concerns, as we will see in chapter 7. Note! The current version of Apache Tuscany (2.0M4) fails to honor binding settings in a element that is external to the element of the component it is to expose. At times, I have also had problems with the service not being exposed at all. The kind people on the Tuscany mailing list has recommended me to use elements in elements, for the time being. Thus, the following example may not work and is to be seen more as a theoretical discussion on the preferred way of doing things. The SCA specification says that: “The services of a composite are defined by promoting services defined by components contained in the composite. A component service is promoted by means of a composite service element.”

51

This would, in the case of our Calculator component, cause the composite definition to look like this: ...

Note that: • There is a element being the immediate child of the element. This element is called composite service element and it will be discussed in detail in the next section. • There is also a element that is the immediate child of the element in which the Calculator component is declared. This element is called component service element and it will be described in more detail in a subsequent section.

52

5.2.2 The Composite Element in Detail References: • Service Component Architecture Assembly Model Specification, version 1.1, section 5.1. The composite element is used to expose services of components in a composite to users of the composite. SCA literature commonly refers to this as promoting a service. As we will see in chapter 7, the composite element need not to be declared in the same composite as the composite in which the component which services it exposes. The composite element has the following structure:

Structure of the composite element.

53

Descriptions of the attributes of the composite element: Attribute Name

Description

name

Name of the service. Must be unique among names of service for the component in question. This name must be the same as the name of the component service element, which, for components implemented in Java, in turn must match the name specified in the @Service annotation.

requires

A list of policy intents.

policySets

A list of policy sets.

promote

Specifies which service is to be promoted. The value is to be of the form “ComponentName/ServiceName”. The name of the service may be omitted if the component only has one single service. Required.

Descriptions of the child elements of the composite element: Element Name Description interface

One, optional, interface describing the operations available in the service. If no interface specified, then the interface specified in the component service element or in the @Service annotation will be used. Not only Java interfaces can be used. Later in the document, we will see how, for instance, a WSDL can be used as a service interface.

binding

Zero or more bindings the service will be made available over. See below for details!

callback

One optional element specifying the callback. If present, the element must contain at least one element.

54

Service Bindings

References: • Service Component Architecture Assembly Model Specification, version 1.1, chapter 8. Bindings are used by services and references and specify in what way the service or the referenced entity is to be accessed. Some examples of bindings, with their corresponding elements, available in Apache Tuscany are: Binding Binding Element Comments SCA Service



Binding used within one and the same SCA Domain. Default binding type. Not interoperable.

Web Service



WSDL-based binding.

Stateless Session EJB



Both EJB versions 2 and 3 are supported.

Enterprise Information Systems Service

Currently only support Java Connector Architecture.

JMS



ATOM



JSON-RPC



The JSON-RPC protocol.

RMI



The Java RMI protocol.

AJAX



For interaction with AJAX clients.

CORBA



HTTP



Supports method binding over HTTP allowing for CRUD operations and conditional CRUD operations.

RSS



Consumes or provides a RSS feed.

Note that the above table may include binding(s) that are not described in the SCA specification documents and only available in Apache Tuscany. Also note that the different binding elements listed above may belong to different namespaces. One of the benefits of using SCA is that the binding is decoupled from the component implementation and, by modifying the composite definition, we can modify, for instance, the binding used between two components without having to modify the component implementation.

55

The structure of the basic element looks like this:

The structure of the basic element.

56

Attributes of the basic element are: Attribute Name

Description

uri

For a reference, the uri contains the target of the reference. For a service, the uri specifies the address (or similar) at which clients can access the service. The uri attribute is optional.

name

Name of the binding. Default name is the name of the service or reference. A service or reference may have multiple bindings, all of which must have names unique within the service or reference. The name attribute is optional if there is only one single binding for a given service or reference.

requires

A list of policy intents for the reference.

policySets

A list of policy sets for the reference.

Child elements of the basic element are: Element Name

Description

wireFormat

If the binding supports more than one wire format, this element allows for specification of the data format which will be used when data is transmitted between the component and the reference or between the client and the service. XML is one example of a wire format. Zero or one wire format can be specified for a binding.

operationSelector

In the case where the binding generates messages that does not specify the operation to be invoked, this attribute is used to specify the operation that is to be invoked in response to a message. In some cases, it is also possible to apply filtering to the messages that are to be forwarded to the operation in question.

Specific binding elements, such as or , may introduce additional attributes and/or child elements of the element. These will be described in connection to the corresponding binding element.

57

5.2.3 Using Component Service Elements References: • Service Component Architecture Assembly Model Specification, version 1.1, section 4.2. To solve the problem with not being able to use the composite element, we use a component service element, that is, a element that is a child element of the element which is to be made available as a service. The working Calculator composite definition looks like this: element external to a component to promote a service and specify a binding due to problems in Tuscany 2.0M4. --> declarations external to the component service). Thus, the element must be for things to work properly.

to honor the requires element in service (which promotes a placed here, in order

-->


58

The Component Element in Detail

The component element differs only from the composite element, that we previously have seen, in that it lacks the promote attribute – the component being promoted is the component in which the component element appears. The component element has the following structure:

Structure of the component element.

59

Descriptions of the attributes of the component element: Attribute Name Description name

Name of the service. Must be unique among names of service for the component in question. This name must be the same as the name of the component service element, which, for components implemented in Java, in turn must match the name specified in the @Service annotation.

requires

A list of policy intents.

policySets

A list of policy sets.

Descriptions of the child elements of the component element: Element Name Description interface

One, optional, interface describing the operations available in the service. If no interface specified, then the interface specified in the component service element or in the @Service annotation will be used. Not only Java interfaces can be used. Later in the document, we will see how, for instance, a WSDL can be used as a service interface.

binding

Zero or more bindings the service will be made available over. See the section on service bindings above for details!

callback

One optional element specifying the callback. If present, the element must contain at least one element.

This concludes the theoretical discussion on the two different kinds of elements in composites and we are ready for some action.

60

5.3 Starting Up the Calculator Web Service To expose the Calculator service, we implement a small program that, in the same manner as in the unit tests of the different components, starts up the SCA node that contains the Calculator service. Add the following class to our project: package com.ivan.server; import java.io.IOException; import org.apache.tuscany.sca.node.Node; import org.apache.tuscany.sca.node.NodeFactory; /** * This server program shows how to create an SCA runtime, and start it which * activates a Calculator Web service endpoint. * * @author Ivan A Krizsan */ public class CalculatorServerStarter { public static void main(final String[] args) { Node theNode = NodeFactory.newInstance().createNode(); theNode.start(); try { System.out .println("Calculator server started (press enter to shutdown)"); System.in.read(); } catch (IOException theException) { theException.printStackTrace(); } theNode.stop(); System.out.println("Calculator server stopped"); } }

When run, the above class will start the SCA node, wait for enter to be pressed in the console and, finally, shut down the SCA node. When run, the program will generate console output similar to this: Dec 3, 2009 8:47:14 PM org.apache.tuscany.sca.node.impl.NodeImpl start INFO: Starting node: http://tuscany.apache.org/sca/1.1/nodes/default0 domain: tuscany.apache.org Dec 3, 2009 8:47:15 PM org.apache.tuscany.sca.node.impl.NodeFactoryImpl loadContributions INFO: Loading contribution: file: ... Calculator server started (press enter to shutdown) Dec 3, 2009 8:47:42 PM org.apache.tuscany.sca.node.impl.NodeImpl stop INFO: Stopping node: http://tuscany.apache.org/sca/1.1/nodes/default0 Calculator server stopped

If the program is run inside Eclipse, you may need to click in the Console view before pressing return, in order to be able to stop the Calculator service. If you are developing in Eclipse, you may also need to clean and rebuild the project, to make sure that all the SCA configuration files have been properly copied to the output directories.

61

5.4 Testing the Calculator Web Service At this stage we have, at least theoretically, a Calculator web service. In order to make sure that it works as expected, we will now test it.

5.4.1 Retrieving the WSDL of the Service The first test consists of retrieving the WSDL of the Calculator service. •

Using the program developed in the previous section, start the Calculator service. Do not stop the service!



Using a browser, access the URL specified in the element of the component element, namely http://localhost:8085/CalcService?wsdl



The WSDL should look something like this:



62



63

5.4.2 Testing Using soapUI Using soapUI, we will perform a very simple test of the Calculator web service, sending a single request and examining the response. •

Start up soapUI.



Create a new project by right-clicking on Projects node in the Navigator window and select New soapUI Project.



You'll be presented with a dialog asking you for information about the project. Enter a name of the project and the URL of the Calculator service's WSDL, as shown in the picture below.

64



When soapUI has finished processing the WSDL and created the sample request, navigate to, and double-click, the Request 1 node in the Navigator.



Modify the SOAP request in the Request 1 window by filling in the argument to the calculate operation as shown in the picture below. Use the formula “1 2 + 4 -” (without quotes).



Click the little green triangle in the top-left corner of the Request 1 window. This sends the request to the Calculator service and a response should appear on the right in the Request 1 window.



Verify the result of the request. It should be -1, as in the picture below.

This concludes the testing of the Calculator web service. We have successfully exposed the Calculator SCA component as a web service.

65

6 Injecting Properties Into Components References: •

Service Component Architecture POJO Component Implementation Specification, version 1.1, chapter 4.



Service Component Architecture SCA-J Common Annotations and APIs Specification, version 1.1, section 10.15

This chapter discusses injection of property values into SCA components.

Figure visualizing an SCA component with two properties.

In this section, we will develop a PrintProperties component that prints the values of some properties. We will see how values of properties can be injected into a component. I'll start with some code to show what property injection looks like in Java code using SCA. In preparation for this example, please set up a Eclipse project named “SCAPropertyPrinter”, with the Maven artifact ID being the same as the project name, as described in chapter 2.

66

6.1 The PropertyPrinter Component Service Interface The interface of the PropertyPrint component, which is the component in which properties will be injected, will remain the same during the entire example. In src/main/java, create an interface named CalculatorService: package com.ivan.components; /** * This interface defines the operations available in the PropertyPrinter * service. * * @author Ivan A Krizsan */ public interface PropertyPrinterService { /* Constant(s): */ /** * Prints the properties that have been injected into the service * implementation class. */ void printProperties(); }

6.2 The PropertyPrinter Component Implementation In src/main/java, create the first incarnation of the implementation of the PropertyPrinter component. package com.ivan.components.impl; import org.oasisopen.sca.annotation.Property; import org.oasisopen.sca.annotation.Scope; import org.oasisopen.sca.annotation.Service; import com.ivan.components.PropertyPrinterService; /** * This class provides the implementation of the PrintProperties service, * a service that prints injected properties to the console. * * @author Ivan A Krizsan */ @Scope("COMPOSITE") @Service(PropertyPrinterService.class) public class PropertyPrinterServiceImpl implements PropertyPrinterService { private String mFirstName; /* (non-Javadoc) * @see com.ivan.components.PropertyPrinterService#printProperties() */ @Override public void printProperties() { System.out.println("***** Starting to print properties:"); System.out.printf("firstName = %s\n", mFirstName); System.out.println("***** Finished printing properties:"); } /** * Sets first name to supplied value. * * @param inFirstName New first name. */ @Property(name="firstName", required=true) public void setFirstName(final String inFirstName) { System.out.printf("setFirstName(%s)\n", inFirstName);

67

mFirstName = inFirstName; } }

Note that: • We use the @Scope annotation with the value “COMPOSITE” to indicate that one single instance of the component is to serve all requests. • We use the @Service annotation with the value PropertyPrinterService interface to specify the services offered by the component. • There is an instance variable named mFirstName with a corresponding setter methods, setFirstName. • The setFirstName setter method is annotated with the @Property annotation, in which the name and required attributes appear. The @Property annotation will be described in more detail in the following section.

6.3 The @Property Annotation The @Property annotation is used to indicate a place where SCA property injection can be performed. The annotation can be placed at the following locations in a Java class: •

An instance field. The instance field must not be final. The earlier SCA specification restricted private instance fields to be annotated, but the 1.1 specification has removed such restrictions.



A setter method.



A constructor parameter.

The @Property annotation has the following attributes: Attribute Name

Comments

name

Optional (required when annotation applied to a constructor parameter). Specifies the name of the property. See the detailed discussion on default values etc below.

required

Optional boolean attribute specifying whether injection of a value is required or not. Default value is true. When annotating a constructor parameter, the required attribute must be true.

(continued on next page)

68

6.3.1 The name Attribute The name attribute of the @Property annotation specifies the name of the property. This name is used in the appropriate composite file when specifying what value to inject into the property. Depending on what entity the annotation annotates, it has the following default values: Annotated Entity

Default Value

Instance field.

The name of the instance field. Must be unique within the component.

Setter method.

The name of the setter method minus the first three “set” character and with the fourth character changed to lowercase. Example: The setter method setMessageParameter will yield a property name “messageParameter”.

Constructor parameter.

No default name. The name attribute must be used and a name must be specified.

6.4 Create and Configure the SCA Composites In src/main/resources, create the PropertyPrinter.composite composite configuration file. The first version of this file looks like this: element. --> Ivan

The new thing in the above composite file is the element. It will be described in detail in a subsequent section.

69

6.5 Adding the Composite to the SCA Contribution Metadata Document As we've seen earlier, we need to add a reference to the composite in the SCA Contribution file. • Locate the sca-contribution.xml file in the META-INF folder in src/main/resources and open it. • Add a element to the file so it looks like this (heading comment removed to conserve space):

6.6 Creating the Client Class The client class in this example is responsible for starting the SCA node containing the PropertyPrinter service and invoking it. • In the com.ivan.client package, create a class named SCAPropertyPrinterClient. The class is implemented as follows: package com.ivan.client; import org.apache.tuscany.sca.node.Node; import org.apache.tuscany.sca.node.NodeFactory; import com.ivan.components.PropertyPrinterService; /** * This class creates and starts the SCA node containing the * PropertyPrinter service, retrieves the service and invokes it. * * @author Ivan A Krizsan */ public class SCAPropertyPrinterClient { /* Constant(s): */ /* Instance variable(s): */ public static void main(final String[] args) { Node theNode = NodeFactory.newInstance().createNode(); theNode.start(); System.out.println("About to invoke PropertyPrinter service."); /* Retrieve the PropertyPrinter service component. */ PropertyPrinterService thePropertyPrinterService = theNode.getService(PropertyPrinterService.class, "PropertyPrinterComponent"); /* Invoke the service. */ thePropertyPrinterService.printProperties(); System.out.println("Finished invoking PropertyPrinter service."); theNode.stop(); } }

70

6.7 Running the PropertyPrinter Client If we now run the PropertyPrinter client, the following console output should be generated. As usual, the output generated by Tuscany is in red and the output generated by our code is in black. ... INFO: Add endpoint - (@1522338280)Endpoint: URI = PropertyPrinterComponent#servicebinding(PropertyPrinterService/PropertyPrinterService) About to invoke PropertyPrinter service. setFirstName(Ivan) ***** Starting to print properties: firstName = Ivan ***** Finished printing properties: Finished invoking PropertyPrinter service. Dec 9, 2009 8:17:53 PM org.apache.tuscany.sca.node.impl.NodeImpl stop INFO: Stopping node: http://tuscany.apache.org/sca/1.1/nodes/default0 ...

We have successfully injected one property into our component! In the following sections, we will explore property injection into components further.

71

6.8 The Component Element References: • Service Component Architecture Assembly Model Specification, version 1.1, section 4.4 Having seen a first example of property injection into SCA components, we will now explore the available options in more depth, starting with a closer look at the component element. This element is, as we have seen, a child element of the element in a composite file. The structure of the element looks like this:

Structure of the component element in a composite. This element is used to inject properties into components.

72

The attributes of the element are: Attribute Name

Description

name

Name of the property. Required. Must be unique within the component and must match the name of an existing property specified using the @Property annotation (for components implemented in Java).

type

Qualified name of an XML schema type specifying the type of the property. Optional. If the type attribute is present, then the element attribute must not be present.

element

Qualified name of an XML schema global element specifying the type of the property. Optional. If the element attribute is present, then the type attribute must not be present.

source

XPath expression referring to a property of the composite, in which the component appears, from which the value of this property will be obtained. Optional. If both the source and file attributes are specified, the source attribute will be used.

file

URI pointing to a file from which the value of the property will be obtained. Optional. If both the source and file attributes are specified, the source attribute will be used.

many

Indicates whether the property contains one single value (false) or multiple values (true). A single-valued property cannot be made multivalued by setting this attribute to false, but a multi-valued property can be made singlevalued. Optional.

value

A string specifying the value of the property. Optional.

requires

A list of policy intents.

policySets

A list of policy sets.

The element can have zero or more child elements. If the type is a collection or an array, then there may be more than one element. If not, there must at most be one single element.

73

There are five different ways in which the value of a property can be specified: Description Example Comments Using the value attribute.



The property type must be an XML schema simple type and the value attribute must contain a single value of that type.

Using one or more subelement(s).

firstValue secondValue

The property type must be an XML schema simple or complex type.

As content of the element.

Ivan

If the value of the property is specified using a child element of the element, then the property type must be a global XML schema element and the child element of the element must be an instance of the global XML schema element.

Using the source attribute and an XPath expression referencing a property of the composite or a part of a property of the composite. Using the file attribute specifying an URI which refers to a file. The contents of the file will be the value of the attribute.

More examples will follow as the different options are explored.

74

See the documentation of the java.net.URI class in the JDK API for more information on the format of a URI. The contents of the file must be XML.

6.8.1 Injecting Values Using the value Attribute Using the value attribute of the element, we can inject single values of XML schema types into a component. Values can be injected into both single-value and multiple-value fields. In the following example, we will modify the PropertyPrinter to inject a single string into a field holding a list of strings. • First, we'll modify the component implementation class PropertyPrinterServiceImpl adding an instance field and modifying the printProperties method. Additions have been highlighted. ... public class PropertyPrinterServiceImpl implements PropertyPrinterService { private String mFirstName; /* A component property that can contain many strings. */ @Property(name="stringList", required=false) private List mStringList; /* (non-Javadoc) * @see com.ivan.components.PropertyPrinterService#printProperties() */ @Override public void printProperties() { System.out.println("***** Starting to print properties:"); System.out.printf("firstName = %s\n", mFirstName); System.out.println("stringList = " + mStringList); System.out.println("stringList type = " + mStringList.getClass().getName()); System.out.println("***** Finished printing properties:"); } ...



Second, we add a element to the composite (in the file PropertyPrinter.composite):

... Ivan




When running the PropertyPrinter client, we now see the following console output from our component:

About to invoke PropertyPrinter service. setFirstName(Ivan) ***** Starting to print properties: firstName = Ivan stringList = [test entry] stringList type = java.util.ArrayList ***** Finished printing properties: Finished invoking PropertyPrinter service.

Note that: • The SCA runtime instantiated the list mStringList for us using an ArrayList. Of course we could have instantiated it ourselves.

75

6.8.2 Injecting Values Using Subelements If we want to inject multiple values into a property that can hold many values, such as the string list in the previous example, we can use multiple elements inside the element. • Modify the element added to the composite in the previous example to look like this. Modified parts have been highlighted. ... Ivan John Lennon Paul McCartney Ringo Starr George Harrison




When running the PropertyPrinter client, the name of the members of The Beatles, among other things, will be listed on the console. New output has been highlighted.

About to invoke PropertyPrinter service. setFirstName(Ivan) ***** Starting to print properties: firstName = Ivan stringList = [John Lennon, Paul McCartney, Ringo Starr, George Harrison] stringList type = java.util.ArrayList ***** Finished printing properties: Finished invoking PropertyPrinter service.

76

6.8.3 Injecting Values From the Contents of the Element We have already seen an example of how to inject a plain string specified in the contents of the element into a field, so we'll now look at a more advanced example injecting complex XML contents into a field which type is not a simple XML type. Note that this example will fail with an exception when using Apache Tuscany 2.0M4 due to a bug. I have been in contact with the developers and this issue will be fixed in a future version. Again, we will be extending the PropertyPrinter example developed in the previous sections. •

Implement the Person class as follows:

package com.ivan.beans; public class Person { private String mFirstName; private String mLastName; private int mAge; public String getFirstName() { return mFirstName; } public void setFirstName(final String inFirstName) { mFirstName = inFirstName; } public String getLastName() { return mLastName; } public void setLastName(final String inLastName) { mLastName = inLastName; } public int getAge() { return mAge; } public void setAge(final int inAge) { mAge = inAge; } @Override public String toString() { return "PersonBean [age=" + mAge + ", first name=" + mFirstName + ", last name=" + mLastName + "]"; } }

77



Modify the component implementation class PropertyPrinterServiceImpl adding an instance field and modifying the printProperties method. Additions have been highlighted.

... public class PropertyPrinterServiceImpl implements PropertyPrinterService { private String mFirstName; /* A component property that can contain many strings. */ @Property(name="stringList", required=false) private List mStringList; /* A component property of a custom type. */ @Property(name="person") private Person mPerson; /* (non-Javadoc) * @see com.ivan.components.PropertyPrinterService#printProperties() */ @Override public void printProperties() { System.out.println("***** Starting to print properties:"); System.out.printf("firstName = System.out.println("stringList System.out.println("stringList System.out.println("person = "

%s\n", mFirstName); = " + mStringList); type = " + mStringList.getClass().getName()); + mPerson);

System.out.println("***** Finished printing properties:"); } ...



Add a element to the composite in the file PropertyPrinter.composite:

... John Lennon Paul McCartney Ringo Starr George Harrison element and that child elements of the element must not be namespace qualified. --> DeeDee Ramone 49 ...



When running the PropertyPrinter client, we can see the additional output from the Person bean instance. New output has been highlighted.

About to invoke PropertyPrinter service. setFirstName(Ivan) ***** Starting to print properties: firstName = Ivan stringList = [John Lennon, Paul McCartney, Ringo Starr, George Harrison] stringList type = java.util.ArrayList person = PersonBean [age=49, first name=DeeDee, last name=Ramone] ***** Finished printing properties: Finished invoking PropertyPrinter service.

78

The XML schema defining the global element person and the Person type looks like this:

Note that: • The namespace declaration for the http://www.ivan.com.person namespace is not strictly necessary. It, as well as the namespace prefix on the element, can be left out. • The null namespace (xmlns=””) must be specified on the element. If not specified, Apache Tuscany will not properly inject values into the Person instance. • Child elements of the element must not be qualified. If they are, Apache Tuscany will not properly inject values into the Person instance. • In an composite element, either the type or the element attribute can be used, but not both. • When using the element attribute of the element, the value must be the qualified name of a global element type. • When using the type attribute of the element, the value must be the qualified name of a type defined in an XML schema or an XML Schema simple type. If we wanted to use the type attribute in the element, the property definition in the composite would look like this: ... DeeDee Ramone 49 ...

The result is the same as when using the element attribute of the element.

79

6.8.4 Injecting Values from Composite Properties So far, we have only seen properties of components. Composites may also have properties and values of such properties may in turn be injected into component properties. Composite properties will be investigated in more detail in the next chapter. For the sake of the following example, composite properties are properties defined as immediate children of a element. As in the previous section, we will be extending the PropertyPrinter example developed in the earlier sections when exploring the component element. • Modify the component implementation class PropertyPrinterServiceImpl adding four instance field and modifying the printProperties method. Additions have been highlighted. ... @Property(name="person") private Person mPerson; @Property(name="dbLogin", required=false) private String mDatabaseLogin; @Property(name="dbPassword", required=false) private String mDatabasePassword; @Property(name="dbUrl", required=false) private String mDatabaseUrl; @Property(name="dbDriver", required=false) private String mDatabaseDriver; /* (non-Javadoc) * @see com.ivan.components.PropertyPrinterService#printProperties() */ @Override public void printProperties() { System.out.println("***** Starting to print properties:"); System.out.printf("firstName = %s\n", mFirstName); System.out.println("stringList = " + mStringList); System.out.println("stringList type = " + mStringList.getClass().getName()); System.out.println("person = " + mPerson); System.out.println("databaseLogin = " + mDatabaseLogin); System.out.println("databasePassword = " + mDatabasePassword); System.out.println("databaseUrl = " + mDatabaseUrl); System.out.println("databaseDriver = " + mDatabaseDriver); System.out.println("***** Finished printing properties:"); } ...

(continued on next page)

80



Add a composite element in the file PropertyPrinter.composite. A default value of the property, contained in the element, has been provided. Additions have been highlighted.

root secret jdbc:yoursql://127.0.0.1/testschema?characterEncoding=utf-8 com.yoursql.jdbc.Driver ...



Add a component element to the PropertyPrinterComponent, again in the PropertyPrinter.composite file.

... DeeDee Ramone 49




When running the PropertyPrinter client, we see the database properties being printed. New output has been highlighted.

About to invoke PropertyPrinter service. setFirstName(Ivan) ***** Starting to print properties: firstName = Ivan stringList = [John Lennon, Paul McCartney, Ringo Starr, George Harrison] stringList type = java.util.ArrayList person = PersonBean [age=49, first name=DeeDee, last name=Ramone] databaseLogin = root databasePassword = secret databaseUrl = jdbc:yoursql://127.0.0.1/testschema?characterEncoding=utf-8 databaseDriver = com.yoursql.jdbc.Driver ***** Finished printing properties: Finished invoking PropertyPrinter service.

Note that: • Different XPath expressions can be used in the source attribute of the component element. Above, two different examples are shown.

81

6.8.5 Injecting the Contents of a File XML content can also be stored in a file, which can be retrieved by the SCA runtime and injected into components. In the following example, we will see how an instance of the Person bean we have used earlier is populated with data from a file. Note that this example will fail with an exception when using Apache Tuscany 2.0M4 due to a bug. I have been in contact with the developers and this issue will be fixed in a future version. •

Modify the component implementation class PropertyPrinterServiceImpl adding one instance field and modifying the printProperties method. Additions have been highlighted.

... private String mDatabaseDriver; /* This property is to contain data from a file. */ @Property(name="fileContents", required=false) private Person mFileContents; /* (non-Javadoc) * @see com.ivan.components.PropertyPrinterService#printProperties() */ @Override public void printProperties() { System.out.println("***** Starting to print properties:"); System.out.printf("firstName = %s\n", mFirstName); System.out.println("stringList = " + mStringList); System.out.println("stringList type = " + mStringList.getClass().getName()); //System.out.println("person = " + mPerson); System.out.println("databaseLogin = " + mDatabaseLogin); System.out.println("databasePassword = " + mDatabasePassword); System.out.println("databaseUrl = " + mDatabaseUrl); System.out.println("databaseDriver = " + mDatabaseDriver); System.out.println("fileContents = " + mFileContents); System.out.println("***** Finished printing properties:"); } ...



Add a element to the composite in the file PropertyPrinter.composite:

...




In src/main/resources, next to the composite file, create a file named “property-file.txt” with the following contents:

Dee Schneider 63

82



When running the PropertyPrinter client, we see that a Person bean instance has been created and populated with the data from the file. New output has been highlighted.

About to invoke PropertyPrinter service. setFirstName(Ivan) ***** Starting to print properties: firstName = Ivan stringList = [John Lennon, Paul McCartney, Ringo Starr, George Harrison] stringList type = java.util.ArrayList person = PersonBean [age=49, first name=DeeDee, last name=Ramone] databaseLogin = root databasePassword = secret databaseUrl = jdbc:yoursql://127.0.0.1/testschema?characterEncoding=utf-8 databaseDriver = com.yoursql.jdbc.Driver fileContents = PersonBean [age=63, first name=Dee, last name=Schneider] ***** Finished printing properties: Finished invoking PropertyPrinter service.

Note that: • In order for Tuscany to be able to match child elements of to properties of the Person bean, the null XML namespace must be specified on the element, as seen in the property-file.txt file. • Additionally, the names of the child elements of the element must match the names of the properties in the Person bean class.

83

6.9 Constructor Injection Out of the three different program elements in a Java class that can be annotated using the @Property annotation, we have seen two, namely instance fields and setter methods. In this final example on property injection into components, we will look at the last option – constructor injection. •

Modify the component implementation class PropertyPrinterServiceImpl adding a constructor.

... /** * Constructor that takes a parameter that is to be injected * at creation time. * Note the @Property annotation on the constructor parameter and that * when annotating a constructor parameter, the required attribute * cannot be false. * * @param inConstructorParam Constructor parameter. */ public PropertyPrinterServiceImpl( @Property(name="constructorParam") final String inConstructorParam) { System.out.println("Constructor parameter = " + inConstructorParam); } ...



Add a element to the composite in the file PropertyPrinter.composite. Additions have been highlighted.

... Constructor parameter value




Running the PropertyPrinter client, we'll see the output from the constructor appear right after the PropertyPrinter service is to be invoked. New output has been highlighted.

About to invoke PropertyPrinter service. Constructor parameter = Constructor parameter value setFirstName(Ivan) ***** Starting to print properties: firstName = Ivan stringList = [John Lennon, Paul McCartney, Ringo Starr, George Harrison] stringList type = java.util.ArrayList person = PersonBean [age=49, first name=DeeDee, last name=Ramone] databaseLogin = root databasePassword = secret databaseUrl = jdbc:yoursql://127.0.0.1/testschema?characterEncoding=utf-8 databaseDriver = com.yoursql.jdbc.Driver fileContents = PersonBean [age=63, first name=Dee, last name=Schneider] ***** Finished printing properties: Finished invoking PropertyPrinter service.

Note that: • The @Property annotation can annotate constructor parameters. • The required attribute of the @Property annotation can not be false, when annotating constructor parameters.

84

7 Assembling Composites In this chapter, we will see how SCA allows for inclusion of composites into other composites. The include mechanism allows for separation of different aspects of an SCA assembly; for instance, services to be exposed to external systems can be specified in one composite file, while the component implementing the service can be specified in another composite file. In this chapter we will look at an example that contains five different composite files: • • • • •

One composite file contains the declaration of the component implementing a calculator service that evaluates expressions containing long integers. One composite file contains the service declaration specifying how the above component is exposed as a service. One composite file contains the declaration of the component that evaluates strings containing RPN mathematical expressions. One composite file contains the wiring that injects the component providing calculator service into the component that evaluates RPN expressions. One composite file that assembles all of the above composite files.

Graphically, the above looks like this:

Using five composites to assemble a service that evaluates RPN expressions.

When implementing this example, we'll try to mimic the process of separately developing the different components and then assembling them. In a real team environment, certain things, such as implementing the two components, would probably happen in parallel. As usual, start by setting up a new Eclipse project as described in chapter 2. I will name my project “SCAAssemblingComposites”.

85

7.1 The Calculator Component The Calculator component provides implementations of the following operations on long integers: • Addition. • Subtraction. • Multiplication. • Division. It consists of one single component that has no dependencies on other components.

7.1.1 Calculator Interface As usual, a SCA component implemented in Java consists of an interface and a class. First we implement the CalculatorService interface: package com.ivan.components; /** * Interface defining the operations exposed by the Calculator Service/ * * @author Ivan A Krizsan */ public interface CalculatorService { /** * Adds the two supplied numbers. * * @param inNumber1 First number to add. * @param innumber2 Second number to add. * @return Sum of the numbers. */ long add(final long inNumber1, final long inNumber2); /** * Subtracts the second supplied number from the first supplied number. * * @param inNumber1 First number. * @param inNumber2 Number to subtract from first number. * @return Difference between the numbers. */ long subtract(final long inNumber1, final long inNumber2); /** * Multiplies the two numbers and returns the product. * * @param inFactor1 First number to multiply. * @param inFactor2 Second number to multiply. * @return Product of two supplied numbers. */ long multiply(final long inFactor1, final long inFactor2); /** * Divides the first supplied number with the second supplied number. * * @param inNumber1 Number to be divided. * @param inNumber2 Number to divide by. Must not be zero. * @return Result of division. */ long divide(final long inNumber1, final long inNumber2); }

86

7.1.2 Calculator Implementation The CalculatorService class implements all the operations of the Calculator component itself, without delegating to other components: package com.ivan.components.impl; import org.oasisopen.sca.annotation.Scope; import org.oasisopen.sca.annotation.Service; import com.ivan.components.CalculatorService; /** * Provides an implementation of the Calculator Service that * performs calculations on long integers. * * @author Ivan A Krizsan */ @Scope("COMPOSITE") @Service(CalculatorService.class) public class CalculatorServiceImpl implements CalculatorService { /** * Default constructor. */ public CalculatorServiceImpl() { System.out.println("Creating CalculatorServiceImpl"); } /* (non-Javadoc) * @see com.ivan.components.CalculatorService#add(long, long) */ public long add(final long inNumber1, final long inNumber2) { return inNumber1 + inNumber2; } /* (non-Javadoc) * @see com.ivan.components.CalculatorService#divide(long, long) */ public long divide(final long inNumber1, final long inNumber2) { return inNumber1 / inNumber2; } /* (non-Javadoc) * @see com.ivan.components.CalculatorService#multiply(long, long) */ public long multiply(final long inFactor1, final long inFactor2) { return inFactor1 * inFactor2; } /* (non-Javadoc) * @see com.ivan.components.CalculatorService#subtract(long, long) */ public long subtract(final long inNumber1, final long inNumber2) { return inNumber1 - inNumber2; } }

87

7.1.3 Calculator Component Composite The Calculator component composite specifies which class implements the functionality of the component as well as the interface of the component. In src/main/resources, create a file named “CalculatorComponent.composite”, with the following contents:

7.1.4 Calculator Service Composite In order to show how to declare the service and specify the binding to be used when interacting with the service in a separate composite file, we introduce the Calculator Service composite file. Given that this example application resides in one single JVM, this is slightly contrived. However, developing in this, more modular, fashion does not require additional effort and, in my opinion, increases the flexibility of the application in the event of future modifications. In src/main/resources, create a file named “CalculatorService.composite” which contains the following service and binding definition:

The is the default binding type and, strictly speaking, need not to be specified unless you want to override an existing binding or list a number of different binding types where the SCA binding is one of them. This completes the Calculator component.

88

7.2 Expression Evaluator Component The Expression Evaluator component takes a string containing a RPN expression As soon as the Calculator interface were available, the development of the Expression Evaluator component could have started. The Expression Evaluator component uses the Calculator component developed above to perform calculations.

7.2.1 Expression Evaluator Interface The ExpressionEvaluatorService interface contains one single operation: package com.ivan.components; /** * Interface defining the operations supplied by the ExpressionEvaluator * Service. * * @author Ivan A Krizsan */ public interface ExpressionEvaluatorService { /** * Evaluates the supplied RPN expression by performing the calculations * and returning the state of the stack after evaluation has been * completed. Allowed operators are +, - * and /. * * @param inExpression RPN expression to evaluate. * @return Result of evaluation, with each item on the stack separated * by space. */ String evaluateRPN(final String inExpression); }

89

7.2.2 Expression Evaluator Implementation The implementation class of the Expression Evaluator component. I've chosen to use constructor injection to establish the relation to the Calculator service that the Expression Evaluator depends on. package com.ivan.components.impl; import java.util.Stack; import import import import

org.oasisopen.sca.annotation.Constructor; org.oasisopen.sca.annotation.Reference; org.oasisopen.sca.annotation.Scope; org.oasisopen.sca.annotation.Service;

import com.ivan.components.CalculatorService; import com.ivan.components.ExpressionEvaluatorService; /** * Provides an implementation of the ExpressionEvaluator Service that * evaluates mathematical expressions on long integers. * * @author Ivan A Krizsan */ @Scope("COMPOSITE") @Service(ExpressionEvaluatorService.class) public class ExpressionEvaluatorServiceImpl implements ExpressionEvaluatorService { /* Constant(s): */ /* Instance variable(s): */ private CalculatorService mCalculatorService; private Stack mNumbersStack = new Stack(); /** * Creates an instance of the service that will use the supplied * calculator service to perform calculations when evaluating expressions. * * @param inCalculatorService Calculator service. */ @Constructor public ExpressionEvaluatorServiceImpl( @Reference(name="calculatorService") final CalculatorService inCalculatorService) { System.out.println("Creating ExpressionEvaluatorServiceImpl"); System.out.println(" Received Calculator Service = " + inCalculatorService); mCalculatorService = inCalculatorService; } /* (non-Javadoc) * @see com.ivan.components.ExpressionEvaluatorService#evaluateRPN(java.lang.String) */ @Override public String evaluateRPN(String inExpression) { String theParts[]; theParts = splitExpressionInParts(inExpression); for (int i = 0; i < theParts.length; i++) { System.out.println("The current part: " + theParts[i]); if (theParts[i].matches("[\\-]*[0-9]+")) { /* The part is a number. Put it on the stack. */ Long theNumber = Long.parseLong(theParts[i]); mNumbersStack.push(theNumber); } else if (theParts[i].matches("[\\+\\-\\*\\/]")) { /* The part is an operator. Perform operation. */ evaluateOperator(theParts[i]);

90

} else { /* Unknown token. */ throw new IllegalArgumentException( "Illegal token in expression: " + theParts[i]); } } String theResultString = resultStringFromStack(); return theResultString; } private String[] splitExpressionInParts(final String inExpression) { String[] theParts; theParts = inExpression.split(" "); for (int i = 0; i < theParts.length; i++) { theParts[i] = theParts[i].trim(); } return theParts; } private void evaluateOperator(final String inOperator) { char theOperator = inOperator.charAt(0); Long theNumber1 = mNumbersStack.pop(); Long theNumber2 = mNumbersStack.pop(); Long theResult; switch (theOperator) { case '+': theResult = mCalculatorService.add(theNumber1, theNumber2); System.out.printf("Added %d and %d = %d\n", theNumber1, theNumber2, theResult); break; case '-': theResult = mCalculatorService .subtract(theNumber2, theNumber1); System.out.printf("Subtracting %d from %d = %d\n", theNumber2, theNumber1, theResult); break; case '*': theResult = mCalculatorService.multiply(theNumber1, theNumber2); System.out.printf("Multiplying %d and %d = %d\n", theNumber1, theNumber2, theResult); break; case '/': theResult = mCalculatorService.divide(theNumber2, theNumber1); System.out.printf("Dividing %d by %d = %d\n", theNumber2, theNumber1, theResult); break; default: throw new IllegalArgumentException( "Unrecognized operand: " + inOperator); } mNumbersStack.push(theResult); } private String resultStringFromStack() { /* * Create the result string, which consists of the space-separated * values on the stack. */ String theResultString = ""; while (!mNumbersStack.isEmpty()) { theResultString += mNumbersStack.pop().toString(); if (!mNumbersStack.isEmpty())

91

{ theResultString += " "; } } return theResultString; } }

Note that: • Constructor-injection is used to establish the relationship between the Expression Evaluation component and the Calculator component it uses to perform calculations.

7.2.3 Expression Evaluator Component Composite The Expression Evaluator component composite specifies which class implements the functionality of the component as well as the interface of the component. In src/main/resources, create a file named “ExpressionEvaluator.composite” with the following contents:

Note that: • Since we are not going to expose the Expression Evaluator service in any way, we'll just retrieve the component from a Java client program, thus there is no service or binding specifications in the composite. • There is nothing in this composite that specifies which Calculator component is to be used by the Expression Evaluator service, that is, no element or such. The wiring connecting the two components has been refactored out to a composite file of its own, which is described in detail in the next section.

92

7.2.4 Expression Evaluator Wiring Instead of using a element in the Expression Evaluator's component declaration, we will use a separate composite file containing a wire between the Expression Evaluator component and the Calculator component. In src/main/resources, create a file named “Wiring.composite” which has the following contents:

The above composite file causes a reference to the Calculator component service to be injected into the calculatorService property of the ExpressionEvaluatorComponent. The element will be examined in more detail in the next section.

93

7.3 The Composite Element in Detail References: • Service Component Architecture Assembly Model Specification, version 1.1, section 5.4. The element is a child element of the element and can be used as an alternative to the element, when separation of concerns is desired – for instance, containing all the wiring of components in one single composite file. The structure of the element looks like this:

Structure of the element in a composite. This element is used to establish relations between components.

94

The attributes of the element are: Attribute Name

Description

source

Name of the component reference which is to be injected. The format is “ComponentName/ReferenceName”. The reference name can be left out if the component only has one single reference.

target

Name of component to be injected. The format is “ComponentName/ServiceName”. The service name can be omitted if the component only has one single service.

replace

Specifies whether existing wires going from the reference to be injected (specified by the source attribute) are to be replaced or not. If true, then existing references are replaced by this (new) reference. If false, then this (new) reference is appended to the set of existing reference. Default value: false.

I felt the names of the source and target attributes of the element are a little confusing. The following figure made things more clear to me:

A wire going from the Addition component to the Calculator component. This wire supplied the Calculator component with a reference to the Addition component and enables the former to use the latter.

Thus, think of the direction of the wire as going from the component to be injected to the reference which to inject (the component) into. SCA also supports automatic wiring of components. For more information, please consult the SCA Assembly specification.

95

7.4 Assembling the Composites References: • Service Component Architecture Assembly Model Specification, version 1.1, section 5.6. With all the parts in place, we can now create the composite in which we assemble them. Assembling different composites is done by including them in the assembly composite. In src/main/resources, create a file named “Assembly.composite” which contains the following component declaration:

96

7.5 The Composite Element in Detail References: Service Component Architecture Assembly Model Specification, version 1.1, section 5.6. When processing a composite in which there are one or more elements, the SCA runtime recursively includes composites using the following procedure: • • • • •





The contents of the included element is inlined, replacing the element which included the composite in question. The attributes targetNamespace, name, constrainingType and local of the included composite are removed. Namespace declarations from the included element are copied to each of the inlined composite's child elements. If a child element overrides a namespace, then this namespace is not copied. The autowire attribute from the included element is copied to each of the inlined composite's child elements, unless it is already declared on an element. The unions of the set of policy intents specified by the requires attribute of the included element and the policy intents specified on included , and are taken and set as the policy intents of respective included , and element. The unions of the set of policies specified by the policySet attribute of the included element and the policies specified on included , and are taken and set as the policies of respective included , and element. Any extension attributes follow the rules of the extension in question.

The element has one single attribute and no child elements: Attribute Name Description name

The qualified name of the composite in the SCA domain that is to be included.

7.6 Adding the Assembly Composite to the SCA Contribution Metadata Document Having included a number of composites into the Assembly composite, we only need to add the Assembly composite to the SCA contribution. • Modify the file “sca-contribution.xml” in the META-INF folder in src/main/resources to look like this:

97

7.7 Creating the Client Class The client class in this example is responsible for starting the SCA node containing the Expression Evaluator component and invoking it. package com.ivan.client; import org.apache.tuscany.sca.node.Node; import org.apache.tuscany.sca.node.NodeFactory; import com.ivan.components.ExpressionEvaluatorService; /** * Starts the SCA node and invokes the ExpressionEvaluator service. * * @author Ivan A Krizsan */ public class SCACompositeAsCompositeImplClient { public static void main(String[] args) { /* Create and start a new SCA node. */ Node theNode = NodeFactory.newInstance().createNode(); theNode.start(); /* Retrieve the ExpressionEvaluator service component. */ ExpressionEvaluatorService theEvalService = theNode.getService( ExpressionEvaluatorService.class, "ExpressionEvaluatorComponent"); System.out.println("Got evaluation service: " + theEvalService); String theResult = theEvalService.evaluateRPN("4 5 *"); System.out.println("Expression result: " + theResult); } }

7.8 Running the Expression Evaluator Client If we now run the Expression Evaluator client, the following console output should be generated. As usual, the output generated by Tuscany is in red and the output generated by our code is in black. ... INFO: Add endpoint - (@2026561073)Endpoint: URI = ExpressionEvaluatorComponent#servicebinding(ExpressionEvaluatorService/ExpressionEvaluat orService) Got evaluation service: [Proxy org.apache.tuscany.sca.core.invocation.impl.JDKInvocationHandler@4ed9f47] Creating ExpressionEvaluatorServiceImpl Received Calculator Service = [Proxy org.apache.tuscany.sca.core.invocation.impl.JDKInvocationHandler@616fdac] The current part: 4 The current part: 5 The current part: * Creating CalculatorServiceImpl Multiplying 5 and 4 = 20 Expression result: 20

This concludes the example showing how to assemble SCA composites.

98

8 Composites as Component Implementation SCA components may not only be implemented using Java or other programming languages, it is also possible for the implementation of a component to be a SCA composite. SCA calls this recursive composition. This construct causes the inner workings of the composite to be hidden from users/clients of the component which the composite acts as the implementation of. Users/clients are only able to access the following parts of the composite: • Services of the composite. Such services are component services that are promoted in the composite. • References of the composite. References to components external to the composite that the composite require. • Properties of the composite. Chapter 9 will show an example of injecting properties into composites. Using recursive composition, we can also, for instance, implement a new service that wraps an existing service, providing a specialized or adapted version of the service. In this chapter we will develop a component which implementation is a composite. Building on the Expression Evaluator developed in chapter 7, we will develop a Batch Calculator service that evaluates batches of expressions. The new service will use the Expression Evaluator service developed earlier and, for convenience's sake, will be developed in the same project. Thus we need not create a new Eclipse project.

8.1 Batch File The batch file contains calculations to be performed by the new service; one expression in RPN form per row. In the root of the project, create a file named “expression-file.txt” containing the following expressions: 4 1 3 4

5 2 4 5

* + + 6 * 3 /

The correct results of the above expressions are: 20, 3, -1 and 18.

99

8.2 Promoting the Expression Evaluator Service The Assembly composite developed earlier will serve as the implementation of a component used by the new Batch Calculator service. In order to be able to be used as a component implementation, a composite must promote the service of at least one component in the composite. In the file “Assembly.composite”, after all the elements, add a element promoting the service of the ExpressionEvaluatorComponent, as shown below: ... element must be located after all the elements in the composite. -->


The ExpressionEvaluatorService is defined by the annotation already present in the class that implements the service.

100

8.3 BatchCalculator Component The BatchCalculator component is responsible for evaluation a batch of RPN expressions located in a file and writing the result to another file. The BatchCalculator component depends on the Expression Evaluator service developed in chapter 7 to evaluate individual expressions.

8.3.1 BatchCalculator Service Interface The interface of the new Batch Calculator service is implemented as follows: package com.ivan.components; import java.io.IOException; /** * Specifies the properties of the Batch Calculator service that * performs batches of calculations read from a file and stores the * results in another file. * * @author Ivan A Krizsan */ public interface BatchCalculatorService { /** * Processes the calculations in a batchfile and write the results * to another file. * The input batch file is to contain integer RPN expressions, * with one expression per row. * If the output file exists, it will be overwritten. * * @param inBatchFile Path to input batchfile containing calculations. * @param inResultFile Path to file to which the results of the * calculations are to be written. * @throws IOException If error occurs accessing input or output * files. */ void processBatch(final String inBatchFile, final String inResultFile) throws IOException; }

101

8.3.2 BatchCalculator Implementation The BatchCalculator implementation reads expressions from a batch file and delegates the processing of each expression to an instance of the Expression Evaluator service: package com.ivan.components.impl; import import import import import

java.io.BufferedReader; java.io.File; java.io.FileReader; java.io.FileWriter; java.io.IOException;

import org.oasisopen.sca.annotation.Reference; import org.oasisopen.sca.annotation.Scope; import org.oasisopen.sca.annotation.Service; import com.ivan.components.BatchCalculatorService; import com.ivan.components.ExpressionEvaluatorService; /** * Provides an implementation of the Batch Calculator Service that * performs batches of calculations read from a file, writing the * results to another file. * * @author Ivan A Krizsan */ @Scope("COMPOSITE") @Service(BatchCalculatorService.class) public class BatchCalculatorServiceImpl implements BatchCalculatorService { /* Instance variable(s): */ @Reference(name = "expressionEvaluator", required = true) private ExpressionEvaluatorService mEvaluatorService; /* (non-Javadoc) * @see com.ivan.components.BatchCalculatorService#processBatch(java.lang.String, java.lang.String) */ @Override public void processBatch(final String inBatchFile, final String inResultFile) throws IOException { FileReader theBatchReader = new FileReader(inBatchFile); BufferedReader theLineReader = new BufferedReader(theBatchReader); File theResultFile = new File(inResultFile); FileWriter theResultWriter = null; try { theResultFile.delete(); theResultFile.createNewFile(); theResultWriter = new FileWriter(theResultFile); while (theLineReader.ready()) { /* Process one line from the batch file and write result. */ String theExpression = theLineReader.readLine(); String theExpressionResult = mEvaluatorService.evaluateRPN(theExpression); theResultWriter.write(theExpressionResult + "\n"); } } finally { /* Close the batch file reader. */ try { theLineReader.close(); } catch (final IOException theException) { /* Ignore exceptions. */ } /* Close the result file writer. */ if (theResultWriter != null)

102

{ try { theResultWriter.close(); } catch (final IOException theException) { /* Ignore exceptions. */ } } } } }

8.3.3 BatchCalculator Composite The BatchCalculator composite file, named “BatchCalculator.composite”and located with the other composite files of the project, contains two SCA components; the BatchCalculator component and a EvaluatorComponent. The Evaluator component uses the Assembly composite, in which we just promoted a service, as implementation. The BatchCalculator component, in turn, is injected with a reference to the EvaluatorComponent:

Note that: • The EvaluatorComponent specifies its implementation using the element. This implementation element will be examined in detail in the next section.

103

8.4 The Element in Detail References: • Service Component Architecture Assembly Model Specification, version 1.1, section 5.5. The element is, as we have seen, a child element of a element and used to specify that the component implementation is provided by a composite. The element has the following attributes: Attribute Name Description name

The qualified name of the component in the SCA domain that is to be used as implementation of the component.

requires

A list of policy intents.

policySets

A list of policy sets.

8.5 Adding the BatchCalculator Composite to the SCA Contribution Metadata Document In order for the BatchCalculator component to be included in the SCA domain, it must be added to the SCA contribution metadata document. Modify the file “sca-contribution.xml” in the META-INF folder in src/main/resources to look like this:

104

8.6 Creating the Client Class The BatchCalculator client class is similar to the client classes we have seen: package com.ivan.client; import java.io.IOException; import org.apache.tuscany.sca.node.Node; import org.apache.tuscany.sca.node.NodeFactory; import com.ivan.components.BatchCalculatorService; /** * Starts the SCA node and invokes the Batch Calculator service. * * @author Ivan A Krizsan */ public class SCABatchCalculatorClient { public static void main(String[] args) { /* Create and start a new SCA node. */ Node theNode = NodeFactory.newInstance().createNode(); theNode.start(); BatchCalculatorService theBatchService = theNode.getService( BatchCalculatorService.class, "BatchCalculatorComponent"); System.out.println("Got Batch Calculator service: " + theBatchService); try { theBatchService.processBatch("expression-file.txt", "result-file.txt"); System.out.println("Finished batch processing!"); } catch (final IOException theException) { System.out.println("An error occurred performing batch processing: " + theException); } } }

8.7 Running the BatchCalculator Client If we now run the BatchCalculator client, there will be some console output, but more important, there will be a new file named “result-file.txt” appearing in the root of the project, next to the batch file created earlier. The contents of the file should be the expected results of the expressions, namely: 20 3 -1 18

This concludes the example showing how to use composites as component implementations.

105

9 Injecting Properties and References Into Composites References: • Service Component Architecture Assembly Model Specification, version 1.1, section 5.3. In this chapter, we will use the skills learned in the previous two chapters, regarding assembling components and using composites as component implementation, as well as see how to inject properties and references into composites.

9.1 Presentation of the Example Program The example program to be developed in this chapter is a paragraph-printing component that can print paragraphs of text preceded by a header and followed by a footer. For example, printing a paragraph containing three rows of text with the tabulation 15 can look like this: **** HEADER **** paragraph row one paragraph row two paragraph row three **** FOOTER *****

The example program developed in this chapter has the following structure: •

There are three composites; the Printer, the ParagraphPrinter and the Client composite.



The Printer composite contains one component; the PrinterService component.



The Printer composite promotes the service of the PrinterService component.



The Printer composite contains one property, the value of which is injected into the PrinterService component.



The ParagraphPrinter composite contains one component; the ParagraphPrinterService component.



The ParagraphPrinter composite promotes the service of the ParagraphPrinterService component.



The ParagraphPrinter composite contains two properties, the values of which are injected into the ParagraphPrinterService component.



The Client composite contains two components which implementations are the above two composites.



The Client composite contains three properties, the value of which are injected in the (corresponding) properties of the above two composites.

106

The following figure shows a visual representation of the SCA components and composites of the example program:

Visual representation of the SCA components and composites in this chapter's example program.

Before looking at how this chapter's example project is implemented, we'll take a closer look at composite properties and references.

107

9.2 The Composite Element in Detail References: Service Component Architecture Assembly Model Specification, version 1.1, section 5.3. The composite element is used to define properties of a composite. Such properties can be injected into components of the composite, as we have seen earlier. Values can also be injected into composite properties when the composite is used as a component implementation. The structure of the composite element looks like this:

Structure of the composite element. This element is used to declare composite-global properties which can also be injected when the composite is used as a component implementation.

108

The composite element has the following attributes: Attribute Name

Description

name

Name of the property. Must be unique within the composite.

type

Qualified name of an XML schema type specifying the type of the property. Optional. If the type attribute is present, then the element attribute must not be present.

element

Qualified name of an XML schema global element specifying the type of the property. Optional. If the element attribute is present, then the type attribute must not be present.

many

Indicates whether the property contains one single value (false) or multiple values (true). A single-valued property cannot be made multivalued by setting this attribute to false, but a multi-valued property can be made singlevalued. Optional.

mustSupply

Indicates whether the value of the property has to be supplied by components using the composite. If true, then declaring a default value (see below) is superfluous. Default is false.

requires

A list of policy intents.

policySets

A list of policy sets.

A default value can be specified for a composite property as in this example: 15

A composite property named tabs of the simple type integer and the default value 15.

109

9.3 The Composite Element in Detail References: Service Component Architecture Assembly Model Specification, version 1.1, section 5.2. The composite element is used to promote a component reference to a composite reference and thereby saying that users of the composite need to supply a reference, in order to fulfill the dependency of the composite. In this chapter's example program, the reference of the ParagraphPrinterService component to a PrinterService is promoted, since this dependency cannot be fulfilled within the composite. Later, in the Client composite, when the ParagraphPrinter composite is used as the implementation of a component, a reference is injected which fulfills the dependency and enables the ParagraphPrinter to work correctly. The structure of the composite element looks like this:

Structure of the composite element. This element is used to promote component references that must be fulfilled when the composite is used as component implementation..

110

The composite element has the following attributes: Attribute Name

Description

name

Name of the reference. Must be unique within references of the composite. The name of the composite reference does not have to be the same as the name of the promoted component reference.

promote

Specifies one or more component reference(s) to be promoted. The value is to be a spaceseparated list of component references of the form “ComponentName/ReferenceName”. The name of the reference may be omitted if the component only has one single reference.

requires

A list of policy intents.

policySets

A list of policy sets.

multiplicity

Specifies the number of of services that can be referenced by the reference. Possible values are: 0..1, 1..1, 0..n, 1..n Default value is 1..1.

target

List of one or more target services to be injected into this reference. The number of targets depend on the multiplicity setting above.

wiredByImpl

If set to true, then the reference is to be established programmatically at runtime. Default value is false.

The composite element has the following child elements: Element Name Description interface

Interface describing the operations available in the referenced component. Specifying an interface for a reference is optional and if it is not supplied, the interface of the promoted reference will be used.

binding

Zero or more bindings to be used when communicating with the referenced component. If no binding specified, then the binding of the referenced component is used. If a binding is specified, it overrides the binding of the promoted reference.

callback

One optional element specifying the callback. If present, the element must contain at least one element.

111

9.4 Creating the Project We are now ready to start the more practical job of implementing the example program. As usual, start by setting up a new Eclipse project as described in chapter 2. I will name my project “SCACompositePropsRef”.

9.5 Printer Component The Printer component is responsible for printing single rows of text tabbed by a number of spaces. The Printer component has no dependencies.

9.5.1 Printer Service Interface The interface of the Printer service is trivial. containing one single operation. package com.ivan.components; /** * Interface specifying the functionality made available by the * Print service. * The service prints tabbed lines of output. * * @author Ivan A Krizsan */ public interface PrinterService { /** * Prints the supplied line, tabbed according to the setting of * the service. */ void printLine(final String inLine); }

112

9.5.2 Printer Service Implementation The implementation of the Printer service is also quite trivial. Everything concerning SCA and Apache Tuscany in this class should be familiar by now. package com.ivan.components.impl; import import import import

org.oasisopen.sca.annotation.Property; org.oasisopen.sca.annotation.Scope; org.oasisopen.sca.annotation.Service; com.ivan.components.PrinterService;

/** * Provides the implementation of the Printer service. * * @author Ivan A Krizsan * @see PrinterService */ @Scope("COMPOSITE") @Service(PrinterService.class) public class PrinterServiceImpl implements PrinterService { /* Constant(s): */ private final static char SPACE_CHAR = ' '; /* Instance variable(s): */ private int mTabs; /** * Default constructor. * Logs the creation of instances of this class. */ public PrinterServiceImpl() { System.out.println("Create PrinterServiceImpl"); } /** * Sets the number of spaces that is to preceed each line being * printed. * * @param inTabs Number of spaces to tab output. */ @Property(name="tabs") public void setTabs(final int inTabs) { System.out.println("PrinterServiceImpl.setTabs: " + inTabs); mTabs = inTabs; } /* (non-Javadoc) * @see com.ivan.components.PrinterService#printLine(java.lang.String) */ public void printLine(final String inLine) { StringBuffer theTabString = new StringBuffer(); for (int i = 0; i < mTabs; i++) { theTabString.append(SPACE_CHAR); } System.out.println(theTabString + inLine); } }

Note that: • The default constructor has been implemented in order for a log message to be printed when an instance of the Printer service is created. • The setTabs setter method has been implemented, and annotated with the @Property annotation, in order for a log message to be printed when the tabs property is set. 113

9.5.3 Printer Composite In src/main/resources, create a file named “Printer.composite” with the following contents: 4

Note that: • The first child element of the element is a element. This is a composite element described earlier. This property has the simple XML schema type integer with the default value 4. It does not have to be set by users of the composite. • The second child element of the element is a element. This composite promotes the services of the PrinterServiceComponent declared below. • The PrinterServiceComponent component has one single property, which is injected with the value of the composite property with the same name.

114

9.6 ParagraphPrinter Component The ParagraphPrinter component is responsible for printing paragraphs of tabbed text containing any number of rows. Each paragraph may be preceded by a header and followed by a footer. The ParagraphPrinter component relies on the Printer component developed above to output lines of text.

9.6.1 ParagraphPrinter Service Interface As the Printer service interface, the ParagraphPrinter service interface just contains one single operation: package com.ivan.components; /** * Interface specifying the functionality made available by the * ParagraphPrinter service. * The service prints tabbed lines of output with a header and a footer. * * @author Ivan A Krizsan */ public interface ParagraphPrinterService { /* Constant(s): */ /** * Prints the paragraph, consisting of the supplied lines, * tabbed as configured, with the configured header and footer. * * @param inLines Text lines of the paragraph to be printed. */ void printParagraph(final String[] inLines); }

9.6.2 ParagraphPrinter Service Implementation The implementation of the ParagraphPrinter service is also quite trivial. Everything concerning SCA and Apache Tuscany in this class should be familiar by now. package com.ivan.components.impl; import import import import

org.oasisopen.sca.annotation.Property; org.oasisopen.sca.annotation.Reference; org.oasisopen.sca.annotation.Scope; org.oasisopen.sca.annotation.Service;

import com.ivan.components.ParagraphPrinterService; import com.ivan.components.PrinterService; /** * Provides the implementation of the ParagrahPrinter service. * * @author Ivan A Krizsan * @see ParagraphPrinterService */ @Scope("COMPOSITE") @Service(ParagraphPrinterService.class) public class ParagraphPrinterServiceImpl implements ParagraphPrinterService { /* Instance variable(s): */ private PrinterService mPrinterService; private String mHeader; private String mFooter; /** * Default constructor. * Logs the creation of instances of this class. */ public ParagraphPrinterServiceImpl() {

115

System.out.println("Created ParagraphPrinterServiceImpl"); } /** * Sets the Printer service that the ParagraphPrinter uses to * print output. * * @param inPrinterService Printer service. */ @Reference(name="printerService", required=true) public void setPrinterService(final PrinterService inPrinterService) { System.out.println("ParagraphPrinterServiceImpl.setPrinterService: " + inPrinterService); mPrinterService = inPrinterService; } /** * Sets the footer to be printed after a paragraph. * * @param inFooter Paragraph footer. */ @Property(name="footer", required=false) public void setFooter(final String inFooter) { System.out.println("ParagraphPrinterServiceImpl.setFooter: " + inFooter); mFooter = inFooter; } /** * Sets the header to be printed before a paragraph. * * @param inHeader Paragraph header. */ @Property(name="header", required=false) public void setHeader(final String inHeader) { System.out.println("ParagraphPrinterServiceImpl.setHeader: " + inHeader); mHeader = inHeader; } /* (non-Javadoc) * @see com.ivan.components.ParagraphPrinter#printParagraph(java.lang.String[]) */ @Override public void printParagraph(final String[] inLines) { if (mHeader != null && mHeader.length() > 0) { mPrinterService.printLine(mHeader); } for (int i = 0; i < inLines.length; i++) { mPrinterService.printLine(inLines[i]); } if (mFooter != null && mFooter.length() > 0) { mPrinterService.printLine(mFooter); } } }

Note that: • Default constructor and setter methods have been created in order to be able to log instance creation and property setting.

116

9.6.3 ParagraphPrinter Composite In src/main/resources, create a file named “ParagraphPrinter.composite” with the following contents:

Note that: • There are two composite properties, named header and footer, which are both of the XML schema simple type string. Both these properties are optional, but have no default values. • The reference to a Printer service in the ParagraphPrinter service is promoted. Reference promotion has been described earlier. • The ParagraphPrinter service is promoted to be accessible to users of the composite. • The ParagraphPrinterServiceComponent component has two properties that are injected with the values of the composite properties with the same names. 117

9.7 Assembly Composite The Assembly composite assembles and injects settings into the Printer and ParagraphPrinter composites. In src/main/resources, create a file named “Assembly.composite” with the following contents: 15 **** HEADER **** **** FOOTER *****

Note that: • The Printer and ParagraphPrinter composites are included into the Assembly composite. First I tried to add these composites to the SCA contribution file, but the PrinterCompositeComponent weren't instantiated. • Three composite properties are specified with default values. Preferably, configuration data like this should be located in an external file in order to be able to configure the program without having to recompile it. To keep this example as simple as possible, I chose to use composite properties. • The implementation of each of the components is supplied by a composite. • Properties are injected into the composites. 118

If the property has a default value, as in the case with the tabs property of the Printer composite, then this value is overridden. A reference is injected into the ParagraphPrinter composite. A reference was promoted in the ParagraphPrinter composite, so in order for this composite to function correctly, the required reference must be injected.



9.8 Adding the Assembly Composite to the SCA Contribution Metadata Document Since the Printer and ParagraphPrinter composites were included into the Assembly composite, it suffices to add the Assembly composite to the SCA contribution file. Modify the file “sca-contribution.xml” in the META-INF folder in src/main/resources to look like this:

9.9 Creating the Client Class The client class is similar to the client classes we have seen: package com.ivan.client; import org.apache.tuscany.sca.node.Node; import org.apache.tuscany.sca.node.NodeFactory; import com.ivan.components.ParagraphPrinterService; /** * Starts the SCA node and invokes the PrinterClient service. * * @author Ivan A Krizsan */ public class SCACompositePropsRefsClient { /* Constant(s): */ private final static String[] PARAGRAPH = { "row one", "row two", "row three" }; public static void main(String[] args) { /* Create and start a new SCA node. */ Node theNode = NodeFactory.newInstance().createNode(); theNode.start(); ParagraphPrinterService theClient = theNode.getService( ParagraphPrinterService.class, "ClientParagraphPrinter"); theClient.printParagraph(PARAGRAPH); } }

119

9.10 Running the ParagraphPrinter Client If we now run the client, the following console output should be generated. As usual, the output generated by Tuscany is in red and the output generated by our code is in black: ... INFO: Add endpoint - (@970926436)Endpoint: URI = ParagraphPrinterServiceComponent#servicebinding(ParagraphPrinterService/ParagraphPrinterS ervice) Created ParagraphPrinterServiceImpl ParagraphPrinterServiceImpl.setHeader: **** HEADER **** ParagraphPrinterServiceImpl.setFooter: **** FOOTER ***** ParagraphPrinterServiceImpl.setPrinterService: [Proxy org.apache.tuscany.sca.core.invocation.impl.JDKInvocationHandler@2017b2b2] Create PrinterServiceImpl PrinterServiceImpl.setTabs: 15 **** HEADER **** row one row two row three **** FOOTER *****

We see that: • An instance of the class ParagraphPrinterServiceImpl was created and injected with a header, a footer and a printer service. • An instance of the class PrinterServiceImpl was created and injected with the tabs value 15. • The paragraph, consisting of three rows of text, was printed as expected. This concludes the example showing injection of properties and references into SCA composites.

120

10 Using an Existing Web Service In this chapter we will see an example of how an existing SOAP web service can be invoked from a SCA component. Additionally, we will examine SCA web service binding in detail.

10.1 An Existing Web Service First we'll develop a regular SOAP web service that we later are to use from a SCA component. The web service is a code-first JAX-WS web service developed using the following steps: •

Create the project. Note that no deployment descriptors are needed.



Implement the endpoint implementation class.



Implement the custom exception class.



Deploy the web service.

I will develop the web service in a Dynamic Web Project named “MyExistingWebService” in Eclipse and deply to GlassFish v3, but you are free to choose any tools you prefer. Just remember to adjust the web service endpoint URL accordingly.

10.1.1 Endpoint Implementation Class The endpoint implementation class of the existing web service is implemented as follows: package com.ivan.webservice; import java.util.Date; import javax.jws.WebParam; import javax.jws.WebService; /** * This class implements a web service endpoint that greets persons. * * @author Ivan A Krizsan */ @WebService public class HelloService { /** * Creates a greeting message that greets the person with the * supplied name telling him/her what time it is. * * @param inName Name of the person to greet. * @throws HelloServiceException If no name was supplied. */ public String sayHello(@WebParam(name = "inName") final String inName) throws HelloServiceException { if (inName == null || inName.length() < 1) { System.out.println("Throwing a HelloServiceException"); throw new HelloServiceException("No name supplied"); } System.out.println("Returning a greeting to: " + inName); return "Hello " + inName + ", the time is now " + (new Date()); } }

121

10.1.2 Custom Exception Class The web service throws an instance of a custom exception class if no name is supplied when invoking the sayHello operation. We do this in order to be able to see how Apache Tuscany handles faults from an existing web service. The custom exception class is implemented as below: package com.ivan.webservice; /** * An exception that indicates that no name was provided to the * HelloService. * * @author Ivan A Krizsan */ public class HelloServiceException extends Exception { /* Constant(s): */ private static final long serialVersionUID = -7720250084293363297L; /** * Creates an exception instance with the supplied detail message. * * @param inMessage Detail message. */ public HelloServiceException(final String inMessage) { super(inMessage); } }

122

10.1.3 Testing the Existing Web Service After having deployed the web service, make sure that it works as expected using soapUI: • Create a new soapUI project with the URL of the WSDL of the web service. The URL for the WSDL of my web service is: http://localhost:8080/MyExistingWebService/HelloServiceService?wsdl • Issue a request with a name specified in the element: Ivan



The response should be similar to the following:

Hello Ivan, the time is now Mon Jan 18 20:01:00 CST 2010



Issue a request with an empty element:





The response to the request without a name should be a fault:

S:Server No name supplied No name supplied

If everything works as expected, then we are now ready to develop the SCA component that will invoke the web service.

123

10.2 Creating the Project As usual, start by setting up a new Eclipse project as described in chapter 2. I will name my project “SCAUseExistingWebService”.

10.3 Generating the Web Service Client Artifacts To be able to use the existing web service from a SCA component implemented in Java, we need to generate client artifacts. The artifacts are generated from the WSDL using the following Ant-script, located in the project root, in a file named “build.xml”:

Depending on your system and the URL of the existing web service developed in the previous section, you may have to modify the following two properties in the above Ant-script: •

wsimport-cmd Specifies the location of the wsimport command that generates the artifacts.



wsdl-location The URL of the existing web service's WSDL.

When finished configuring, run the default target of the script by right-clicking on the build.xml file in Eclipse and selecting Run As -> Ant Build. Refreshing the project, a number of Java files should appear in the com.ivan.webservice package.

124

10.4 InvokeService Component The InvokeService component forwards calls to the existing web service, catching any checked exceptions and generating log messages.

10.4.1 InvokeService Service Interface The interface of the service invoking the existing web service is implemented as follows: package com.ivan.components; /** * Defines properties of a service that invokes an existing web service. * * @author Ivan A Krizsan */ public interface InvokeService { String invokeWebService(final String inName); }

10.4.2 InvokeService Service Implementation The InvokeService service invokes the HelloService service which is referenced by the instance variable mHelloService. Any checked exceptions thrown when invoking the HelloService are caught and logged. The InvokeService service is implemented as follows: package com.ivan.components.impl; import import import import

org.oasisopen.sca.annotation.Reference; org.oasisopen.sca.annotation.Service; com.ivan.components.InvokeService; com.ivan.webservice.HelloService;

/** * Implements the service that invokes an existing web service. * * @author Ivan A Krizsan */ @Service(InvokeService.class) public class InvokeServiceImpl implements InvokeService { /* Instance variable(s): */ @Reference(name="helloService") private HelloService mHelloService; /* (non-Javadoc) * @see com.ivan.components.InvokeService#invokeWebService(java.lang.String) */ @Override public String invokeWebService(String inName) { String theResult = null; try { System.out.println("Invoking the web service. Name = \"" + inName + "\""); theResult = mHelloService.sayHello(inName); } catch (final Exception theException) { System.out.println("An error occurred invoking the web service: " + theException); theException.printStackTrace(); theResult = "[an error occurred]"; } return theResult; } }

125

Note that: • The type of the reference to the existing web service used in the implementation of the InvokeService service is the generated HelloService interface.

126

10.4.3 InvokeService Composite The InvokeService composite defines the SCA component that uses the existing web service and injects a reference to the existing web service into the component. In src/main/resources, create a file named “InvokeService.composite” with the following contents:

Note that: • The element in the element contains a element with the name attribute uri containing the URL of the WSDL service port. Looking at the WSDL of the existing web service, we see: ... ...

An alternative way to specify the address is by using a WS-Addressing element in the element. The following component element is functinally equivalent to the one above: ... http://localhost:8080/MyExistingWebService/HelloServiceService ...

We will look more closely at the element later in this chapter.

127

10.5 Adding the InvokeService Composite to the SCA Contribution Metadata Document The SCA contribution metadata document provides no surprises - it includes the single composite of the project. Modify the file “sca-contribution.xml” in the META-INF folder in src/main/resources to look like this:

10.6 Creating the Client Class The client class is similar to the client classes we have seen: package com.ivan.client; import org.apache.tuscany.sca.node.Node; import org.apache.tuscany.sca.node.NodeFactory; import com.ivan.components.InvokeService; /** * Main class of the SCA example that uses an existing web service. * * @author Ivan A Krizsan */ public class SCAUseExistingWSClient { public static void main(String[] args) { System.out.println("Starting the SCA node..."); Node theNode = NodeFactory.newInstance().createNode(); theNode.start(); System.out.println("SCA node started..."); InvokeService theInvokeService = theNode.getService(InvokeService.class, "InvokeServiceComponent"); String theResult = theInvokeService.invokeWebService("Ivan"); System.out.println("The result: " + theResult); theNode.stop(); System.out.println("Stopped the SCA node."); } }

128

10.7 Running the InvokeService Client To see how the InvokeService service behaves when invoked with legal respective illegal input, we will first run the InvokeService client first supplying a name and then supplying an empty string as parameter.

10.7.1 Running with a Legal Parameter If we run the client in its original form, the following console output should be generated. As usual, the output generated by Tuscany is in red and the output generated by our code is in black: Starting the SCA node... Jan 18, 2010 8:11:17 PM org.apache.tuscany.sca.node.impl.NodeImpl start INFO: Starting node: http://tuscany.apache.org/sca/1.1/nodes/default0 domain: tuscany.apache.org ... INFO: Add endpoint - (@90379635)Endpoint: URI = InvokeServiceComponent#servicebinding(InvokeService/InvokeService) SCA node started... Invoking the web service. Name = "Ivan" The result: Hello Ivan, the time is now Mon Jan 18 20:11:21 CST 2010 Jan 18, 2010 8:11:21 PM org.apache.tuscany.sca.node.impl.NodeImpl stop INFO: Stopping node: http://tuscany.apache.org/sca/1.1/nodes/default0 Jan 18, 2010 8:11:21 PM org.apache.tuscany.sca.core.assembly.impl.EndpointRegistryImpl endpointRemoved INFO: Remove endpoint - (@90379635)Endpoint: URI = InvokeServiceComponent#servicebinding(InvokeService/InvokeService) Stopped the SCA node.

Note that: • Under normal circumstances, the InvokeService service behaves as expected and it successfully invokes the existing web service.

10.7.2 Running with an Illegal Parameter If we, in the SCAUseExistingWSClient class' main method, modify the invocation of the InvokeService to take an empty string as parameter: ... String theResult = theInvokeService.invokeWebService(""); ...

If we now run the client, the existing web service should now generate a fault when receiving the empty string parameter, resulting in an exception: Starting the SCA node... Jan 18, 2010 8:27:06 PM org.apache.tuscany.sca.node.impl.NodeImpl start INFO: Starting node: http://tuscany.apache.org/sca/1.1/nodes/default0 domain: tuscany.apache.org ... Jan 18, 2010 8:27:09 PM org.apache.tuscany.sca.core.assembly.impl.EndpointRegistryImpl addEndpoint INFO: Add endpoint - (@792382546)Endpoint: URI = InvokeServiceComponent#servicebinding(InvokeService/InvokeService) SCA node started... Invoking the web service. Name = "" An error occurred invoking the web service: com.ivan.webservice.HelloServiceException_Exception: No name supplied The result: [an error occurred] com.ivan.webservice.HelloServiceException_Exception: No name supplied ... at com.ivan.client.SCAUseExistingWSClient.main(SCAUseExistingWSClient.java:32) Caused by: org.apache.axis2.AxisFault: No name supplied at org.apache.axis2.util.Utils.getInboundFaultFromMessageContext(Utils.java:512) ... Jan 18, 2010 8:27:10 PM org.apache.tuscany.sca.node.impl.NodeImpl stop INFO: Stopping node: http://tuscany.apache.org/sca/1.1/nodes/default0

129

Jan 18, 2010 8:27:10 PM org.apache.tuscany.sca.core.assembly.impl.EndpointRegistryImpl endpointRemoved INFO: Remove endpoint - (@792382546)Endpoint: URI = InvokeServiceComponent#servicebinding(InvokeService/InvokeService) Stopped the SCA node.

Note that: • The exception thrown as a result of invoking the existing web service with an empty string parameter is of the type HelloServiceException_Exception – one of the classes generated from the WSDL. This is the same type of exception as is declared in the HelloService interface on the method sayHello. • The cause of the HelloServiceException_Exception is an AxisFault exception. We can conclude that Apache Tuscany does translate service-specific faults generated by an external service to a corresponding checked exception, when the necessary artifacts has been generated by wsimport.

130

10.8 The Element In Detail References: •

Service Component Architecture Assembly Model Specification, version 1.1, chapter 8.



Service Component Architecture Web Service Binding Specification, version 1.1.



Service Component Architecture SCA-J Common Annotations and APIs Specification, version 1.1, chapter 11.

The element can, as we have seen earlier in this document, occur either as the child element of either a element or a element. In the first case, the service will be exposed as a web service. In the second case, the reference will be invoked as a web service. The structure of the composite element looks like this:

Structure of the element. This element is used to specify that a reference or service is to be accessed as a web service.

131

The element has the following attributes: Attribute Name

Description

name

Name of the binding instance. If a service or reference has multiple bindings, each must have a unique name. Optional if the service or reference only has one single binding.

uri

If the element is the child of a element, then the value of this element must be absolute. For instance, an absolute URL.

wsdlElement

Specifies the element in a WSDL 1.1 document to which to bind the reference or service. See below for details.

wsdli:wsdlLocation

Used to specify the location(s) of the WSDL document(s) associated with the namespace(s) used in the wsdlElement attribute. Note that the wsdlLocation attribute belongs to the http://www.w3.org/ns/wsdl-instance namespace, thus the wsdli namespace prefix. See below for details.

requires

A list of policy intents.

policySets

A list of policy sets.

132

The element can have the following child elements: Element Name

Description

wireFormat

Specifies the format of the data sent “on the wire” when using the binding.

operationSelector

Used to map a message to an operation. Commonly not used with the web service binding.

wsa:EndpointReference

Enables the ability to specify the address of the service or reference endpoint using WSAddressing. See below for details.

Note that: • A binding of a element must not specify an endpoint address using the uri attribute or the element. • If the WSDL contains only one single element, then using the wsdlElement attribute of the element is not required. • According to the SCA 1.1 web service binding specifications, only one of the following things can occur in a element at the same time: - The uri attribute. - The wsdlElement attribute referring to a WSDL port/endpoint or a WSDL service. - The child element.

133

10.8.1 The wsdlElement Attribute The optional wsdlElement attribute is used to specify an element in an existing WSDL document that should be used for the web service binding. The value of this attribute is to be specified using one of the four following forms: Form Example Comments #wsdl.service()

http://ivan.com/service#wsdl.service( MyServiceName)

May not be used when the element is for a SCA service. The element in the WSDL must contain at least one .

#wsdl.port(/)

http://www.ivan.com/service#wsdl.po WSDL 1.1 only. rt(MyServiceName/SomePortName) The WSDL port must be compatible with the SCA service or SCA reference interface.

#wsdl.endpoint(/)

http://www.ivan.com/service#wsdl.en WSDL 2.0 only. dpoint(MyServiceName/SomeEndpoi Tuscany-specific, not defined by the ntName) SCA 1.1 standard. The WSDL endpoint must be compatible with the SCA service or SCA reference interface.

of the binding URI>#wsdl.binding() ding(MyBindingName) must be compatible with the SCA service or SCA reference interface. The endpoint address of a SCA reference for which the element is specified must be specified either using the uri attribute or using a child element.

10.8.2 The wsdlLocation Attribute References: • http://www.w3.org/TR/2007/REC-wsdl20-20070626/#wsdlLocation-aii The wsdlLocation attribute enables specifying the absolute or relative location(s) of WSDL document(s) corresponding to namespace(s). The following snippet shows an example of how a value consisting of one namespace and one WSDL location pair is specified on a element:

134

10.8.3 The Child Element The endpointReference attribute allows specifying the endpoint of the service or reference using a WS-Addressing reference. The following snippet shows an example of how it is used in a component reference: ... http://localhost:8080/MyExistingWebService/HelloServiceService ...

135

11 Using Callbacks References: •

Service Component Architecture Assembly Model Specification, version 1.1, section 7.2, 7.3.



Service Component Architecture SCA-J Common Annotations and APIs Specification, version 1.1, chapter 7.

A service that returns some data can be made asynchronous by using callbacks. The service is invoked by the client, which can then continue execution without awaiting the result. Later, when a result is available, the service calls the client and delivers the data. A callback can be invoked multiple times, for instance if result data arrives in portions spread over a longer period of time. In this chapter we will look at examples of an asynchronous service that uses a callback to deliver computation results. The service and its client will run within the same SCA node and use the default SCA binding.

11.1 Creating the Project Start by setting up a new Eclipse project as described in chapter 2. I will name my project “SCACallbacks”.

11.2 ChecksumService Component The ChecksumService component provides an asynchronous service that calculates checksums of individual strings and reports the result using a callback. The service uses two interfaces; a regular service interface, as we have seen in earlier examples, and a callback interface that must be implemented by clients of the ChecksumService.

136

11.2.1 ChecksumService Service Interface The interface of the ChecksumService is implemented as follows: package com.ivan.components; import org.oasisopen.sca.annotation.Callback; import org.oasisopen.sca.annotation.OneWay; import org.oasisopen.sca.annotation.Remotable; /** * Defines the services made available by the Checksum service. * The service is asynchronous and will deliver results using a * callback mechanism. * * @author Ivan A Krizsan */ @Remotable @Callback(ChecksumServiceCallback.class) public interface ChecksumService { /** * Calculates the checksum of the supplied string. * In order for this method to be non-blocking, it must be annotated * with the @OneWay annotation. * * @param inString String to calculate checksum on. * @param inResultIdentifier Result correlation identifier. */ @OneWay void checksumString(final String inString, final int inResultIdentifier); }

Note that: •

The service interface is annotated using the @Remotable annotation. A bug in Apache Tuscany prevents the use of local service interfaces when callbacks are used and thus we are forced to use the @Remotable annotation.



The service interface is annotated with the @Callback annotation. This annotation specifies the callback interface that clients must implement. More about the @Callback annotation in a subsequent section.



The method checksumString is annotated with the @OneWay annotation. The @OneWay annotation tells the SCA runtime that the service operation is to be invoked without waiting for the operation to execute. Further details about the @OneWay annotation below.



The method checksumString takes a parameter named inResultIdentifier. The value of this parameter will later, as we will see, be returned to the client when calling back to report the checksum of the string inString. This construct is used in order for the client to know which string the reported result corresponds to, since results may be reported in arbitrary order.

137

11.2.2 ChecksumService Callback Interface The ChecksumService's callback interface is implemented as follows: package com.ivan.components; import org.oasisopen.sca.annotation.Remotable; /** * Defines the callback interface used by the asynchronous Checksum * service to deliver results. * * @author Ivan A Krizsan */ @Remotable public interface ChecksumServiceCallback { /** * Delivers the result of calculating the checksum a string. * * @param inResultIdentifier Result correlation identifier. * @param inStringChecksum String checksum. */ void deliverChecksumStringResult(final int inResultIdentifier, final long inStringChecksum); }

Note that: •

Again, the interface is annotated using the @Remotable annotation due to a bug in Tuscany.



For details on callback interfaces, see references in the sections on the @Callback annotation and the @OneWay annotation below.



A result correlation parameter, the inResultIdentifier parameter, also appears in the deliverChecksumStringResult method for reasons discussed when implementing the service interface in the previous section.

138

11.2.3 ChecksumService Implementation The ChecksumService calculates checksums of single strings and delivers the checksums using a callback. Additionally, a lot of log messages are generated, in order for us to be able to follow the activities in the service. One single instance handles all request, that is the service's scope is COMPOSITE. package com.ivan.components.impl; import import import import import

org.oasisopen.sca.annotation.Callback; org.oasisopen.sca.annotation.Scope; org.oasisopen.sca.annotation.Service; com.ivan.components.ChecksumService; com.ivan.components.ChecksumServiceCallback;

/** * Provides implmentation of the asynchronous Checksum service. * * @author Ivan A Krizsan */ @Service(ChecksumService.class) @Scope("COMPOSITE") public class ChecksumServiceImpl implements ChecksumService { /* Instance variable(s): */ private ChecksumServiceCallback mResultCallback; /** * Sets the callback used to deliver results of service operations. * * @param inCallback Result delivery callback. */ @Callback public void setResultCallback(final ChecksumServiceCallback inResultCallback) { System.out.println("ChecksumServiceImpl.setResultCallback: " + inResultCallback); mResultCallback = inResultCallback; } /* (non-Javadoc) * @see com.ivan.components.ChecksumService#checksumString(java.lang.String, int) */ @Override public void checksumString(final String inString, final int inResultIdentifier) { long theChecksum = 0; System.out.println("ChecksumServiceImpl.checksumString: " + inString + ", thread: " + Thread.currentThread() + " - processing..."); /* Simulate processing that requires some time. */ try { long theDelay = (long)(Math.random() * 500.0) + 1500L; Thread.sleep(theDelay); } catch (final InterruptedException theException) { // Ignore exceptions. } theChecksum = performChecksumCalculation(inString, theChecksum); System.out.println("ChecksumServiceImpl.checksumString: " + inString + ", thread: " + Thread.currentThread() + " - calculated checksum: " + theChecksum); /* Deliver result using callback. */ mResultCallback.deliverChecksumStringResult(inResultIdentifier, theChecksum); System.out.println("ChecksumServiceImpl.checksumString: " + inString

139

+ ", thread: " + Thread.currentThread() + " - finished delivering result."); } private long performChecksumCalculation(final String inString, long theChecksum) { for (int i = 0; i < inString.length(); i++) { theChecksum += inString.charAt(i); } return theChecksum; } }

Note that: •

There is an instance field with the type of the ChecksumService's callback interface. References to callback proxies will be stored in this instance field by the setter method described below. This reference is later used in the checksumString method to deliver results to clients.



There is a setter method taking a parameter of the type of the ChecksumService's callback interface. The method is annotated with the @Callback annotation. Using this method, references to callback proxies will be injected into the service implementation class. More details about the @Callback annotation in a subsequent section.

140

11.2.4 ChecksumService Composite The ChecksumService composite defines the SCA component that calculates checksums of single strings. In src/main/resources, create a file named “ChecksumService.composite” with the following contents:

Note that: • The element in the ChecksumServiceComponent is not required. However, if we want to specify the binding to be used, then this can be done in the element. • If the element is used inside the element, then both the interface and the callbackInterface attributes must be specified for services using callbacks. Alternatively, the element may be omitted.

141

11.3 The @Callback Annotation References: •

Service Component Architecture SCA-J Common Annotations and APIs Specification, version 1.1, chapter 7 and section 11.1.



Service Component Architecture POJO Component Implementation Specification, version 1.1, chapter 7 and section 2.5.

The @Callback annotation is used on a service interface to specify the interface clients of the service must implement in order to receive callbacks. It is also used to annotate instance fields or instance methods of the service implementation class: Annotated Element

Purpose

Service interface.

Specify the callback interface clients of the service must implement in order to receive callbacks. Takes the Java class objects of the callback interface as parameter.

Instance field of service implementation class.

Request that a callback proxy is injected into the instance field.

Instance method (typically a setter method) of service implementation class with a parameter that has the callback interface type.

Request that a callback proxy is injected by invoking the instance method.

There is no limit on the number of instance fields or instance methods in a service implementation class that can be annotated with the @Callback annotation. This can be useful if one service implementation class implements multiple services that are using different callbacks.

11.3.1 Callback Service References When annotating an instance field or a setter method with the @Callback annotation, the type of the instance field or the method parameter can be a ServiceReference, like in this example: ... @Callback private ServiceReference mCallbackServiceRef; ...

Using a service reference enables us to store the reference and retrieve it at some later point in time to invoke the callback or service.

142

11.4 The @OneWay Annotation References: •

Service Component Architecture SCA-J Common Annotations and APIs Specification, version 1.1, chapter 7.



Service Component Architecture POJO Component Implementation Specification, version 1.1, section 2.4.

To enable asynchronous invocation of a method in a Java class implementing a component, the method can be annotated with the @OneWay annotation. Note that the method must not return any data (void return type) and must not declare any exceptions. Additionally, the SCA specification mandates support for the JAX-WS client API that allows asynchronous invocation of synchronous web services – see section 11.1 of the Service Component Architecture SCA-J Common Annotations and APIs Specification for detailed information. The @OneWay annotation is not used in this case.

11.5 BulkChecksum Component The BulkChecksum component calculates checksums of multiple strings synchronously. The task of calculating the checksum for an individual string is delegated to the ChecksumService component developed above.

11.5.1 BulkChecksum Service Interface The interface of the BulkChecksum service is implemented as follows: package com.ivan.components; /** * Defines the services made available by the BulkChecksum service * which, synchronously, calculates individual checksums on multiple items. * * @author Ivan A Krizsan */ public interface BulkChecksumService { /** * Calculates the individual checksum of each of the supplied * strings. * * @param inStrings Strings to checksum. * @return Checksums of strings. */ long[] checksumStrings(final String[] inStrings); }

Note that: •

The BulkChecksum service interface does not need to be remotable, since we are going to access it using a local client.

143

11.5.2 BulkChecksum Implementation The BulkChecksum service receives an array of strings, for which it is to calculate checksums. Each string, along with a correlation identifier, is sent to the ChecksumService. When the ChecksumService has finished calculating the checksum for a string, it uses the callback reference injected into the ChecksumService to invoke the callback method and deliver the correlation identifier and the calculated checksum to the client. The BulkChecksum service must keep track of when the checksums for all strings have been calculated. To do this, it uses a counter and a semaphore. package com.ivan.components.impl; import java.util.concurrent.Semaphore; import org.oasisopen.sca.annotation.Reference; import org.oasisopen.sca.annotation.Scope; import org.oasisopen.sca.annotation.Service; import com.ivan.components.BulkChecksumService; import com.ivan.components.ChecksumService; import com.ivan.components.ChecksumServiceCallback; /** * Provides implementation of the BulkChecksum service. * Note that only one single instance of this service exists in a * SCA node and that this service must not be called by multiple * threads at the same time. * * @author Ivan A Krizsan */ @Service(BulkChecksumService.class) @Scope("COMPOSITE") public class BulkChecksumServiceImpl implements BulkChecksumService, ChecksumServiceCallback { /* Instance variable(s): */ @Reference(name = "checksumService", required = true) private ChecksumService mChecksumService; /** Holds results received from the checksum service. */ private long[] mCalculatedChecksums; /** Expected number of checksums to receive from checksum service. */ private int mExpectedResultCount; /** Semaphore indicating all checksums calculated. */ private Semaphore mCalcFinishedSemaphore = new Semaphore(1); /** * Default constructor. * Just log creation of instances of this class. */ public BulkChecksumServiceImpl() { System.out.println("Created instance of BulkChecksumServiceImpl: " + this); } /* (non-Javadoc) * @see com.ivan.components.BulkChecksumService#checksumStrings(java.lang.String[]) */ @Override public long[] checksumStrings(String[] inStrings) { mCalculatedChecksums = new long[inStrings.length]; long[] theResult; mExpectedResultCount = inStrings.length; /* Clear the semaphore indicating that all results have arrived. */ mCalcFinishedSemaphore.drainPermits(); System.out.println("In checksumStrings, about to send requests for " + this); for (int i = 0; i < inStrings.length; i++)

144

{ mChecksumService.checksumString(inStrings[i], i); System.out.println("In checksumStrings, sent request: " + inStrings[i] + " for: " + this); } System.out.println("In checksumStrings, waiting for results for " + this); /* Wait until all results have been delivered. */ mCalcFinishedSemaphore.acquireUninterruptibly(); theResult = mCalculatedChecksums; mCalculatedChecksums = null; return theResult; } /* (non-Javadoc) * @see com.ivan.components.ChecksumServiceCallback#deliverChecksumStringResult(long, long) */ @Override public void deliverChecksumStringResult(int inResultIdentifier, long inStringChecksum) { System.out.printf( "Checksum %d for result id %d delivered to service %s.\n", inStringChecksum, inResultIdentifier, this); if (mCalculatedChecksums != null) { mCalculatedChecksums[inResultIdentifier] = inStringChecksum; /* Are all calculations finished? */ if (--mExpectedResultCount <= 0) { mCalcFinishedSemaphore.release(); } } else { System.err.println("Null result array, cannot store result for " + this); mCalcFinishedSemaphore.release(); } } }

Note that: •

The scope of the BulkChecksum service is COMPOSITE. In order for the original instance of the component to be called back, its scope must be COMPOSITE. We will look more closely at callback instance management after this example program.



The BulkChecksumServiceImpl class implements not only the service interface, but also the ChecksumServiceCallback interface. This makes it possible for it to receive results from the ChecksumService.



The BulkChecksum service retains state while processing a request. If called by multiple clients at the same time, this state will be corrupted. We'll try to devise a solution to this problem later in this chapter.

145

11.5.3 BulkChecksum Service Composite The BulkChecksum service composite defines the SCA component that calculates checksums of multiple strings. In src/main/resources, create a file named “BulkChecksumService.composite” with the following contents: element is not necessary in this example, since we use the default SCA binding. If some other binding is to be declared for the reference, this is where to do it. -->

Note that: •

The element contains a element. This element is not required except when a binding other than the default SCA binding is to be specified for the reference.

11.6 Adding the Composites to the SCA Contribution Metadata Document The two composites are now to be added to the SCA contribution metadata document. Modify the file “sca-contribution.xml” in the META-INF folder in src/main/resources to look like this:

146

11.7 Creating the Client Class The client class invokes the BulkChecksum service and then verifies the result: package com.ivan.client; import org.apache.tuscany.sca.node.Node; import org.apache.tuscany.sca.node.NodeFactory; import com.ivan.components.BulkChecksumService; /** * Main class of the Simple Callback example. * Starts the SCA node, invokes the Bulk Checksum service and * checks the result of the checksum calculations. * * @author Ivan A Krizsan */ public class SCASimpleCallback { /* Constant(s): */ private final static String[] SINGER_NAMES = { "Bon Scott", "Sebastian Bach", "Ozzy Osbourne", "Ronnie James Dio", "Ian Gillan", "Lita Ford" }; private final static long[] SINGER_CHECKSUMS = { 844L, 1320L, 1321L, 1463L, 911L, 821L }; /** * @param args */ public static void main(String[] args) { long[] theChecksums; /* Create and start a new SCA node. */ Node theNode = NodeFactory.newInstance().createNode(); theNode.start(); BulkChecksumService theBuldChecksumService = theNode.getService(BulkChecksumService.class, "BulkChecksumServiceComponent"); theChecksums = theBuldChecksumService.checksumStrings(SINGER_NAMES); for (int i = 0; i < SINGER_NAMES.length; i++) { System.out.printf("Name: %s has checksum %d. Checked: %s\n", SINGER_NAMES[i], theChecksums[i], (theChecksums[i] == SINGER_CHECKSUMS[i])); } } }

147

11.8 Running the Client If we now run the client that invokes the BulkChecksum service, there should be console output similar to that below. The output has been annotated with comments in blue. Note that the ordering of the log statements may not always reflect the execution sequence; for instance, on at least one occasion the log indicating that the ChecksumService service starts processing a string appears before the log indicating that the BulkChecksum service sends the string in question to the ChecksumService service. ** A BulkChecksum service instance is created. Created instance of BulkChecksumServiceImpl: com.ivan.components.impl.BulkChecksumServiceImpl@1bd7b222 ** The BulkChecksum service delegates work to the ChecksumService. In checksumStrings, about to send requests for com.ivan.components.impl.BulkChecksumServiceImpl@1bd7b222 In checksumStrings, sent request: Bon Scott for: com.ivan.components.impl.BulkChecksumServiceImpl@1bd7b222 In checksumStrings, sent request: Sebastian Bach for: com.ivan.components.impl.BulkChecksumServiceImpl@1bd7b222 In checksumStrings, sent request: Ozzy Osbourne for: com.ivan.components.impl.BulkChecksumServiceImpl@1bd7b222 ** The callback reference is injected into the ChecksumService. ChecksumServiceImpl.setResultCallback: [Proxy org.apache.tuscany.sca.core.invocation.impl.JDKCallbackInvocationHandler@773829d5] ** The ChecksumService service starts processing a string. ChecksumServiceImpl.checksumString: Bon Scott, thread: Thread[Thread-2,5,main] – processing... ** The BulkChecksum service delegates work to the ChecksumService. In checksumStrings, sent request: Ronnie James Dio for: com.ivan.components.impl.BulkChecksumServiceImpl@1bd7b222 In checksumStrings, sent request: Ian Gillan for: com.ivan.components.impl.BulkChecksumServiceImpl@1bd7b222 ** The ChecksumService service starts processing four strings. ChecksumServiceImpl.checksumString: Ian Gillan, thread: Thread[Thread-6,5,main] processing... ChecksumServiceImpl.checksumString: Sebastian Bach, thread: Thread[Thread-1,5,main] processing... ChecksumServiceImpl.checksumString: Ronnie James Dio, thread: Thread[Thread-8,5,main] processing... ChecksumServiceImpl.checksumString: Lita Ford, thread: Thread[Thread-5,5,main] – processing... ** The BulkChecksum service delegates work to the ChecksumService. In checksumStrings, sent request: Lita Ford for: com.ivan.components.impl.BulkChecksumServiceImpl@1bd7b222 ** The BulkChecksum service has finished delegating work to the ChecksumService service and is now waiting for results to be delivered. In checksumStrings, waiting for results for com.ivan.components.impl.BulkChecksumServiceImpl@1bd7b222 ** The ChecksumService service starts processing a string. ChecksumServiceImpl.checksumString: Ozzy Osbourne, thread: Thread[Thread-4,5,main] – processing... ** The ChecksumService service finished calculating checksum of a string. ChecksumServiceImpl.checksumString: Sebastian Bach, thread: Thread[Thread-1,5,main] calculated checksum: 1320 ** The BulkChecksum service received a result for the correlation id 1. Checksum 1320 for result id 1 delivered to service com.ivan.components.impl.BulkChecksumServiceImpl@1bd7b222. ** The ChecksumService service finished calculating checksum of a string. ChecksumServiceImpl.checksumString: Ozzy Osbourne, thread: Thread[Thread-4,5,main] calculated checksum: 1321 ** The ChecksumService service finished processing a string and delivering the result. ChecksumServiceImpl.checksumString: Sebastian Bach, thread: Thread[Thread-1,5,main] finished delivering result. ** The BulkChecksum service received a result for the correlation id 2. Checksum 1321 for result id 2 delivered to service com.ivan.components.impl.BulkChecksumServiceImpl@1bd7b222. ** The ChecksumService service finished processing a string and delivering the result. ChecksumServiceImpl.checksumString: Ozzy Osbourne, thread: Thread[Thread-4,5,main] finished delivering result. ** The ChecksumService service finished calculating checksum of a string. ChecksumServiceImpl.checksumString: Lita Ford, thread: Thread[Thread-5,5,main] calculated checksum: 821

148

** The BulkChecksum service received a result for the correlation id 5. Checksum 821 for result id 5 delivered to service com.ivan.components.impl.BulkChecksumServiceImpl@1bd7b222. ** The ChecksumService service finished processing a string and delivering the result. ChecksumServiceImpl.checksumString: Lita Ford, thread: Thread[Thread-5,5,main] finished delivering result. ** The ChecksumService service finished calculating checksum of a string. ChecksumServiceImpl.checksumString: Bon Scott, thread: Thread[Thread-2,5,main] calculated checksum: 844 ** The BulkChecksum service received a result for the correlation id 0. Checksum 844 for result id 0 delivered to service com.ivan.components.impl.BulkChecksumServiceImpl@1bd7b222. ** The ChecksumService service finished processing a string and delivering the result. ChecksumServiceImpl.checksumString: Bon Scott, thread: Thread[Thread-2,5,main] finished delivering result. ** The ChecksumService service finished calculating checksum of a string. ChecksumServiceImpl.checksumString: Ronnie James Dio, thread: Thread[Thread-8,5,main] calculated checksum: 1463 ** The BulkChecksum service received a result for the correlation id 3. Checksum 1463 for result id 3 delivered to service com.ivan.components.impl.BulkChecksumServiceImpl@1bd7b222. ** The ChecksumService service finished processing a string and delivering the result. ChecksumServiceImpl.checksumString: Ronnie James Dio, thread: Thread[Thread-8,5,main] finished delivering result. ** The ChecksumService service finished calculating checksum of a string. ChecksumServiceImpl.checksumString: Ian Gillan, thread: Thread[Thread-6,5,main] calculated checksum: 911 ** The BulkChecksum service received a result for the correlation id 4. Checksum 911 for result id 4 delivered to service com.ivan.components.impl.BulkChecksumServiceImpl@1bd7b222. ** The ChecksumService service finished processing a string and delivering the result. ChecksumServiceImpl.checksumString: Ian Gillan, thread: Thread[Thread-6,5,main] finished delivering result. ** The client lists all the results received from the BulkChecksum service. Name: Bon Scott has checksum 844. Checked: true Name: Sebastian Bach has checksum 1320. Checked: true Name: Ozzy Osbourne has checksum 1321. Checked: true Name: Ronnie James Dio has checksum 1463. Checked: true Name: Ian Gillan has checksum 911. Checked: true Name: Lita Ford has checksum 821. Checked: true

A problem with the BulkChecksum service is that it cannot handle multiple requests at the same time. An attempt at enabling the handling of multiple requests that I tried was to change the scope of the service to STATELESS: ... @Service(BulkChecksumService.class) @Scope("STATELESS") public class BulkChecksumServiceImpl implements BulkChecksumService, ChecksumServiceCallback { ...

If the client is then run, we will see console output similar to this and the program will never terminate: ** A BulkChecksum service instance is created. Created instance of BulkChecksumServiceImpl: com.ivan.components.impl.BulkChecksumServiceImpl@225f1ae9 ** The BulkChecksum service delegates work to the ChecksumService. In checksumStrings, about to send requests for com.ivan.components.impl.BulkChecksumServiceImpl@225f1ae9 In checksumStrings, sent request: Bon Scott for: com.ivan.components.impl.BulkChecksumServiceImpl@225f1ae9 In checksumStrings, sent request: Sebastian Bach for: com.ivan.components.impl.BulkChecksumServiceImpl@225f1ae9 In checksumStrings, sent request: Ozzy Osbourne for: com.ivan.components.impl.BulkChecksumServiceImpl@225f1ae9 ** The callback reference is injected into the ChecksumService. ChecksumServiceImpl.setResultCallback: [Proxy org.apache.tuscany.sca.core.invocation.impl.JDKCallbackInvocationHandler@50f38cf0]

149

** The ChecksumService service starts processing three string. ChecksumServiceImpl.checksumString: Bon Scott, thread: Thread[Thread-2,5,main] processing... ChecksumServiceImpl.checksumString: Sebastian Bach, thread: Thread[Thread-4,5,main] processing... ChecksumServiceImpl.checksumString: Ozzy Osbourne, thread: Thread[Thread-3,5,main] – processing... ** The BulkChecksum service delegates work to the ChecksumService. In checksumStrings, sent request: Ronnie James Dio for: com.ivan.components.impl.BulkChecksumServiceImpl@225f1ae9 ** The ChecksumService service starts processing a string. ChecksumServiceImpl.checksumString: Ronnie James Dio, thread: Thread[Thread-5,5,main] – processing... ** The BulkChecksum service delegates work to the ChecksumService. In checksumStrings, sent request: Ian Gillan for: com.ivan.components.impl.BulkChecksumServiceImpl@225f1ae9 ** The ChecksumService service starts processing a string. ChecksumServiceImpl.checksumString: Ian Gillan, thread: Thread[Thread-1,5,main] – processing... ** The BulkChecksum service delegates work to the ChecksumService. In checksumStrings, sent request: Lita Ford for: com.ivan.components.impl.BulkChecksumServiceImpl@225f1ae9 ** The BulkChecksum service has finished delegating work to the ChecksumService service and is now waiting for results to be delivered. In checksumStrings, waiting for results for com.ivan.components.impl.BulkChecksumServiceImpl@225f1ae9 ** The ChecksumService service starts processing a string. ChecksumServiceImpl.checksumString: Lita Ford, thread: Thread[Thread-7,5,main] – processing... ** The ChecksumService service finished calculating checksum of a string. ChecksumServiceImpl.checksumString: Ronnie James Dio, thread: Thread[Thread-5,5,main] calculated checksum: 1463 ** Another BulkChecksum service instance is created. Created instance of BulkChecksumServiceImpl: com.ivan.components.impl.BulkChecksumServiceImpl@2017b2b2 ** The BulkChecksum service received a result for the correlation id 3. Checksum 1463 for result id 3 delivered to service com.ivan.components.impl.BulkChecksumServiceImpl@2017b2b2. ** The BulkChecksum service do not have an array to store results in. Null result array, cannot store result for com.ivan.components.impl.BulkChecksumServiceImpl@2017b2b2 ** The ChecksumService service finished processing a string and delivering the result. ChecksumServiceImpl.checksumString: Ronnie James Dio, thread: Thread[Thread-5,5,main] finished delivering result. ...

Note that: •

When the ChecksumService service has finished calculating a checksum, a new instance of the BulkChecksum service is created and the ChecksumService service tries to deliver the result to the new instance of the BulkChecksum service.

Obviously this attempt failed. A possible solution is to refactor all of the things related to the state of an invocation of the BulkChecksum service to a helper instance. Each invocation of the BulkChecksum service will result in an instance of the helper being created and, when the ChecksumService service is invoking the callback method, the BulkChecksum service delegates to the appropriate helper. Further investigation of this problem is outside of the scope of this document. Instead we'll examine callback instance management in more detail.

150

11.9 Callback Instance Management References: •

Service Component Architecture SCA-J Common Annotations and APIs Specification, version 1.1, sections 7.2.2 and 9.1.

The following figure shows what happened when we tried to make the BulkChecksum service component STATELESS in the previous section:

Callback invocation from a STATELESS component; a new instance of the component is created when the callback is invoked.

As described in the SCA specification documentation, there is another way to obtain a callback. •

First, we add an annotated instance variable which causes the component context to be injected into ChecksumServiceImpl instances:

... @Service(ChecksumService.class) @Scope("COMPOSITE") public class ChecksumServiceImpl implements ChecksumService { /* Instance variable(s): */ private ChecksumServiceCallback mResultCallback; @Context protected ComponentContext mComponentContext; ...



When the component is to perform a callback, the callback proxy can be retrieved using the following line of code. Note that the callback proxy needs to be cast to the appropriate interface, ChecksumServiceCallback in this case.

mComponentContext.getRequestContext().getCallback()

This approach does not change anything – a new instance of the BulkChecksum service component is still created and invoked as the result of the callback. To obtain the callback programmatically, we used the component context which was injected into an instance field annotated by @Context by the SCA runtime. In the next section we'll take a closer look at what the component context can provide. 151

11.10 Component Context References: •

Service Component Architecture SCA-J Common Annotations and APIs Specification, version 1.1, sections 9.1 and 10.7.

As seen above, the component context can be used at runtime to obtain, for instance, a callback reference. In this section we'll briefly look at the API offered by the component context.

11.10.1 Obtaining a Component Context A reference to a component context is obtained by dependency injection. For example, an instance field with the type ComponentContext is annotated with the @Context annotation and the SCA runtime injects the reference when the application is run. The @Context annotation can annotate either an instance field or a setter-method. Depending on the type of the instance field or the parameter of the setter-method, either a reference to a ComponentContext or a RequestContext is injected. (continue on next page)

152

11.10.2 Component Context API The ComponentContext API contains the following methods: Method Signature

Description

String getURI()

Retrieve the absolute URI of the SCA component.

> R cast(B paramB) throws IllegalArgumentException

Cast a reference to a ServiceReference.

B getService(Class businessInterface, String referenceName)

Retrieves a proxy that implements the supplied interface for the named reference in the current component. The reference name is the value of the name attribute of a element in the current component. If the multiplicity of the reference is greater than one, an exception will be thrown.

Collection getServices(Class paramClass, String paramString)

Retrieves multiple proxies that implements the supplied interface for the named reference in the current component. The reference name is the value of the name attribute of a element in the current component.

ServiceReference getServiceReference(Class businessInterface, String referenceName)

Retrieves a ServiceReference to the reference in the current component with the supplied name. The reference name is the value of the name attribute of a element in the current component. If the multiplicity of the reference is greater than one, an exception will be thrown.

Collection> getServiceReferences(Class paramClass, String paramString)

Retrieves multiple ServiceReference objects corresponding to the references in the current component with the supplied name. The reference name is the value of the name attribute of a element in the current component.

B getProperty(Class paramClass, String paramString)

Retrieves the value of a SCA property defined in the current component.

ServiceReference createSelfReference(Class Retrieves a ServiceReference that can be used to invoke businessInterface) this component over the selected service. ServiceReference createSelfReference(Class Retrieves a ServiceReference that can be used to invoke businessInterface, String serviceName); this component over the service with the supplied name. RequestContext getRequestContext()

Retrieves the current SCA service request context. Null if there is no request or the context is not available.

153

12 Dynamic Component Relationships References: •

Service Component Architecture Assembly Model Specification, version 1.1, sections 3.1.2 and 4.3.

Not only is it possible to, as we have seen, establish relationships between components through dependency injection. In this chapter we will implement the Observer design pattern, allowing observers (a SCA component) to dynamically register with one or more observers (a different SCA component) to receive notifications. The wiredByImpl attribute in the element is used to enable dynamic wiring of relationships. It should be noted that, as of writing this, there is no standardized way of using this capability from Java. Also note that the SCA callback mechanism is not used in this example. Instead, other means to obtain the same result are used. For more information, see https://issues.apache.org/jira/browse/TUSCANY-3464.

12.1 Creating the Project Start by setting up a new Eclipse project as described in chapter 2. I will name my project “SCAObserver”.

12.2 Observer Component The role of the Observer component corresponds to that of an observer in the design pattern with the same name. In this example, an observer will register with two observable components, from which the observer will receive notifications.

12.2.1 ObserverService Interface The following interface acts both as the interface of the Observer service and as the callback interface of the Observable service we are to develop later. In a real application, it may be a good idea to separate these concerns; one interface that only contains the notify method and another interface containing the other methods of the service. The interface of the Observer service is implemented as follows: package com.ivan.components; import org.oasisopen.sca.ServiceReference; import org.oasisopen.sca.annotation.OneWay; import org.oasisopen.sca.annotation.Remotable; /** * Interface defining the operations that a service observing * an observable service must supply. * One instance of an observer can observe multiple observables. * In this example, this interface defines both the operation * that an observer need to implement (notify) and other operations. * In a real-world scenario this interface would ideally be refactored * to two separate interfaces. * * @author Ivan A Krizsan */ @Remotable public interface ObserverService { /** * Notifies the observer that the event with the supplied id has * been issued by the observer.

154

* * @param inEventId Id identifying the event. */ @OneWay void notify(String inEventId); /** * Starts observing the supplied observable services by registering * with each of them for event notifications. * * @param inObservables References to observable services that are * to be observed. */ @OneWay void startObserving(ServiceReference[] inObservables); /** * Stops observing the observable services that are observed * by unregistering for event notifications with each observable. * If no observable services are observed, then do nothing. */ @OneWay void stopObserving(); }

12.2.2 ObserverService Implementation The Observer service can start observing a number of observables, stop observing observables and receive notifications from one or more observables. Each instance of the ObserverService implementation class will have an id which will be shown in log messages, for instance when logging a received notification. package com.ivan.components.impl; import import import import import import import

org.oasisopen.sca.ComponentContext; org.oasisopen.sca.ServiceReference; org.oasisopen.sca.annotation.Context; org.oasisopen.sca.annotation.Init; org.oasisopen.sca.annotation.Reference; org.oasisopen.sca.annotation.Scope; org.oasisopen.sca.annotation.Service;

import com.ivan.components.ObservableService; import com.ivan.components.ObserverService; /** * Implementation of a observer service that can be told to register * and unregister with observable services, as well as receive * event notifications (when registered). * * @author Ivan A Krizsan */ @Service(ObserverService.class) @Scope("COMPOSITE") public class ObserverServiceImpl implements ObserverService { /* Class variable(s): */ private static int sInstanceCounter = 1; /* Instance variable(s): */ private int mInstanceNumber; /** * Despite having the required attribute set to true for these * references, it need not be injected by the runtime, since * we also set the wiredByImpl attribute to true. */ @Reference(name = "observable", required = true) private ObservableService[] mObservables; private String[] mObserverIds; @Context

155

private ComponentContext mComponentContext; /** * Initializes the component instance before it is taken into use. */ @Init public void init() { mInstanceNumber = sInstanceCounter++; System.out.println("ObserverServiceImpl.init() - Observer id: " + mInstanceNumber); } /* (non-Javadoc) * @see com.ivan.components.ObserverService#notify(java.lang.String) */ @Override public void notify(String inEventId) { System.out.println("ObserverServiceImpl.notify(" + inEventId + ") - Observer id: " + mInstanceNumber); } /* (non-Javadoc) * @see com.ivan.components.ObserverService#startObserving(org.oasisopen.sca.ServiceReference[]) */ @Override public void startObserving( ServiceReference[] inObservables) { /* * Since we set the wiredByImpl attribute to true on the * references to the observables, here is were we fulfill * the responsibility of establishing the wiring to the * observables. */ mObservables = new ObservableService[inObservables.length]; for (int i = 0; i < inObservables.length; i++) { mObservables[i] = inObservables[i].getService(); } /* * Create array to hold the ids assigned to the observer * by each observable. These ids are to be used when unregistering * for notifications later. * */ mObserverIds = new String[mObservables.length]; /* * In order for the SCA runtime to be able to establish a valid * wire between the observable and the observer, we need to create * a self-reference to the observer and supply it to the observable * when registering for notifications. */ ServiceReference theRef = mComponentContext.createSelfReference(ObserverService.class); /* Register for notification with each observable. */ for (int i = 0; i < mObservables.length; i++) { mObserverIds[i] = mObservables[i].register(theRef); System.out.println("ObserverServiceImpl.startObserving() - " + "Observer id: " + mInstanceNumber + " registered with observable and received id: " + mObserverIds[i]); } } /* (non-Javadoc) * @see com.ivan.components.ObserverService#stopObserving() */ @Override

156

public void stopObserving() { /* Unregister with each observable. */ for (int i = 0; i < mObservables.length; i++) { /* * Use the id assigned by the observable when unregistering, * in order for the observable to know which observer to * unregister. */ mObservables[i].unregister(mObserverIds[i]); mObserverIds[i] = null; System.out.println("ObserverServiceImpl.stopObserving() - " + "Observer id: " + mInstanceNumber); } mObserverIds = null; mObservables = new ObservableService[0]; } }

Note that: •

The mObservables instance field annotated with the @Reference annotation is an array. As seen in the startObserving method, one observer can register with multiple observables for notification.



The required attribute of the @Reference annotation annotating the mObservables instance field is set to true. Despite this no relationships with other components are established in the composite, as we are used to. Instead such relationships are formed in the startObserving method. This is made possible by setting the wiredByImpl attribute of the reference to true in the composite – the composite file will be defined in the next section.



The component context is injected by the SCA runtime into the instance field mComponentContext.



The component context is used in the startObserving method to create a self-reference that is supplied to the observable(s) with which the observer registers. The observable(s) will later use this reference to send notifications to the observer, as we will see when implementing the ObservableService in a subsequent section.



The init method is annotated with the @Init annotation. This annotation is used to annotate a method that takes no parameters and has the void return type. Such a method will be invoked by the SCA runtime when an instance of the class is created, before the component starts to process requests. In this case, the init method is used to assign an id to the component implementation instance and print a log message.

157

12.2.3 ObserverService Composite The ObserverService service composite defines two SCA components that each observers an unspecified number of observables. In src/main/resources, create a file named “Observers.composite” with the following contents:

Note that: •

The “observable” references in both of the components have the wiredByImpl attribute set to true. If not set to true, the SCA runtime will want to wire the reference when loading the composite and an error will occur.

158

12.3 Observable Component The role of the Observable component corresponds to that of an observable in the Observer design pattern. In this example, an observable will allow a number of observers to register for notification. Notifications will be send out with a fixed time interval. Finally, when not wanting to receive further notifications, observers can unregister with the observable.

12.3.1 ObservableService Interface The ObservableService interface defines the operations provided by the Observable service: package com.ivan.components; import org.oasisopen.sca.ServiceReference; import org.oasisopen.sca.annotation.OneWay; import org.oasisopen.sca.annotation.Remotable; /** * Interface defining the properties of an observable service. * * @author Ivan A Krizsan */ @Remotable public interface ObservableService { /** * Registers the observer identified by the supplied service reference * to receive event notifications. * * @return Id assigned to the observer in connection to this observable. */ String register(ServiceReference inObserverRef); /** * Unregisters the caller as to stop receiving event notifications. * Does nothing if the caller is not previously registered to * receive event notifications or if supplied observer id is * incorrect. * * @param inObserverId Id of the observer to unregister, as received * when registering. */ @OneWay void unregister(final String inObserverId); }

Note that: •

The unregister method is annotated with the @OneWay annotation. Thus, unregistering for notifications is an asynchronous operation.

159

12.3.2 ObservableService Implementation The Observable service allows for client to register and unregister for notifications with the service: package com.ivan.components.impl; import import import import

java.util.Date; java.util.HashMap; java.util.Map; java.util.UUID;

import import import import import

org.oasisopen.sca.ServiceReference; org.oasisopen.sca.annotation.Destroy; org.oasisopen.sca.annotation.Init; org.oasisopen.sca.annotation.Scope; org.oasisopen.sca.annotation.Service;

import com.ivan.components.ObservableService; import com.ivan.components.ObserverService; import edu.emory.mathcs.backport.java.util.concurrent.ScheduledThreadPoolExecutor; import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit; /** * Implements the Observable service which will send notification * events to observers having registered with the service. * This example-observable will periodically send out events telling * observers the current time and date. * * @author Ivan A Krizsan */ @Service(ObservableService.class) @Scope("COMPOSITE") public class ObservableServiceImpl implements ObservableService, Runnable { /* Class variable(s): */ private static int sInstanceCounter = 1; /* Instance variable(s): */ @SuppressWarnings("unchecked") private Map mObservers = new HashMap(); private ScheduledThreadPoolExecutor mExecutor; private int mInstanceNumber; /** * Initializes an instance of this component by scheduling the * task that periodically is to send events to observers. */ @Init public void init() { /* Scheduled event sending. */ mExecutor = new ScheduledThreadPoolExecutor(10); long theScheduledInterval = (long)(Math.random() * 300.0) + 200L; mExecutor.scheduleAtFixedRate(this, 0, theScheduledInterval, TimeUnit.MILLISECONDS); /* Assign an id to the observable. */ mInstanceNumber = sInstanceCounter++; System.out.println("ObservableServiceImpl.init() - Observable id: " + mInstanceNumber); } /** * Cleans up when an instance of this component is to be taken out * of service by stopping the periodical task sending events to * observers. */ @Destroy public void cleanUp() { mExecutor.shutdownNow(); mExecutor = null;

160

mObservers.clear(); System.out.println("ObservableServiceImpl.cleanUp() - Observable id: " + mInstanceNumber); } /* (non-Javadoc) * @see com.ivan.components.ObservableService#register(org.oasisopen.sca.ServiceReference) */ @Override public String register(ServiceReference inObserverRef) { /* * Assign a unique id to the observer and store its reference. * This id is also used by the observer when unregistering for * notifications. */ String theObserverId = UUID.randomUUID().toString(); mObservers.put(theObserverId, inObserverRef); /* * If we do not call the observer that is to be registered here, * a relation will not be established and future attempts at * calling back the observer will fail silently. * Note that we can call any method, like toString in this case. */ inObserverRef.getService().toString(); /* * Note that using the request context to retrieve a callback * reference to the observer will NOT work - the SCA runtime * will complain about there not being any wire between * the components. * The observer must create a self-reference and supply it * when calling this method - see the observer for how this is * accomplished! */ System.out .println("ObservableServiceImpl.register() - " + "Registered an observer to observable with id: " + mInstanceNumber + ", assigning the observer id: " + theObserverId); return theObserverId; } /* (non-Javadoc) * @see com.ivan.components.ObservableService#unregister(java.lang.String) */ @Override public void unregister(String inObserverId) { mObservers.remove(inObserverId); System.out.println("ObservableServiceImpl.unregister(" + inObserverId + ") - Completed (observable id: " + mInstanceNumber + ")"); } /* (non-Javadoc) * @see java.lang.Runnable#run() */ @Override public void run() { Date theCurrentTime = new Date(); System.out.println("ObservableServiceImpl.run() - " + "Entering (observable id: " + mInstanceNumber + ")"); for (ServiceReference theObserverRef : mObservers .values()) { theObserverRef.getService().notify("" + theCurrentTime); } System.out.println("ObservableServiceImpl.run() - "

161

+ "Exiting (observable id: " + mInstanceNumber + ")"); } }

Note that: •

The init method is annotated with the @Init annotation. See the notes for the ObserverService implementation!



The cleanUp method is annotated with the @Destroy annotation. This annotation is used to annotate a method that takes no parameters and has the void return type. Such a method will be invoked by the SCA runtime when an instance of the class is to be taken out of service. In this case, the cleanUp method is used to cancel the scheduling of periodic notifications to observers and printing a log message.



In the register method, the toString method is called on the observer service that registers for notifications. A bug in Tuscany causes further calls to the observer to fail silently, unless a method is called on the observer in the register method. Any method may be called.



In the register method, using the request context to retrieve a callback reference or a plain reference to the caller and later use this reference to invoke observers will not work. See https://issues.apache.org/jira/browse/TUSCANY-3464 for more information.

12.3.3 ObservableService Composite The ObservableService service composite defines two SCA components. In src/main/resources, create a file named “Observable.composite” with the following contents:

162

12.4 Adding the Composites to the SCA Contribution Metadata Document The two composites are now to be added to the SCA contribution metadata document. Modify the file “sca-contribution.xml” in the META-INF folder in src/main/resources to look like this:

12.5 Creating the Client Class The client class does the following: •

Starts the SCA node.



Retrieves the observable services.



Retrieves the observer services.



Instructs the observers to start observe the observables.



Waits five seconds.



Instructs the observers to stop observing the observables.



Stops the SCA node.

The client class is implemented as follows: package com.ivan.client; import org.apache.tuscany.sca.node.Node; import org.apache.tuscany.sca.node.NodeFactory; import org.oasisopen.sca.ServiceReference; import com.ivan.components.ObservableService; import com.ivan.components.ObserverService; /** * Main class of the Observer Callback example. * Starts the SCA node, retrieves the observers and observables and tells * the observers to start observing the observables. * After some time, tell the observers to stop observing and finally * shut down the SCA node. * * @author Ivan A Krizsan */ public class SCAObserverCallback { public static void main(String[] args) { /* Create and start a new SCA node. */ Node theNode = NodeFactory.newInstance().createNode(); theNode.start(); /* Retrieve the services to be observed. */ ServiceReference[] theObservables = new ServiceReference[2]; theObservables[0] = theNode.getServiceReference(ObservableService.class, "ObservableServiceComponent1");

163

theObservables[1] = theNode.getServiceReference(ObservableService.class, "ObservableServiceComponent2"); /* Retrieve the observers. */ ObserverService theObserver1 = theNode.getService(ObserverService.class, "Observer1Component"); ObserverService theObserver2 = theNode.getService(ObserverService.class, "Observer2Component"); /* Tell the observers to start observing. */ theObserver1.startObserving(theObservables); theObserver2.startObserving(theObservables); /* Let the observers observe for five seconds. */ waitSec(5); /* Tell observers to stop observing. */ theObserver1.stopObserving(); theObserver2.stopObserving(); waitSec(5); theNode.stop(); } private static void waitSec(final long inSeconds) { try { Thread.sleep(inSeconds * 1000L); } catch (final InterruptedException theException) { // Ignore exceptions. } } }

164

12.6 Running the Client When running the client, console output similar to the listing below will be output. Red text is log output from the SCA runtime and blue text is comments added by hand. Portions of the output have been removed for brevity. Note that the ordering of the log statements may not always reflect the execution sequence. INFO: Add endpoint - (@544096693)Endpoint: URI = Observer2Component#servicebinding(ObserverService/ObserverService) ** Observers 1 & 2 created. ObserverServiceImpl.init() - Observer id: 1 ObserverServiceImpl.init() - Observer id: 2 ** Observable 1 created. ObservableServiceImpl.init() - Observable id: 1 ** Observable 1 starts notifying observers. ObservableServiceImpl.run() - Entering (observable id: 1) ** Observer 1 registers for notifications with observable 1. ObservableServiceImpl.register() - Registered an observer to observable with id: 1, assigning the observer id: 69a6558c-415b-41aa-aa39-e8746 c61d08a ObserverServiceImpl.startObserving() - Observer id: 1 registered with observable and received id: 69a6558c-415b-41aa-aa39-e8746c61d08a ** Observer 2 registers for notifications with observable 1. ObservableServiceImpl.register() - Registered an observer to observable with id: 1, assigning the observer id: e503a22a-0f23-453c-bb52-edafe 4c9dfc7 ObserverServiceImpl.startObserving() - Observer id: 2 registered with observable and received id: e503a22a-0f23-453c-bb52-edafe4c9dfc7 ** Observable 1 finished notifying observers. ObservableServiceImpl.run() - Exiting (observable id: 1) ** Observable 0 starts and finished notifying observers (no observers to notify). ObservableServiceImpl.run() - Entering (observable id: 0) ObservableServiceImpl.run() - Exiting (observable id: 0) ** Observable 2 created. ObservableServiceImpl.init() - Observable id: 2 ** Observer 2 registers for notifications with observable 2. ObservableServiceImpl.register() - Registered an observer to observable with id: 2, assigning the observer id: b6c38194-3536-4258-9c99-24a93 6f94ea7 ObserverServiceImpl.startObserving() - Observer id: 2 registered with observable and received id: b6c38194-3536-4258-9c99-24a936f94ea7 ** Observer 1 registers for notifications with observable 2. ObserverServiceImpl.notify(Wed Mar 03 21:08:47 CST 2010) - Observer id: 1 ObservableServiceImpl.register() - Registered an observer to observable with id: 2, assigning the observer id: 1ca03736-a649-40af-af4e-700a4 29b09eb ObserverServiceImpl.startObserving() - Observer id: 1 registered with observable and received id: 1ca03736-a649-40af-af4e-700a429b09eb ** Observers receives a number of notifications from the Observables. ObservableServiceImpl.run() - Entering (observable id: 2) ObserverServiceImpl.notify(Wed Mar 03 21:08:47 CST 2010) - Observer id: 2 ObservableServiceImpl.run() - Exiting (observable id: 2) ObserverServiceImpl.notify(Wed Mar 03 21:08:47 CST 2010) - Observer id: 1 ObservableServiceImpl.run() - Entering (observable id: 1) ObservableServiceImpl.run() - Entering (observable id: 2) ObserverServiceImpl.notify(Wed Mar 03 21:08:47 CST 2010) - Observer id: 2 ObservableServiceImpl.run() - Exiting (observable id: 1) ObserverServiceImpl.notify(Wed Mar 03 21:08:47 CST 2010) - Observer id: 1 ObservableServiceImpl.run() - Exiting (observable id: 2) ... ObservableServiceImpl.run() - Entering (observable id: 2) ObserverServiceImpl.notify(Wed Mar 03 21:08:52 CST 2010) - Observer id: 2 ObservableServiceImpl.run() - Exiting (observable id: 2) ObserverServiceImpl.notify(Wed Mar 03 21:08:52 CST 2010) - Observer id: 1 ** Observers 1 and 2 stops observing by unregistering with observables 1 and 2. ObserverServiceImpl.stopObserving() - Observer id: 1 ObserverServiceImpl.stopObserving() - Observer id: 2 ObserverServiceImpl.stopObserving() - Observer id: 1 ObservableServiceImpl.unregister(69a6558c-415b-41aa-aa39-e8746c61d08a) - Completed (observable id: 1) ObservableServiceImpl.unregister(1ca03736-a649-40af-af4e-700a429b09eb) - Completed (observable id: 2) ObservableServiceImpl.unregister(e503a22a-0f23-453c-bb52-edafe4c9dfc7) - Completed

165

(observable id: 1) ObserverServiceImpl.stopObserving() - Observer id: 2 ObservableServiceImpl.unregister(b6c38194-3536-4258-9c99-24a936f94ea7) - Completed (observable id: 2) ** Observables 1 and 2 continues to notify, but there are no observers to notify. ObservableServiceImpl.run() - Entering (observable id: 1) ObservableServiceImpl.run() - Exiting (observable id: 1) ObservableServiceImpl.run() - Entering (observable id: 2) ObservableServiceImpl.run() - Exiting (observable id: 2) ... ObservableServiceImpl.run() - Entering (observable id: 1) ObservableServiceImpl.run() - Exiting (observable id: 1) ObservableServiceImpl.run() - Entering (observable id: 2) ObservableServiceImpl.run() - Exiting (observable id: 2) Mar 3, 2010 21:08:57 PM org.apache.tuscany.sca.node.impl.NodeImpl stop INFO: Stopping node: http://tuscany.apache.org/sca/1.1/nodes/default0 Mar 3, 2010 21:08:57 PM org.apache.tuscany.sca.core.assembly.impl.EndpointRegistryImpl endpointRemoved INFO: Remove endpoint - (@451237309)Endpoint: URI = ObservableServiceComponent1#service-binding(ObservableService/ObservableService) Mar 3, 2010 21:08:57 PM org.apache.tuscany.sca.core.assembly.impl.EndpointRegistryImpl endpointRemoved INFO: Remove endpoint - (@1180694806)Endpoint: URI = ObservableServiceComponent2#service-binding(ObservableService/ObservableService) Mar 3, 2010 21:08:57 PM org.apache.tuscany.sca.core.assembly.impl.EndpointRegistryImpl endpointRemoved INFO: Remove endpoint - (@1188706162)Endpoint: URI = Observer1Component#servicebinding(ObserverService/ObserverService) Mar 3, 2010 21:08:57 PM org.apache.tuscany.sca.core.assembly.impl.EndpointRegistryImpl endpointRemoved INFO: Remove endpoint - (@544096693)Endpoint: URI = Observer2Component#servicebinding(ObserverService/ObserverService) ** Observables 1 and 2 are taken out of service. ObservableServiceImpl.cleanUp() - Observable id: 1 ObservableServiceImpl.cleanUp() - Observable id: 2

This concludes the example program showing how to dynamically establish relations between SCA components at runtime.

166

13 SCA Domain with Distributed Nodes Apache Tuscany has support for running composites in multiple JVMs that may be deployed on different nodes and still be able to perform reference injection into components as if referenced components were deployed in one and the same JVM. To accomplish this Tuscany uses either Tribes, which originally were developed to facilitate clustering of Tomcat servers, or Hazelcast. The example program in this chapter consists of the following parts: •

A service, the Standalone Processor service, that processes strings enclosed in requests from clients and returns acknowledgements to the clients.



A client to the Standalone Processor service that periodically sends requests to the service and logs the results.



A service interface that is used both in the client and service nodes.

The client and server will run in different JVMs. We will use first Tribes and, later, Hazelcast to facilitate dynamic discovery of nodes. We will see that configuring for Tribes or Hazelcast does not require modifications to Java code and that changing the protocol used between the nodes does not require modifying the source code of the components deployed to the different nodes. Some features used in this example program are not available in Apache Tuscany 2.0-M4, so we have to download the latest nightly build and use the pre-built binaries.

13.1 Creating the Projects The example program in this chapter consists of three different projects: •

The TuscanyServerInterface project. This project will contain the interface of the Standalone Processor service. From this project, a library JAR will be created that is used by the client and server.



The TuscanyServerNode project. This project will contain the implementation of the Standalone Processor service.



The TuscanyClientNode project. This project will contain the implementation of the client that sends requests to the Standalone Processor service.

It is assumed that the three projects are located in their own directory, as common with Eclipse project, and that the project directories are all located in one and the same directory; commonly the workspace directory.

167

13.1.1 Creating the TuscanyServerInterface Project Create a new Eclipse project as described in chapter 2 named “TuscanyServerInterface”, with the following modifications: •

Do not create a SCA Contributions Metadata Document. It is not needed in this project, since there will only be a single interface in the project.



Remove all dependencies from the Maven pom.xml files. Since we will use the pre-built binaries, we do not need to use Maven dependency management.



Add the “tuscany-sca-manifest.jar” JAR file to the build path of the project. This JAR file is found in the “features” directory in the binary distribution of Apache Tuscany 2. Remember to use the JAR file from the nightly build, not the one from the 2.0M4 release. Including it on the classpath results in all of the Tuscany modules being included on the classpath.

13.1.2 Creating the TuscanyServerNode Project Create a new Eclipse project as described in chapter 2 named “TuscanyServerNode”, with the following modifications: •

Remove all dependencies from the Maven pom.xml files.



Add the “tuscany-sca-manifest.jar” JAR file to the build path of the project. This JAR file is found in the “features” directory in the binary distribution of Apache Tuscany 2. Remember to use the JAR file from the nightly build, not the one from the 2.0M4 release.



In the SCA Contributions Metadata Document, replace all occurrences of the XML namespace “http://docs.oasis-open.org/ns/opencsa/sca/200903” with the XML namespace “http://docs.oasis-open.org/ns/opencsa/sca/200912”. The most recent version of Tuscany includes modifications to XML schemas and the new versions uses a different namespace.

13.1.3 Creating the TuscanyClientNode Project Create a new Eclipse project as described in chapter 2 named “TuscanyClientNode”, with the following modifications: •

Remove all dependencies from the Maven pom.xml files.



Add the “tuscany-sca-manifest.jar” JAR file to the build path of the project. This JAR file is found in the “features” directory in the binary distribution of Apache Tuscany 2.



In the SCA Contributions Metadata Document, replace all occurrences of the XML namespace “http://docs.oasis-open.org/ns/opencsa/sca/200903” with the XML namespace “http://docs.oasis-open.org/ns/opencsa/sca/200912”.

168

13.2 Service Interface The interface of the Standalone Processor service is used by the service implementation project as well as the client, so it has been refactored into a separate project – the TuscanyServerInterface project. The interface is remotable, since it will be used when communicating between nodes in different JVMs, possibly on different nodes. package com.ivan.service; import org.oasisopen.sca.annotation.Remotable; /** * Interface defining the services supplied by the Standalone Processor * service. * * @author Ivan A Krizsan */ @Remotable public interface StandaloneProcessorService { /** * Processes the supplied message from the client with the * supplied id and generates a string. * * @param inClientId Id of client requesting message processing. * @param inMessage Message to process. * @return Processed message, or empty string if unable to process * message. */ String process(final String inClientId, final String inMessage); }

No additional artifacts are needed in the TuscanyServerInterface project. Having finished creating the project, build its JAR-file issuing “mvn install”. This can be accomplished in Eclipse by right-clicking the project and selecting Run As -> Maven install.

Building the TuscanyServiceInterface JAR in Eclipse.

Having built the JAR file, confirm that the target directory in the project root directory contains a JAR file named “TuscanyServerInterface-0.0.1-SNAPSHOT.jar”.

169

13.3 Standalone Processor Service The Standalone Processor service is implemented in the TuscanyServerNode project. It processes strings from clients and returns a resulting string. Perhaps more important, in order for us to follow the operation of the service, are the log messages that are printed to the console. Before we start to implement the different parts of the Standalone Processor service, add the JAR “ TuscanyServerInterface-0.0.1-SNAPSHOT.jar” generated in the above section to the project classpath.

13.3.1 Service Implementation The implementation of the Standalone Processor service is straightforward. One new thing is the @EagerInit annotation, which will be discussed in detail in the next section. package com.ivan.service; import import import import import

java.util.Date; org.oasisopen.sca.annotation.EagerInit; org.oasisopen.sca.annotation.Init; org.oasisopen.sca.annotation.Scope; org.oasisopen.sca.annotation.Service;

/** * Implements the Standalone Processor service. * * @author Ivan A Krizsan */ @EagerInit @Scope("COMPOSITE") @Service(StandaloneProcessorService.class) public class StandaloneProcessorServiceImpl implements StandaloneProcessorService { /** * Initializes the instance before the service is taken into use. */ @Init public void init() { System.out.println("StandaloneProcessorServiceImpl.init"); } /* (non-Javadoc) * @see com.ivan.service.StandaloneProcessorService#process(java.lang.String, java.lang.String) */ @Override public String process(final String inClientId, final String inMessage) { Date theDate = new Date(); System.out.println("StandaloneProcessorServiceImpl.process: " + "Client: " + inClientId + " sent the message: " + inMessage + " at " + theDate); return "ACK: Message: " + inMessage + " from client: " + inClientId + " at " + theDate; } }

Note that: •

The StandaloneProcessorServiceImpl class implements the StandaloneProcessorService interface.



The scope of the StandaloneProcessor service is COMPOSITE. 170

13.3.2 The @EagerInit Annotation References: • Service Component Architecture SCA-J Common Annotations and APIs Specification, version 1.1, section 3.4 and 10.9. The @EagerInit annotation can be applied to the Java implementation class that has the COMPOSITE scope. The effect of the annotation is that the instance is created as soon as the component that contains the class is started. Note that the annotation @EagerInit may only be applied to classes that are scoped COMPOSITE. The @EagerInit annotation can be used in conjunction with the @Init annotation to create an entrypoint for a SCA node. An example of this will be seen when we implement the client of the Standalone Processor service.

13.3.3 Service Composite The Standalone Processor service composite defines a single SCA component. In src/main/resources, create a file named “TuscanyServerNode.composite” with the following contents:

It is possible to specify a element in the Standalone Processor component and, inside the element, specify a binding. However, the result would be exposing the service using the specified binding type – we have already seen how to expose services and this is not the aim of this example. Recall that when no binding is specified, the SCA binding will be used.

13.3.4 Adding the Composite to the SCA Contribution Metadata Document Adding the above composite to the Standalone Processor service's SCA contribution metadata document should now be straightforward. The modified document looks like this:

171

13.3.5 Node Configuration Document A Tuscany-specific artifact that we haven't seen before is the node configuration document – an XML document that we later supply to the node starter when starting the node in which the service is deployed. The node configuration document contains the following information about the node: •

Node identifier. An URI uniquely identifying the node in the domain.



Domain. Specifies which domain the node belongs to.



Domain registry. Kind of domain registry.



Base URIs for different kinds of bindings.



Contributions to the node. Contributions include interfaces and implementations of service as well as composites.

Create a file named “server-config.xml” in src/main/resources with the following contents: element determine the following: uri - Node identifier. domainRegistry - Determine kind and location of the domain registry. For example: tribes://228.0.0.100:50000 for Tribes or tuscany:default for Hazelcast. domain - Domain the node belongs to. -->

172

In addition to being embedded in an application, as seen in this example, a node configuration document can be stored in a separate file and the node launcher started from the command line. In such a use case, being able to specify the contribution locations makes more sense. Note that: •

The value of the domainRegistry attribute of the element has the prefix “tribes”. This causes Tribes to be used as the means for dynamically discovering nodes.



The value of the domain attribute of the element is what determine which domain a given node belongs to. In this example, the service node and all the client nodes must belong to the same domain, in order for the clients to be able to discover the service.



The element has a number of child elements. Each of the elements specifies base URIs for one binding type. For example, all URLs for services exposed as web services start with either “http://localhost:8082/ws” or “https://localhost:8082/ws” and then have the component name and service name appended.



The element contains two elements. These elements are used to specify the location of the interface the node exposes and the class implementing the exposed interface. Empty values for the location attributes are used when the contributions in question are on the classpath.



The second element contains a element. This element is used to specify the location of the composite(s) to be deployed in the node.

173

13.3.6 Adding the Composite to the SCA Contribution Metadata Document The composite is now to be added to the SCA contribution metadata document. Modify the file “sca-contribution.xml” in the META-INF folder in src/main/resources to look like this:

174

13.3.7 Service Node Starter The service node needs to be started supplying the node configuration from the previous section. Tuscany has a special class that takes care of starting up a node. In addition, the protocol to be used when components in different nodes are communicating with each other needs to be specified. package com.ivan.starter; import org.apache.tuscany.sca.binding.rmi.RMIBinding; import org.apache.tuscany.sca.node.launcher.NodeMain; /** * Starts the node on which the Processor Service is to run. * * @author Ivan A Krizsan */ public class StartProcessorServerNode { /* Constant(s): */ private final static String[] NODELAUNCHER_ARGS = { "-node", "target/classes/server-config.xml" }; /** * Program entry point. * * @param args Command line arguments. * @throws Exception If error occurred launching node. */ public static void main(String[] args) throws Exception { /* * In this example, using Tribes or Hazelcast, the fact that * the client and server are running in different VMs is supposed * to be transparent to both nodes. * Thus the binding used between the client and service nodes cannot * be specified in the client's composite file but has to be * specified by setting a system property as below. * See the client composite file for more details! */ String theBindingType = RMIBinding.TYPE.toString(); System.setProperty( "org.apache.tuscany.sca.binding.sca.provider.SCABindingMapper.mappedBinding", theBindingType); /* * Use the Tuscany node launcher to launch the node, supplying * the path to the node configuration file. */ NodeMain.main(NODELAUNCHER_ARGS); } }

Note that: •

The constant NODELAUNCHER_ARGS contains the arguments to the node launcher specifying the node configuration file to use.



The protocol used between distributed components is set to RMI by setting an Apache Tuscany system property. The example program has been successfully run with the following protocols: RMIBinding, WebServiceBinding



The class NodeMain is used to launch the SCA node.

175

13.3.8 Running the Service Node The node on which the Standalone Processor service is to be run can now be started by running the service node starter. Output similar to the following will be output to the console (red text are output from the SCA runtime, blue text are comments added by hand, black text is printed by the program): ** Node configuration file used. Mar 18, 2010 9:27:42 PM org.apache.tuscany.sca.node.launcher.NodeLauncher main INFO: SCA Node configuration: target/classes/server-config.xml Mar 18, 2010 9:27:44 PM org.apache.tuscany.sca.node.launcher.NodeLauncher main INFO: Apache Tuscany SCA Node is starting... ** Name of node and name of the domain the node belongs to. Mar 18, 2010 9:27:44 PM org.apache.tuscany.sca.node.impl.NodeImpl start INFO: Starting node: http://ivan.com/nodes/ServerNode domain: http://ivan.com/domains/test ** Tribes startup and cluster initialization. Mar 18, 2010 9:27:44 PM org.apache.tuscany.sca.endpoint.tribes.AbstractReplicatedMap init INFO: Initializing AbstractReplicatedMap with context name:http://ivan.com/domains/test Mar 18, 2010 9:27:44 PM org.apache.catalina.tribes.transport.ReceiverBase bind INFO: Receiver Server Socket bound to:/127.0.0.1:4000 Mar 18, 2010 9:27:45 PM org.apache.catalina.tribes.membership.McastServiceImpl setupSocket INFO: Attempting to bind the multicast socket to /228.0.0.100:50000 Mar 18, 2010 9:27:45 PM org.apache.catalina.tribes.membership.McastServiceImpl setupSocket INFO: Setting multihome multicast interface to:/fe80:0:0:0:0:0:0:1%1 Mar 18, 2010 9:27:45 PM org.apache.catalina.tribes.membership.McastServiceImpl setupSocket INFO: Setting cluster mcast soTimeout to 500 Mar 18, 2010 9:27:45 PM org.apache.catalina.tribes.membership.McastServiceImpl waitForMembers INFO: Sleeping for 1000 milliseconds to establish cluster membership, start level:4 Mar 18, 2010 9:27:46 PM org.apache.catalina.tribes.membership.McastServiceImpl waitForMembers INFO: Done sleeping, membership established, start level:4 Mar 18, 2010 9:27:46 PM org.apache.catalina.tribes.membership.McastServiceImpl waitForMembers INFO: Sleeping for 1000 milliseconds to establish cluster membership, start level:8 Mar 18, 2010 9:27:47 PM org.apache.catalina.tribes.membership.McastServiceImpl waitForMembers INFO: Done sleeping, membership established, start level:8 ** Loading node configuration file. Mar 18, 2010 9:27:47 PM org.apache.tuscany.sca.node.impl.NodeFactoryImpl loadContributions INFO: Loading contribution: file:/Volumes/.../TuscanyServerNode/target/classes/serverconfig.xml Mar 18, 2010 9:27:47 PM org.apache.tuscany.sca.node.impl.NodeFactoryImpl loadContributions INFO: Loading contribution: file:/Volumes/.../TuscanyServerNode/target/classes/serverconfig.xml ** Registering the service in the domain registry. Mar 18, 2010 9:27:47 PM org.apache.tuscany.sca.host.rmi.DefaultRMIHost registerService INFO: RMI service registered: /StandaloneProcessorComponent/StandaloneProcessorService Mar 18, 2010 9:27:47 PM org.apache.tuscany.sca.endpoint.tribes.ReplicatedEndpointRegistry addEndpoint INFO: Add endpoint - (@1343563331)Endpoint: URI = StandaloneProcessorComponent#servicebinding(StandaloneProcessorService/StandaloneProcess orService) ** SCA node finished startign up. Mar 18, 2010 9:27:47 PM org.apache.tuscany.sca.node.launcher.NodeLauncher main INFO: SCA Node is now started. Mar 18, 2010 9:27:47 PM org.apache.tuscany.sca.node.launcher.NodeLauncher main INFO: Press 'q' to quit, 'r' to restart. ** The Standalone Processor service instance is created and initialized. StandaloneProcessorServiceImpl.init

The server starts up but without any clients, there is not much fun.

176

Note that: •

The service node can be stopped by pressing the character “q” followed by enter in the console window.

13.4 Standalone Processor Client The Standalone Processor client is implemented in the TuscanyClientNode project. It will periodically invoke the Standalone Processor service, to which the SCA runtime injects a reference into the client component. This injection takes place automatically, thanks to Tribe or Hazelcast.

13.4.1 StandaloneProcessor Client Interface The Standalone Processor client interface contains two methods. Strictly speaking, the interface could be empty since the client is only invoked by the SCA runtime. package com.ivan.client; /** * Defines the operations available on the Standalone Processor client. * The client interface does not need to be remotable, since the client * will not be called except by the SCA runtime. * Strictly speaking, this interface does not need to contain any methods, * since the client is not called by anyone but the SCA runtime when * it calls the initialization method. * * @author Ivan A Krizsan */ public interface StandaloneProcessorClient { /** * Starts the client. * The client will periodically send requests to the Standalone * Processor service and log the results. */ void start(); /** * Stops the client. */ void stop(); }

177

13.4.2 StandaloneProcessor Client Implementation The Standalone Processor client is responsible for starting and stopping the task that periodically sends requests to the Standalone Processor service. The task is also implemented in the client. Each client has a unique id which it will enclose when sending requests to the service. package com.ivan.client; import import import import import

java.util.Date; java.util.UUID; java.util.concurrent.ScheduledFuture; java.util.concurrent.ScheduledThreadPoolExecutor; java.util.concurrent.TimeUnit;

import import import import import import

org.oasisopen.sca.annotation.Destroy; org.oasisopen.sca.annotation.EagerInit; org.oasisopen.sca.annotation.Init; org.oasisopen.sca.annotation.Reference; org.oasisopen.sca.annotation.Scope; org.oasisopen.sca.annotation.Service;

import com.ivan.service.StandaloneProcessorService; /** * Implements the Standalone Processor service client. * By using the @EagerInit annotation in combination with an @Init * method, the init() method will be called when the node starts up. * Thus this class acts as an entry-point for the application deployed * on the client node. * * @author Ivan A Krizsan */ @Service(StandaloneProcessorClient.class) @Scope("COMPOSITE") @EagerInit public class StandaloneProcessorClientImpl implements StandaloneProcessorClient, Runnable { /* Instance variable(s): */ private StandaloneProcessorService mProcessorService; private String mClientId; private ScheduledThreadPoolExecutor mExcecutor = new ScheduledThreadPoolExecutor(1); private ScheduledFuture mTaskFuture; /** * Initializes the client instance. * Start the task implemented by this class that periodically * calls the service having been injected into the client. */ @Init public void init() { mClientId = UUID.randomUUID().toString(); System.out.println("StandaloneProcessorClientImpl.init: " + "Client id: " + mClientId); mExcecutor.prestartAllCoreThreads(); start(); } /** * Performs clean-up when the instance is to be taken out of service, * shutting down the task that calls the service and the scheduled * executor. */ @Destroy public void cleanUp() { stop(); mExcecutor.shutdownNow(); }

178

/** * Sets the Processor Service to be used by the client to supplied * reference. * * @param inProcessorService Processor Service reference. */ @Reference(name = "processorService") public void setProcessorService( final StandaloneProcessorService inProcessorService) { mProcessorService = inProcessorService; System.out .println("StandaloneProcessorClientImpl.setProcessorService: " + inProcessorService); } /* (non-Javadoc) * @see com.ivan.client.StandaloneProcessorClient#start() */ @Override public void start() { /* Schedule periodic invocation of the Standalone Processor service. */ mTaskFuture = mExcecutor.scheduleAtFixedRate(this, 0, 5, TimeUnit.SECONDS); System.out.println("StandaloneProcessorClientImpl.start: " + "Client id: " + mClientId + " started periodic task."); } /* (non-Javadoc) * @see com.ivan.client.StandaloneProcessorClient#stop() */ @Override public void stop() { /* Stop periodic invocation of the Standalone Processor service. */ if (mTaskFuture != null) { mTaskFuture.cancel(true); } System.out.println("StandaloneProcessorClientImpl.stop: " + "Client id: " + mClientId + " stopped periodic task."); } /* (non-Javadoc) * @see java.lang.Runnable#run() */ @Override public void run() { String theResult = null; System.out.println("StandaloneProcessorClientImpl.run: " + "About to send request to " + mProcessorService); /* * Surround the service call with try-catch, in case the * call fails for some reason, for instance if the * service node is offline. * We do not want the application terminate upon error, but * to continue trying until, hopefully, the service node comes * back online. */ try { theResult = mProcessorService.process(mClientId, "Request sent at: " + new Date()); System.out.println("StandaloneProcessorClientImpl.run: " + "Client id: " + mClientId + " received result: " + theResult); } catch (Throwable theException) { /* Just log the fact that the service could not be invoked. */ System.out.println("StandaloneProcessorClientImpl.run: " +

179

"Request to service failed at " + new Date()); //theException.printStackTrace(); } } }

Note that: •

The scope of the client implementation class is COMPOSITE. As before, this is required since we are also using the @EagerInit annotation.



The client implementation class is annotated with the @EagerInit annotation and contains a method init annotated with the @Init annotation. This combination of annotations causes the init method to be invoked when the node in which the client is deployed is started and thus provides an entry point to the client.



The cleanUp method in the client implementation class is annotated with the @Destroy annotation. This method will be called when the component containing the client is to be taken out of service and gives the client an opportunity to unschedule the periodic invocation of the service.



The client implementation class contains the method setProcessorService that is annotated with the @Reference annotation. A reference to the Standalone Processor service to be used by the client will be injected by the SCA runtime using this method.



Invocation of the Standalone Processor service in the run method is surrounded by a trycatch block that catches all exceptions. This is done in order to catch exceptions occurring, for instance, if the service becomes unavailable. By catching the exceptions, the client is able to continue and, when the service becomes available again, can resume usage of the service.

180

13.4.3 StandaloneProcessor Client Composite The Standalone Processor client composite defines a single SCA component. In src/main/resources, create a file named “TuscanyClientNode.composite” with the following contents: element or a binding element in the element but not both. In this example, the fact that the client and server composites are deployed on two different VMs is to be transparent and thus we use the target attribute. The binding used when communicating with the service node is set programmatically - see the StartProcessorClientNode class! -->

Note that: •

There is a element which uses the target attribute with the name of the Standalone Processor service component as value. While this looks like injection of a component in the same node, Apache Tuscany will inject a reference to the component in question if it has been found in the same domain as this component. See the section on the service's node configuration document for a description of where the domain of a node is configure.



If a element of some kind, for instance , is inserted in the element specifying the binding of the reference, then the target attribute of the element may not be used (as specified in section 4.3.1 in the SCA Assembly v1.1 specification).

181

13.4.4 Node Configuration Document Please refer to the section on the Standalone Processor service's node configuration document for information on node configuration documents. Create a file named “client-config.xml” in src/main/resources with the following contents: element determine the following: uri - Node identifier. domainRegistry - Determine kind and location of the domain registry. For example: tribes://228.0.0.100:50000 for Tribes or tuscany:default for Hazelcast. domain - Domain the node belongs to. -->

13.4.5 Adding the Composite to the SCA Contribution Metadata Document The composite is now to be added to the SCA contribution metadata document. Modify the file “sca-contribution.xml” in the META-INF folder in src/main/resources to look like this:

182

13.4.6 Client Node Starter As with the service node, the client node also needs to be started supplying the node configuration from the previous section. package com.ivan.starter; import org.apache.tuscany.sca.binding.rmi.RMIBinding; import org.apache.tuscany.sca.node.launcher.NodeMain; /** * Starts the node on which a Processor Service client is to run. * * @author Ivan A Krizsan */ public class StartProcessorClientNode { /* Constant(s): */ private final static String[] NODELAUNCHER_ARGS = { "-node", "target/classes/client-config.xml" }; /** * Program entry point. * * @param args Command line arguments. * @throws Exception If error occurred launching node. */ public static void main(String[] args) throws Exception { /* * In this example, using Tribes or Hazelcast, the fact that * the client and server are running in different VMs is supposed * to be transparent to both nodes. * Thus the binding used between the client and service nodes cannot * be specified in the composite file (see comments in the composite * file) but has to be specified by setting a system property * as below. */ String theBindingType = RMIBinding.TYPE.toString(); System.setProperty( "org.apache.tuscany.sca.binding.sca.provider.SCABindingMapper.mappedBinding", theBindingType); /* * Use the Tuscany node launcher to launch the node, supplying * the path to the node configuration file. */ NodeMain.main(NODELAUNCHER_ARGS); } }

183

13.4.7 Running Client Nodes Now that we have completed the client, we can finally look at the client and service working together. First we'll run one service node with one single client node. Running a Single Client

If, after having started the service node, we start one client node and wait about 30 seconds, we'll see console output similar to the following. Again, red text are output from the SCA runtime, blue text are comments added by hand and black text is printed by the program. ** Node configuration file used. Mar 18, 2010 9:04:16 PM org.apache.tuscany.sca.node.launcher.NodeLauncher main INFO: SCA Node configuration: target/classes/client-config.xml Mar 18, 2010 9:04:18 PM org.apache.tuscany.sca.node.launcher.NodeLauncher main INFO: Apache Tuscany SCA Node is starting... ** Name of node and name of the domain the node belongs to. Mar 18, 2010 9:04:18 PM org.apache.tuscany.sca.node.impl.NodeImpl start INFO: Starting node: http://ivan.com/nodes/ClientNode domain: http://ivan.com/domains/test ** Tribes startup and cluster initialization. Mar 18, 2010 9:04:18 PM org.apache.tuscany.sca.endpoint.tribes.AbstractReplicatedMap init INFO: Initializing AbstractReplicatedMap with context name:http://ivan.com/domains/test Mar 18, 2010 9:04:18 PM org.apache.catalina.tribes.transport.ReceiverBase bind INFO: Receiver Server Socket bound to:/127.0.0.1:4001 Mar 18, 2010 9:04:18 PM org.apache.catalina.tribes.membership.McastServiceImpl setupSocket INFO: Attempting to bind the multicast socket to /228.0.0.100:50000 Mar 18, 2010 9:04:18 PM org.apache.catalina.tribes.membership.McastServiceImpl setupSocket INFO: Setting multihome multicast interface to:/fe80:0:0:0:0:0:0:1%1 Mar 18, 2010 9:04:18 PM org.apache.catalina.tribes.membership.McastServiceImpl setupSocket INFO: Setting cluster mcast soTimeout to 500 Mar 18, 2010 9:04:19 PM org.apache.catalina.tribes.membership.McastServiceImpl waitForMembers INFO: Sleeping for 1000 milliseconds to establish cluster membership, start level:4 Mar 18, 2010 9:04:20 PM org.apache.catalina.tribes.membership.McastServiceImpl waitForMembers INFO: Done sleeping, membership established, start level:4 Mar 18, 2010 9:04:20 PM org.apache.catalina.tribes.membership.McastServiceImpl waitForMembers INFO: Sleeping for 1000 milliseconds to establish cluster membership, start level:8 Mar 18, 2010 9:04:21 PM org.apache.catalina.tribes.membership.McastServiceImpl waitForMembers INFO: Done sleeping, membership established, start level:8 ** Loading node configuration file. Mar 18, 2010 9:04:21 PM org.apache.tuscany.sca.node.impl.NodeFactoryImpl loadContributions INFO: Loading contribution: file:/Volumes/SharedImage1/UTVECKLING/EclipseWorkspace/SOA/TuscanyClientNode/target/clas ses/client-config.xml Mar 18, 2010 9:04:21 PM org.apache.tuscany.sca.node.impl.NodeFactoryImpl loadContributions INFO: Loading contribution: file:/Volumes/SharedImage1/UTVECKLING/EclipseWorkspace/SOA/TuscanyClientNode/target/clas ses/client-config.xml Mar 18, 2010 9:04:21 PM org.apache.tuscany.sca.endpoint.tribes.ReplicatedEndpointRegistry addEndpoint INFO: Add endpoint - (@792382546)Endpoint: URI = StandaloneProcessorClient#servicebinding(StandaloneProcessorClient/StandaloneProcessorCli ent) ** Service reference is injected into the client. StandaloneProcessorClientImpl.setProcessorService: [Proxy org.apache.tuscany.sca.core.invocation.impl.JDKInvocationHandler@45d1c3cd] ** Client implementation class is initialized and a client ID is obtained. StandaloneProcessorClientImpl.init: Client id: dacd0b16-a59d-4c11-a4f6-dc78b9a4d2d6 ** Client task that periodically sends requests to the service is started. StandaloneProcessorClientImpl.start: Client id: dacd0b16-a59d-4c11-a4f6-dc78b9a4d2d6 started periodic task.

184

** Client sends request to service. StandaloneProcessorClientImpl.run: About to send request to [Proxy org.apache.tuscany.sca.core.invocation.impl.JDKInvocationHandler@45d1c3 cd] ** SCA node finished starting up. Mar 18, 2010 9:04:21 PM org.apache.tuscany.sca.node.launcher.NodeLauncher main INFO: SCA Node is now started. Mar 18, 2010 9:04:21 PM org.apache.tuscany.sca.node.launcher.NodeLauncher main INFO: Press 'q' to quit, 'r' to restart. ** Tribes matching the reference to the service to an endpoint. Mar 18, 2010 9:04:21 PM org.apache.tuscany.sca.endpoint.tribes.ReplicatedEndpointRegistry findEndpoint INFO: Repeating endpoint reference match - StandaloneProcessorComponent Mar 18, 2010 9:04:22 PM org.apache.catalina.tribes.io.BufferPool getBufferPool INFO: Created a buffer pool with max size:104857600 bytes of type:org.apache.catalina.tribes.io.BufferPool15Impl Mar 18, 2010 9:04:22 PM org.apache.tuscany.sca.endpoint.tribes.ReplicatedEndpointRegistry entryAdded INFO: null Remote endpoint added: (@188108235)Endpoint: URI = StandaloneProcessorComponent#service-binding(StandaloneProcessorService/Stand aloneProcessorService) ** Client receives response from service. StandaloneProcessorClientImpl.run: Client id: dacd0b16-a59d-4c11-a4f6-dc78b9a4d2d6 received result: ACK: Message: Request sent at: Thu Mar 1 8 21:04:21 CST 2010 from client: dacd0b16-a59d-4c11-a4f6-dc78b9a4d2d6 at Thu Mar 18 21:04:22 CST 2010 ** Client sends request to service and receives response. StandaloneProcessorClientImpl.run: About to send request to [Proxy org.apache.tuscany.sca.core.invocation.impl.JDKInvocationHandler@45d1c3 cd] StandaloneProcessorClientImpl.run: Client id: dacd0b16-a59d-4c11-a4f6-dc78b9a4d2d6 received result: ACK: Message: Request sent at: Thu Mar 1 8 21:04:26 CST 2010 from client: dacd0b16-a59d-4c11-a4f6-dc78b9a4d2d6 at Thu Mar 18 21:04:26 CST 2010 ** Client sends request to service and receives response. StandaloneProcessorClientImpl.run: About to send request to [Proxy org.apache.tuscany.sca.core.invocation.impl.JDKInvocationHandler@45d1c3 cd] StandaloneProcessorClientImpl.run: Client id: dacd0b16-a59d-4c11-a4f6-dc78b9a4d2d6 received result: ACK: Message: Request sent at: Thu Mar 1 8 21:04:31 CST 2010 from client: dacd0b16-a59d-4c11-a4f6-dc78b9a4d2d6 at Thu Mar 18 21:04:31 CST 2010 ...

Note that: •

The client node can be stopped by pressing the character “q” followed by enter in the console window.

185

Service Node Log

On the service node, we can see requests from the client logged: ** Service processed a request from a client. StandaloneProcessorServiceImpl.process: Client: d07c58b6-ea6a-47a1-993e-ee8e9046f7f5 sent the message: Request sent at: Thu Mar 18 16:17:06 CST 2010 at Thu Mar 18 16:17:08 CST 2010 ** Tribes registered an endpoint to the service. Mar 18, 2010 4:17:11 PM org.apache.tuscany.sca.endpoint.tribes.ReplicatedEndpointRegistry entryAdded INFO: null Remote endpoint added: (@1438105478)Endpoint: URI = StandaloneProcessorClient#service-binding(StandaloneProcessorClient/Standalo neProcessorClient) ** Service processed a request form a client. StandaloneProcessorServiceImpl.process: Client: d07c58b6-ea6a-47a1-993e-ee8e9046f7f5 sent the message: Request sent at: Thu Mar 18 16:17:11 CST 2010 at Thu Mar 18 16:17:11 CST 2010 ...

Note that: •

Both client and service side logs will contain client ids. On the client side, each node will have one single id but on the service side, multiple client ids may appear, as we will see in the next step.

Running Multiple Clients

The service node is not limited to processing requests from one single client. After having started the service node, two or more client nodes can be started. To conclude this chapter, we'll quickly see an example with three client nodes and one service node running. The client nodes will display approximately the same console output as we have seen before. On the service node, client requests with three different ids will appear: StandaloneProcessorServiceImpl.process: Client: 08df6577-1a2a-40ab-b328-35cb80d296e4 sent the message: Request sent at: Wed Mar 17 21:20:16 CST 2010 at Wed Mar 17 21:20:16 CST 2010 StandaloneProcessorServiceImpl.process: Client: 7c84c0ac-f59d-4728-ad07-8b439b3cace4 sent the message: Request sent at: Wed Mar 17 21:20:17 CST 2010 at Wed Mar 17 21:20:17 CST 2010 StandaloneProcessorServiceImpl.process: Client: 54d81d8a-80f2-4dde-ab4f-b846d73448ec sent the message: Request sent at: Wed Mar 17 21:20:18 CST 2010 at Wed Mar 17 21:20:18 CST 2010

Note that it is not possible to run multiple instances of the service node, since the server address will be bound by the first instance and subsequent instances will not be able to bind tot he same address. Finally, as an exercise to the reader, try the following: •

Start the service node.



Start one or more client nodes.



After one or more successful requests from the client node(s), stop the service node.



Wait some time.



Restart the service node.

186

13.5 Using Hazelcast instead of Tribes To use Hazelcast instead of Tribes, we only need to modify the domain registry in the node configuration documents in both the service and client nodes.

13.5.1 Changing the Domain Registry The domain registry for a node is configured in the element in the node configuration document. In the TuscanyServerNode project, modify the element in the server-config.xml file to look like this:

The value of the domainRegistry attribute in the element is changed to “tuscany:default”. In the TuscanyClientNode project, modify the element in the client-config.xml file to look like this:

If we now start the service node, we will see that Hazelcast is now used: Mar 18, 2010 9:15:44 PM org.apache.tuscany.sca.node.launcher.NodeLauncher main INFO: SCA Node configuration: target/classes/server-config.xml Mar 18, 2010 9:15:46 PM org.apache.tuscany.sca.node.launcher.NodeLauncher main INFO: Apache Tuscany SCA Node is starting... Mar 18, 2010 9:15:46 PM org.apache.tuscany.sca.node.impl.NodeImpl start INFO: Starting node: http://ivan.com/nodes/ServerNode domain: http://ivan.com/domains/test Mar 18, 2010 9:15:47 PM com.hazelcast.impl.Node INFO: Hazelcast 1.8 (20091216) starting at Address[127.0.0.1:14820] Mar 18, 2010 9:15:47 PM com.hazelcast.impl.Node INFO: Copyright (C) 2009 Hazelcast.com Mar 18, 2010 9:15:49 PM com.hazelcast.impl.Node join INFO: Members [1] { Member [127.0.0.1:14820] this } ...

Similar console log will appear when starting the client. Note that for each client started, an additional cluster member will appear: ... Members [2] { Member [127.0.0.1:14820] Member [127.0.0.1:14821] this } ...

187

SCA With Apache Tuscany.pdf

SCA With Apache Tuscany.pdf. SCA With Apache Tuscany.pdf. Open. Extract. Open with. Sign In. Main menu. Displaying SCA With Apache Tuscany.pdf.

3MB Sizes 5 Downloads 168 Views

Recommend Documents

SCA With Apache Tuscany.pdf
SCA With Apache Tuscany.pdf. SCA With Apache Tuscany.pdf. Open. Extract. Open with. Sign In ... Displaying SCA With Apache Tuscany.pdf. Page 1 of 187.

SCA - tgcthailand.com
IBM 6 platform and programming language neutrality in the composite applications which are built using SCA. A developer can focus on assembling composite applications and business goals rather than on the underlying implementation and infrastructure

sca adesh.pdf
Sign in. Page. 1. /. 2. Loading… Page 1 of 2. Page 1 of 2. Page 2 of 2. Page 2 of 2. sca adesh.pdf. sca adesh.pdf. Open. Extract. Open with. Sign In. Main menu.

SCA - tgcthailand.com
Service Component Architecture (SCA) and Service Data Objects (SDO) represent new technology that allows ... updating, and deleting business data regardless of how the data is physically accessed, providing for both static and .... “qualities of se

sca-flyer.pdf
Paid Internship. Those that successfully complete their. training will transition to a one year. paid internship with one of several. leading custom software shops.

November 2016 SCA Newsletter.pdf
5 - Yard Sale Fundraiser. 8 -Youth Engagement Workshop. 10 – Remembrance Day Ceremony. 11 – Remembrance Day (no school). 14 – No school for ...

SCA Resolution - Incidental Business.pdf
... only carry the name and telephone. number of the Incidental Business. g. Other than business cards, no advertising shall indicate the residential address of.

Synthesis_17th SCA _ Philippines.pdf
There was a problem previewing this document. Retrying... Download. Connect more apps... Try one of the apps below to open or edit this item. Synthesis_17th ...

November 2016 SCA Newsletter.pdf
Halloween dance Thursday, October 27th. - Drive Away Hunger food collections were doubled from last. year. SCA collected 665.5 lbs of food. Awesome job! -Terry Fox Run/Walk – SCA raised approximately $1,200.00. towards cancer research. Hurray for S

SCA Service Component Architecture
Siebel is a registered trademark of Siebel Systems, Inc. Sybase is a ...... The basic artifact is the Module, which is the unit of deployment for SCA and which holds.

6. SCA Awareness Form.pdf
Commotio Cordis – concussion of the heart that can occur from being hit in the chest. by a ball, puck, or fist. ♢ Myocarditis – infection/inflammation of the heart, usually caused by a virus. ♢ Recreational/Performance-Enhancing drug use. Ø

Algoritmo SCA + Dx diferencial.pdf
IAM NO ST. NO ELEVACIÓN ST ELEVACIÓN en 2/> DERIVACIONES CONTIGUAS. ST ≥ 0.5 ↑ST en aVR = afectación DA =↑ riesgo/ < 40 años descarta ...

May 2017 SCA Newsletter.pdf
mothers for daughters is that which forevermoreshall bethe United States needs to limit theamount ofimmigrants it lets in every day, week,. month,and year. In trap wetrust.HighHeelsand LowLifes 2001.Whitecollar s01e07.65730397309. Symantecendpoint pr

PDF NoSQL Web Development with Apache ...
PDF NoSQL Web Development with Apache. Cassandra Full eBook. Books detail. Title : PDF NoSQL Web Development with Apache q. Cassandra Full eBook.

Deploying the BIG-IP System v11 with Apache HTTP server
Aug 13, 2013 - Welcome to the F5® and Apache web server (httpd) deployment guide. ... 10. SSL Encryption. 12. Application Firewall Manager (BIG-IP AFM) ...... in the host portion of the IP address (that is, the host portion of its IP address is ...

Developing Web Services with Apache CXF and Axis2
Java web services: •. How to work with both Apache CXF 2.2.x and Axis2 1.5.x. •. How to use caching to create scalable RESTful web services. •. How to encrypt and sign SOAP messages using Rampart. •. How to send user authentication informatio

Developing Web Services with Apache CXF and Axis2
Jan 5, 2010 - How to work with both Apache CXF 2.2.x and Axis2 1.5.x using standard API. (JAX-WS, JAX-RS) as much as possible. •. How to use caching to create scalable RESTful web services. •. How to encrypt and sign SOAP messages using Rampart.