+ All Categories
Home > Documents > Amigo Training Document: Using Web-ServicesAmigo Training Document: Using Web-Services...

Amigo Training Document: Using Web-ServicesAmigo Training Document: Using Web-Services...

Date post: 22-May-2020
Category:
Upload: others
View: 2 times
Download: 0 times
Share this document with a friend
64
IST Amigo Project Amigo Training Document: Using Web-Services IST-2004-004182 Public
Transcript

IST Amigo Project

Amigo Training Document: Using Web-Services

IST-2004-004182

Public

June 2007 Confidential

Amigo IST-2004-004182 1/63

Project Number : IST-004182

Project Title : Amigo

Deliverable Type : Tutorial

Deliverable Number :

Title of Deliverable : Using Amigo Security Services

Nature of Deliverable : Public

Internal Document Number :

Contractual Delivery Date :

Actual Delivery Date :

Contributing WPs :

Version : 1.4

Author(s) : Todor Dimitrov, Edwin Naroska, Jörg Schmalenstroer, Volker Leutnant

Abstract This document gives an introduction on how to use Web Services within the Amigo framework. Web Services are a key technology to maintain interoperability between different deployment domains. This is of major importance, as Amigo is supports .NET as well as Java/OSGi.

Keyword list Web Services, .NET, Java, OSGi, WSDL, Eventing

June 2007 Confidential

Amigo IST-2004-004182 2/63

Table of Contents

Table of Contents................................................................................................... 2

1 Introduction ..................................................................................................... 5

1.1 Prerequisites..............................................................................................................................5

1.2 How to use this document ........................................................................................................5

2 System and software requirements ............................................................... 6

2.1 Installing and configuring Oscar ............................................................................................6

2.1.1 Installation ..........................................................................................................................6

2.1.2 GUI.....................................................................................................................................6

2.1.3 Installing bundles ...............................................................................................................7

2.1.4 Repositories ........................................................................................................................8

2.1.5 Stopping Oscar ...................................................................................................................9

3 Web Services.................................................................................................. 10

3.1 Overview .................................................................................................................................10

3.1.1 Objectives.........................................................................................................................10

3.1.2 Principles ..........................................................................................................................10

3.2 How to develop a simple service: example 1 ........................................................................10

3.2.1 Service implementation .NET ..........................................................................................10

3.2.1.1 Definition of the major server class........................................................................................................... 11 3.2.1.2 Web method “add” .................................................................................................................................... 11 3.2.1.3 Web method “ping”................................................................................................................................... 12 3.2.1.4 Service deployment ................................................................................................................................... 12 3.2.1.5 Complete server code ................................................................................................................................ 12 3.2.1.6 Compiling and starting the server.............................................................................................................. 14 3.2.1.7 Checking the server with a browser........................................................................................................... 14

3.2.2 Service implementation Java / OSGi................................................................................16

3.2.2.1 Definition of the service class interface..................................................................................................... 16 3.2.2.2 Definition of the service class ................................................................................................................... 16 3.2.2.3 Server lifecycle handling........................................................................................................................... 16 3.2.2.4 Service deployment ................................................................................................................................... 17 3.2.2.5 Web Method “add”.................................................................................................................................... 18 3.2.2.6 Web Method “ping” .................................................................................................................................. 18 3.2.2.7 Complete server code ................................................................................................................................ 19 3.2.2.8 Service bundle metadata............................................................................................................................ 20 3.2.2.9 Service bundle manifest ............................................................................................................................ 21 3.2.2.10 Service activator ........................................................................................................................................ 21 3.2.2.11 Installing the service bundle...................................................................................................................... 21

3.2.3 Client implementation .NET ............................................................................................23

3.2.3.1 Importing the web service ......................................................................................................................... 23 3.2.3.2 Definition of the major client class............................................................................................................ 26 3.2.3.3 Looking up the service on the network...................................................................................................... 27 3.2.3.4 Calling the service ..................................................................................................................................... 28

June 2007 Confidential

Amigo IST-2004-004182 3/63

3.2.3.5 Complete client code ................................................................................................................................. 28 3.2.3.6 Executing the client ................................................................................................................................... 30

3.2.4 Client implementation Java / OSGi..................................................................................31

3.2.4.1 Implementation of the client class ............................................................................................................. 31 3.2.4.2 The client core method .............................................................................................................................. 31 3.2.4.3 Complete client code ................................................................................................................................. 33 3.2.4.4 Client bundle metadata .............................................................................................................................. 34 3.2.4.5 Client bundle manifest............................................................................................................................... 35 3.2.4.6 Client activator .......................................................................................................................................... 35 3.2.4.7 Run the client ............................................................................................................................................ 35

3.3 How to develop a service using events: example 2...............................................................38

3.3.1 Service implementation .NET ..........................................................................................39

3.3.1.1 Definition of the major server class........................................................................................................... 39 3.3.1.2 Event firing................................................................................................................................................ 39 3.3.1.3 Defining a Web method setFireState......................................................................................................... 40 3.3.1.4 Service deployment ................................................................................................................................... 40 3.3.1.5 Complete server code ................................................................................................................................ 41 3.3.1.6 Executing the service................................................................................................................................. 42

3.3.2 Service implementation Java / OSGi................................................................................44

3.3.2.1 Definition of the major server class........................................................................................................... 44 3.3.2.2 Firing events.............................................................................................................................................. 44 3.3.2.3 Activating the service ................................................................................................................................ 45 3.3.2.4 Deactivating the service ............................................................................................................................ 46 3.3.2.5 Web method “setFireState” ....................................................................................................................... 46 3.3.2.6 Complete server code ................................................................................................................................ 46 3.3.2.7 Service bundle metadata............................................................................................................................ 48 3.3.2.8 Service bundle manifest ............................................................................................................................ 49 3.3.2.9 Service activator ........................................................................................................................................ 49 3.3.2.10 Run the service .......................................................................................................................................... 49

3.3.3 Client implementation .NET ............................................................................................50

3.3.3.1 Importing the web service ......................................................................................................................... 50 3.3.3.2 Definition of the major client class............................................................................................................ 50 3.3.3.3 Definition of the event handler .................................................................................................................. 50 3.3.3.4 Service lookup........................................................................................................................................... 51 3.3.3.5 The core loop............................................................................................................................................. 51 3.3.3.6 Starting the client....................................................................................................................................... 51 3.3.3.7 Complete client code ................................................................................................................................. 51 3.3.3.8 Running the client ..................................................................................................................................... 53

3.3.4 Client implementation Java / OSGi..................................................................................56

3.3.4.1 Implementation of the event sink class...................................................................................................... 56 3.3.4.2 Implementation of the client class ............................................................................................................. 57 3.3.4.3 The client core method .............................................................................................................................. 57 3.3.4.4 Creating the event sink .............................................................................................................................. 58 3.3.4.5 Deactivating the service ............................................................................................................................ 59 3.3.4.6 Complete client code ................................................................................................................................. 59 3.3.4.7 Client bundle metadata .............................................................................................................................. 60 3.3.4.8 Client bundle manifest............................................................................................................................... 61 3.3.4.9 Client activator .......................................................................................................................................... 61 3.3.4.1 Run the client ............................................................................................................................................ 61

June 2007 Confidential

Amigo IST-2004-004182 4/63

4 Recommended next steps.............................................................................. 63

June 2007 Confidential

Amigo IST-2004-004182 5/63

1 Introduction This document gives an introduction on how to use Web Services within the Amigo framework. Web Services are a key technology to maintain interoperability between different deployment domains. This is of major importance, as Amigo is supports .NET as well as Java/OSGi. The tutorial will show how to write simple web services in .NET as well as in Java. Further, it will show how to write web service clients. For the service, plain remote method calls as well as how to write and consume an eventing service will be explained.

1.1 Prerequisites

Amigo training blocks typically requires some Amigo related as well as non Amigo related skills or knowledge. These are given in the following:

� Non Amigo related prerequisites:

o The reader should be familiar with either Java or C# to easily follow the explanations.

o The reader should be familiar with an integrated development environment (IDE) for the programming language that shall be used. It is suggested to use either Eclipse 3.2 (or higher) for Java or Visual Studio 2005 for C#.

� Amigo related prerequisites: None.

For some general information on the Amigo project please refer to the Amigo project web site at http://www.hitech-projects.com/euprojects/amigo/index.htm.

1.2 How to use this document

This document aims at providing the reader with valuable information about how to use the Amigo middleware. To this end it gives a step by step introduction where explanations are accompanied with short programming assignments. Please proceed to the document section by section and step by step and try to do the assignments. In order to save some typing, an appropriate set of template files is given that can be used to fulfill the assignments. These template files already include a programming frame that must be extend by you in order to complete the assignment. For information on how where to obtain these template files please refer to Chapter 2.

The example below shows how a piece of source code is printed. Note that code blocks are embedded in a box in order to easily separate them from normal text:

using (Service service = new AddService(securedService)) {

WebServer.AddWebService(serviceLocation, service);

Console.WriteLine("Sample service 2 running." +

Environment.NewLine +

"Press <return> to quit");

Console.ReadLine();

}

If code is printed in normal text, it is shown in italic.

June 2007 Confidential

Amigo IST-2004-004182 6/63

2 System and software requirements For the tutorial you need a PC compatible system with the following properties:

� PC running Windows XP, Vista or Windows Server 2003.

� A working network connection if server and client applications shall be executed on different machines.

For the Java parts of the tutorial you need an installed version of the Java Development Kit, including the javac compiler and the jar tools (Java Version 1.5.x).

If your are using Java, then the following additional software is needed:

� Java Software Development Kit. This is a general package needed for Java software development. The package is available from http://java.sun.com.

� Oscar. Oscar is a open source OSGi framework. This software is also available from http://amigo.gforge.inria.fr/obr/tools/.

If you are using .NET (C#) as development language, then it is recommended to use Visual Studio from Microsoft as development framework.

Please note that it may be required to de-activated the firewall on the computer that shall run the test server.

A set of template files have been prepared in order to help you running the practical parts of this tutorial. It is recommended that you use this templates. A package that includes this template along with some other files an be downloaded from https://gforge.inria.fr/frs/?group_id=160. Note that all path names that are referred to in this document are relative to the folder the package has been extracted to. Note further, that there is a separate directory names “Code_Solution” which contains the complete programs.

2.1 Installing and configuring Oscar

Oscar is an open source implementation of the Open Services Gateway Initiative (OSGi) framework specification, which is used by many Amigo services. The main components of the CMS system are based on Oscar bundles, which are available through the Online Bundle Repository (OBR) of Amigo.

2.1.1 Installation

Oscar does not have a special installation routine, so the full software package is available as a zip-file (e.g. http://amigo.gforge.inria.fr/obr/tools/oscar_j2se.zip) and has to be extracted to a directory of your choice. If you are connected to the Internet through a proxy server, you’ll have to add the appropriate proxy settings to the “system.properties” file located in the “lib” sub-directory. You’ll find the start scripts “oscar.bat” (Windows) and “oscar.sh” (Linux1) in the main directory. After calling the start script you are asked for a profile name (enter CMSTutorial, for example), which defines the name of the current Oscar profile. You can use profile names for separating different configurations.

2.1.2 GUI

Below you see a screenshot of a running version of Oscar. On the left side you find the three items:

1If “oscar.sh” is not executable use “chmod u+x oscar.sh” to change the file attribute.

June 2007 Confidential

Amigo IST-2004-004182 7/63

� Bundle List: A list of all installed bundles, showing Id , State and Location; additionally, local URLs can be entered for installing bundles (e.g.: file:///home/user/amigo_bundle.jar)

� Shell: Command shell

� OBR: A list of all bundles available from the repositories, this can be online or offline repositories as defined in /lib/bundle.properties.

Use the OBR item to install bundles from the repository and the Bundle List item for starting and stopping installed bundles.

2.1.3 Installing bundles

Now start installing bundles to the Oscar platform:

1. Click on the item “OBR”

2. Select “amigo_core” and press “Start All” Installed dependencies:

� log4j

� Service Binder

3. Select “amigo_ksoap_binding” and press “Start All”

4. Select “amigo_ksoap_export” and press “Start All” Installed dependencies:

� Servlet

� HTTP Service (+ Amigo mods)

5. Select “amigo_wsdiscovery” and press “Start All”

June 2007 Confidential

Amigo IST-2004-004182 8/63

Now click on the “Bundle list” to check if all bundles are installed. You should get the following list:

If during the bundle installation the above listed dependencies are not installed (an error will occur and pop up), please install the dependencies prior to the bundles by hand.

In the “OBR” list you also find several other software bundles, which you can use for your services. Now we should start with compiling bundles and installing them.

2.1.4 Repositories

The URL of the repositories is defined in “lib/bundle.properties”. The standard Amigo repositories are defined by the following copy of the “bundle.properties” file. You also find the address of the actual repositories on the item OBR.

# all known repositories oscar.repository.url=\ http://amigo.gforge.inria.fr/obr/tools/oscar-repository.xml \ http://amigo.gforge.inria.fr/obr/v2/repository.xml

# the port on which the http server will listen # if you have several OSGi platforms on the same machines change the port number org.osgi.service.http.port=8080

June 2007 Confidential

Amigo IST-2004-004182 9/63

2.1.5 Stopping Oscar

You can stop Oscar by typing “shutdown” in the Oscar shell. If nothing happens you can press “Ctrl+c” for killing Oscar, but try to shutdown Oscar gently through the “shutdown”-command beforehand.

June 2007 Confidential

Amigo IST-2004-004182 10/63

3 Web Services

3.1 Overview

3.1.1 Objectives

This tutorial is not intended to serve as an in-depth introduction into Web Services. Instead it will briefly show how to use the Amigo framework to write and consume web services. For an in depth introduction to Web Services please refer to external literature.

In principle, Web Services are a platform independent way to do remote calls from one computer to another. It exploits web technology in order to ensure interoperability between different machine types. As a result, it does not matter which kind of machine is used at server or client side. Further, appropriate support to offer and consume Web Services are available for many programming languages, making this approach nearly language neutral.

Finally, another interesting option is to use eventing based on Web Services. For eventing, a server informs a client about a specific event by calling a remote event handler on the client. Note that this is an asynchronous mechanism, i.e., the client does not know when an event may occurs. In order to be notified by server, the client must register to receive events.

3.1.2 Principles

In fact, building a Web Services and calling a remote method requires a lot of technology in order to create the appropriate messages that are send between client and server. Due to interoperability reasons these messages are encoded using common standardized mechanisms. Fortunately, for both, .NET as well as Java there are libraries available which do major part of the processing so that the tidy and complex operations that are actually required are in fact hidden and invisible to the programmer. While users of Web Services are in fact do not need to know all the details about the embedded mechanisms that are used, it is helpful to have a basic understand about the mechanism that are applied.

Basically, a Web Service consists of a web server that is running and accepting incoming calls and requests. All messages that are send between server and client are encoded using web technology. In order to locate a service, a client can lookup the network using special discovery messages. Form these messages the client learns what type of services are available at which host. Further, it can retrieve information about the methods that can be called and what parameters are accepted by the methods as well as the type of the return value. Note that there is a special language to describe all this information.

3.2 How to develop a simple service: example 1

Now that we very briefly described what Web Services are, let us try to develop our own simple Web Service. In the following, we will describe the necessary steps to develop a service using the Amigo framework. We do this by walking through the code sample that is installed as part of tutorial source code.

3.2.1 Service implementation .NET

As a first step, we are going to develop a very simple Web Service. This service shall provide two methods. One methods that takes two integer parameters and returns the sum of it. The other method takes a string and returns another string. The .NET codes resides in Web Services\Code\DOT_NET\Server.

June 2007 Confidential

Amigo IST-2004-004182 11/63

3.2.1.1 Definition of the major server class

The following C# example shows the external libraries that is used by our code. Note that especially EMIC.WebServer, EMIC.WSDiscovery.Server, and EMIC.WSAddressing are needed to process the web service related messaged and to provide the code with mechanisms to run and control a web server.

using System;

using System.Collections.Generic;

using System.Text;

using System.Web.Services;

using EMIC.WebServer;

using EMIC.WSDiscovery.Server;

using EMIC.WSAddressing;

The lines below define a new namespace (called Server) that is going to hold our server code. Within the square brackets [] we define a set of parameters that shall be asscicated with the Web Service. In detail, the scope, type and namespace of the the service are determined. These definitions are placed right before definition of the acual class that is going to implement the service.

namespace Server

{

[ScopeAttribute("urn:AmigoDemo")]

[TypeAttribute("MySimpleAddService")]

[WebService(Namespace = "http://amigoproject.org")]

public class Service : DiscoverableService

{

Service () { }

...

}

}

Note that we added a simple contructor for the class. Note further, that the class Service is derived from DiscoverableService! Within class Server we now build our first simple Web Service.

3.2.1.2 Web method “add”

To define a method that can be exported as a Web Service, a special token [WebMethod] is placed before the actual method declaration according to the following teamplate:

[WebMethod]

Public Return_Type Method_Name(parameter …)

{

}

The keyword [WebMethod] informs the system that the following method shall be made available as Web Service.

Now, within class Server define a method named add that takes two integers and return the integer sum of the parameter.

The solution is:

[WebMethod]

public int add(int a, int b)

{

int result = a + b;

Console.WriteLine("Web method \"add\" called with parameter " +

June 2007 Confidential

Amigo IST-2004-004182 12/63

a.ToString () + " and " + b.ToString () + ".");

Console.WriteLine("Will return " + result.ToString () + ".");

return result;

}

Note that the console output will be printed at server and not of at client side:

3.2.1.3 Web method “ping”

The second method that shall be added, takes the parameter string and returns a string. Solution:

[WebMethod]

public string ping(string parameter)

{

Console.WriteLine("Web method \"ping\" called with parameter " +

parameter);

return "Method \"ping\" of service MySimpleAddService "

"was called with parameter " + parameter;

}

3.2.1.4 Service deployment

The only thing that is left now is to write some code that creates and installs our new service on the web server. Here, this is done by an additional class Program that also provides the main rountine. Note that this new class is also defined within the namspace Server:

class Program

{

static void Main(string[] args)

{

...

}

}

Within the main method, the service is created as well as a web server. Here, the web server is assigned port 8080. After starting of the web server, our new service is associated with the web server. Note that the first parameter of method AddWebService is the name of a virtual directoy. The web server will use this virtually direcory to “store” all the service related web pages.

Service service = new Service();

WebServer.Port = 8080;

WebServer.Start();

WebServer.AddWebService("MyService", service);

Console.WriteLine("The MyService Web Service is now" +

" running and ready to be discovered (also WSDL)" +

"as http://" + System.Net.Dns.GetHostName() + ":" +

WebServer.Port + "/MyService/service.asmx");

Console.WriteLine("Press ENTER to close application.");

Console.ReadLine();

3.2.1.5 Complete server code

For your convenience, the complete server code is printed below. Note that the code is quite compact, instead of the complex and tedious mechanism that are in fact applied during operation.

June 2007 Confidential

Amigo IST-2004-004182 13/63

using System;

using System.Collections.Generic;

using System.Text;

using System.Web.Services;

using EMIC.WebServer;

using EMIC.WSDiscovery.Server;

using EMIC.WSAddressing;

namespace Server

{

[ScopeAttribute("urn:AmigoDemo")]

[TypeAttribute("MySimpleAddService")]

[WebService(Namespace = "http://amigoproject.org")]

public class Service : DiscoverableService

{

public Service() { }

[WebMethod]

public int add(int a, int b)

{

int result = a + b;

Console.WriteLine("Web method \"add\" called with parameter " +

a.ToString () + " and " + b.ToString () + ".");

Console.WriteLine("Will return " + result.ToString () + ".");

return result;

}

[WebMethod]

public string ping(string parameter)

{

Console.WriteLine("Web method \"ping\" called with parameter " +

parameter);

return "Method \"ping\" of service MySimpleAddService "

"was called with parameter " + parameter;

}

}

class Program

{

static void Main(string[] args)

{

Service service = new Service();

WebServer.Port = 8080;

WebServer.Start();

WebServer.AddWebService("MyService", service);

Console.WriteLine("The MyService Web Service is now" +

" running and ready to be discovered (also WSDL)" +

"as http://" + System.Net.Dns.GetHostName() + ":" +

WebServer.Port + "/MyService/service.asmx");

Console.WriteLine("Press ENTER to close application.");

Console.ReadLine();

}

}

}

June 2007 Confidential

Amigo IST-2004-004182 14/63

3.2.1.6 Compiling and starting the server

Next, compile the server code and start it. It should print a message like below on the screen. Note that the server prints an URL to the screen that can be used with a browser to check whether the service is in fact running and available.

3.2.1.7 Checking the server with a browser

In order to verify whether the server is running and the service is operable, please copy the URL that is printed in the console to a browser address field and press return (or click on the re-load button). If everything runs fine, something like the following window should how up (see below). Note that the text in the window is an XML encoded description of the services. This description can now be retrieved by a client in order to find out what kind of methods and features are associated with the service.

June 2007 Confidential

Amigo IST-2004-004182 15/63

June 2007 Confidential

Amigo IST-2004-004182 16/63

3.2.2 Service implementation Java / OSGi

For implementing the service in Java please note that the service must run under control of OSGi. As a result, the service (as well as the client) must be implemented as an OSGi bundle.

Note that for the Java implementation, any text output is not printed to a separate console bust send to a logger which can then be used to display the messages on the screen. Typically, the logger will print the various messages to the console window of OSGi.

The code for the server component should go to file Web Services\Code\Java\tutorial_server\src\de\ims\fraunhofer\amigo\tutorial\server\ServerComponent.java.

3.2.2.1 Definition of the service class interface

In order to define the service, we first have to provide an interface definition for it:

pacpacpacpackagekagekagekage de.ims.fraunhofer.amigo.tutorial.server;

publicpublicpublicpublic interfaceinterfaceinterfaceinterface IServerInterface {

intintintint add(intintintint a, intintintint b);

StringStringStringString ping(String parameter);

}

This definition should go to file Web Services\Code\Java\tutorial_server\src\de\ims\fraunhofer\amigo\tutorial\server\IServerInterface.java.

3.2.2.2 Definition of the service class

In order to implement the Web Service a lot of packages can be exploited that ease code development significantly. As a result, the server class definition must begin with the following package imports:

packagepackagepackagepackage de.ims.fraunhofer.amigo.tutorial.server; importimportimportimport org.apache.log4j.Logger; importimportimportimport org.ungoverned.gravity.servicebinder.Lifecycle; importimportimportimport com.francetelecom.amigo.core.AmigoException; importimportimportimport com.francetelecom.amigo.core.AmigoExportedService; impoimpoimpoimportrtrtrt com.francetelecom.amigo.core.AmigoLdapLookup; importimportimportimport com.francetelecom.amigo.core.AmigoReference; importimportimportimport com.francetelecom.amigo.core.AmigoServiceExporter; importimportimportimport com.francetelecom.amigo.core.ExportedMethod;

Next, the actual server class is defined. Here, we name it ServerComponent. This class implements interfaces Lifecycle and IServerInterface:

publicpublicpublicpublic classclassclassclass ServerComponent implementsimplementsimplementsimplements Lifecycle, IServerInterface { … }

Note that all the following server code will belong to this class.

3.2.2.3 Server lifecycle handling

During server life cycle, the server must be started, stopped and other events might happen. To this end, the following definitions are needed. For the scope of the tutorial it is not required that the reader is familiar with what each declaration is good fore. More details can be obtained from the corresponding documentation:

June 2007 Confidential

Amigo IST-2004-004182 17/63

privateprivateprivateprivate AmigoServiceExporter m_serviceExporter; privateprivateprivateprivate AmigoExportedService m_myService; privateprivateprivateprivate AmigoLdapLookup m_lookup; privateprivateprivateprivate finalfinalfinalfinal Logger m_logger = Logger.getLogger(ServerComponent.classclassclassclass); publicpublicpublicpublic voidvoidvoidvoid bindLookupService(AmigoLdapLookup lookup) { m_lookup = lookup; } publicpublicpublicpublic voidvoidvoidvoid unbindLookupService(AmigoLdapLookup lookup) { m_lookup = nullnullnullnull; } publicpublicpublicpublic voidvoidvoidvoid bindServiceExporter(AmigoServiceExporter serviceExporter) { m_serviceExporter = serviceExporter; } publicpublicpublicpublic voidvoidvoidvoid unbindServiceExporter( AmigoServiceExporter serviceExporter) { m_serviceExporter = nullnullnullnull; } …

3.2.2.4 Service deployment

To start up the actual service, we must define a method called activate within the ServerComponent class:

publicpublicpublicpublic voidvoidvoidvoid activate() { … }

Within this method, define the service and specify some properties that are needed:

m_myService = m_serviceExporter.createService(thisthisthisthis); m_myService.addProperty("namespace", "http://amigoproject.org"); m_myService.addProperty("oid", "Server");

The next step is to prepare exporting the methods that shall be available from outside:

finalfinalfinalfinal ExportedMethod methodAdd = newnewnewnew ExportedMethod("add", newnewnewnew String[] { "a", "b" }); finalfinalfinalfinal ExportedMethod methodPing = newnewnewnew ExportedMethod("ping", newnewnewnew String[] { "parameter" });

The actual export is done by the exportMethods method from the service. In order to deal with errors, embed this export attempt within a try-catch block:

trytrytrytry { m_myService.exportMethods(AmigoReference.DEFAULT, newnewnewnew ExportedMethod[] { methodAdd, methodPing }); m_logger.info("Exported service at address " + m_myService.getReference()); } catchcatchcatchcatch (Exception ex) { m_logger.error("Unable to export service!", ex); throwthrowthrowthrow newnewnewnew RuntimeException(ex); }

Note that the URL of the service is printed to the log window.

Now, add the service type and scope to the service. Note that these are very important for service lookup. Hence, be careful not to misspell the text. Otherwise, your client will not be able to find your service.

m_myService.addProperty("ServiceType", "MySimpleAddService"); m_myService.addProperty("Scope", "urn:AmigoDemo");

June 2007 Confidential

Amigo IST-2004-004182 18/63

Finally, register the service:

trytrytrytry { m_lookup.registerService(m_myService); m_logger.info("Service registered!"); } catchcatchcatchcatch (AmigoException ex) { m_logger.error("Unable to register service!", ex); throwthrowthrowthrow newnewnewnew RuntimeException(ex); }

Additionally, some code is needed to gracefully shutdown the service. Note that the Java service is running in OSGi, hence, some more attention must be paid to life cycle handling compared to the stand alone programs that are executed in case of .NET.

The shutdown code is embedded in method deactivate:

publicpublicpublicpublic voidvoidvoidvoid deactivate() { ifififif (m_myService != nullnullnullnull) { ifififif (m_lookup != nullnullnullnull) { trytrytrytry { m_lookup.unregisterService(m_myService); m_logger.info("Service unregistered!"); } catchcatchcatchcatch (AmigoException ex) { m_logger.error( "Unable to unregister service!", ex); } } trytrytrytry { m_myService.unexport(); } catchcatchcatchcatch (AmigoException ex) { m_logger.error("Unable to unexport service!", ex); } } }

Basically, the code un-registers the service with the lookup service and shuts down the service thereafter.

3.2.2.5 Web Method “add”

What is still missing in our server code are the actual implementation of methods add and ping. In fact, these methods are defined like any other plain class method. Define a method named add that takes two int parameter a and b and returns the sum of the parameter (the sum is of type integer as well).

Here is method add:

publicpublicpublicpublic intintintint add(intintintint a, intintintint b) { finalfinalfinalfinal intintintint result = a + b; m_logger.info("Web method \"add\" called with parameter " + a + " and " + b + "."); m_logger.info("Will return " + result + "."); returnreturnreturnreturn result; }

3.2.2.6 Web Method “ping”

Next, define method ping. It takes a string parameter and returns a string: Here is method ping: publicpublicpublicpublic String ping(String parameter) { m_logger.info("Web method \"ping\" called with parameter " + parameter); returnreturnreturnreturn "Method \"ping\" of service MySimpleAddService " + "was called with parameter " + parameter; }

June 2007 Confidential

Amigo IST-2004-004182 19/63

Note that the method is just a normal local method.

3.2.2.7 Complete server code

For your convenience, the complete server code is printed below. Note that the code is quite compact, instead of the complex and tedious mechanism that are in fact applied during operation.

packagepackagepackagepackage de.ims.fraunhofer.amigo.tutorial.server; importimportimportimport org.apache.log4j.Logger; importimportimportimport org.ungoverned.gravity.servicebinder.Lifecycle; importimportimportimport com.francetelecom.amigo.core.AmigoException; importimportimportimport com.francetelecom.amigo.core.AmigoExportedService; importimportimportimport com.francetelecom.amigo.core.AmigoLdapLookup; importimportimportimport com.francetelecom.amigo.core.AmigoReference; importimportimportimport com.francetelecom.amigo.core.AmigoServiceExporter; importimportimportimport com.francetelecom.amigo.core.ExportedMethod; publicpublicpublicpublic classclassclassclass ServerComponent implementsimplementsimplementsimplements Lifecycle, IServerInterface { privateprivateprivateprivate AmigoServiceExporter m_serviceExporter; privateprivateprivateprivate AmigoExportedService m_myService; privateprivateprivateprivate AmigoLdapLookup m_lookup; privateprivateprivateprivate finalfinalfinalfinal Logger m_logger = Logger.getLogger(ServerComponent.classclassclassclass); publicpublicpublicpublic voidvoidvoidvoid bindLookupService(AmigoLdapLookup lookup) { m_lookup = lookup; } publicpublicpublicpublic voidvoidvoidvoid unbindLookupService(AmigoLdapLookup lookup) { m_lookup = nullnullnullnull; } publicpublicpublicpublic voidvoidvoidvoid bindServiceExporter(AmigoServiceExporter serviceExporter) { m_serviceExporter = serviceExporter; } publicpublicpublicpublic voidvoidvoidvoid unbindServiceExporter( AmigoServiceExporter serviceExporter) { m_serviceExporter = nullnullnullnull; } publicpublicpublicpublic voidvoidvoidvoid activate() { m_myService = m_serviceExporter.createService(thisthisthisthis); m_myService.addProperty("namespace", "http://amigoproject.org"); m_myService.addProperty("oid", "Server"); finalfinalfinalfinal ExportedMethod methodAdd = newnewnewnew ExportedMethod("add", newnewnewnew String[] { "a", "b" }); finalfinalfinalfinal ExportedMethod methodPing = newnewnewnew ExportedMethod("ping", newnewnewnew String[] { "parameter" }); trytrytrytry { m_myService.exportMethods(AmigoReference.DEFAULT, newnewnewnew ExportedMethod[] { methodAdd, methodPing }); m_logger.info("Exported service at address " + m_myService.getReference()); } catchcatchcatchcatch (Exception ex) { m_logger.error("Unable to export service!", ex); throwthrowthrowthrow newnewnewnew RuntimeException(ex); } m_myService.addProperty("ServiceType", "MySimpleAddService"); m_myService.addProperty("Scope", "urn:AmigoDemo"); trytrytrytry { m_lookup.registerService(m_myService); m_logger.info("Service registered!"); } catchcatchcatchcatch (AmigoException ex) { m_logger.error("Unable to register service!", ex); throwthrowthrowthrow newnewnewnew RuntimeException(ex); }

June 2007 Confidential

Amigo IST-2004-004182 20/63

} publicpublicpublicpublic voidvoidvoidvoid deactivate() { ifififif (m_myService != nullnullnullnull) { ifififif (m_lookup != nullnullnullnull) { trytrytrytry { m_lookup.unregisterService(m_myService); m_logger.info("Service unregistered!"); } catchcatchcatchcatch (AmigoException ex) { m_logger.error( "Unable to unregister service!", ex); } } trytrytrytry { m_myService.unexport(); } catchcatchcatchcatch (AmigoException ex) { m_logger.error("Unable to unexport service!", ex); } } } publicpublicpublicpublic intintintint add(intintintint a, intintintint b) { finalfinalfinalfinal intintintint result = a + b; m_logger.info("Web method \"add\" called with parameter " + a + " and " + b + "."); m_logger.info("Will return " + result + "."); returnreturnreturnreturn result; } publicpublicpublicpublic String ping(String parameter) { m_logger.info("Web method \"ping\" called with parameter " + parameter); returnreturnreturnreturn "Method \"ping\" of service MySimpleAddService " + "was called with parameter " + parameter; } }

3.2.2.8 Service bundle metadata

In order to run this software as an OSGi bundle, some information is needed that describes some dependencies of the software:

<?xml version="1.0" encoding="UTF-8" ?> <bundle> <component class="de.ims.fraunhofer.amigo.tutorial.server.ServerComponent"> <requires service="com.francetelecom.amigo.core.AmigoServiceExporter" filter="" cardinality="1..1" policy="dynamic"

bind-method="bindServiceExporter" unbind-method="unbindServiceExporter" /> <requires service="com.francetelecom.amigo.core.AmigoLdapLookup" filter="" cardinality="1..1" policy="dynamic" bind-method="bindLookupService" unbind-method="unbindLookupService" /> </component> </bundle>

Note that this data structure states that there are two other services required in order to run our Web Service. The first is the AmigoServicerExporter, the other is AmigoLdapLookup. Further, the metadata also defines which routines shall be called in case of one of these services becomes available or unavailable.

The XML data is stored in Web Services\Code\Java\tutorial_server\res\metadata.xml.

June 2007 Confidential

Amigo IST-2004-004182 21/63

3.2.2.9 Service bundle manifest

Each OSGi bundle requires a “manifest”. This a plain text file that stores some general information about the bundle. For example, it also includes the name of the metadata file:

Manifest-Version: 1.0

Bundle-Description: Bundle registers a simple Amigo service

Bundle-Name: Tutorial-Server

Bundle-Activator: de.ims.fraunhofer.amigo.tutorial.server.AmigoCoreActivator

Created-By: Fraunhofer IMS

Bundle-Vendor: Fraunhofer IMS

Import-Package: com.francetelecom.amigo.core,

org.ungoverned.gravity.servicebinder,

org.apache.log4j

Bundle-Copyright: Less General Public License

Bundle-ClassPath: .

Metadata-location: metadata.xml

This manifest (named manifest.mf) is stored in the same directory as the metadata file.

3.2.2.10 Service activator

To start or service as a OSGi bundle an activator class is needed. This class will be used by OSGi to start the service. Here it is located in Web Services\Code\Java\tutorial_server\src\de\ims\fraunhofer\amigo\tutorial\server\AmigoCoreActivator.java.

packagepackagepackagepackage de.ims.fraunhofer.amigo.tutorial.server; importimportimportimport org.apache.log4j.Logger; importimportimportimport org.osgi.framework.BundleContext; importimportimportimport org.ungoverned.gravity.servicebinder.GenericActivator; publicpublicpublicpublic classclassclassclass AmigoCoreActivator extendsextendsextendsextends GenericActivator { privateprivateprivateprivate finalfinalfinalfinal Logger m_logger = Logger.getLogger(AmigoCoreActivator.classclassclassclass); publicpublicpublicpublic voidvoidvoidvoid start(BundleContext context) throwsthrowsthrowsthrows Exception { supersupersupersuper.start(context); m_logger.info("Server started."); } }

3.2.2.11 Installing the service bundle

In order to run the server, it must be loaded into Oscar and started. To this end, start Oscar and change to the Bundle List menu. Then type in the path to the service bundle. Note that the path must start with file:/// in order to load it from the file system. Then, press install and finally start. As a result, some log text will be printed on the console window of Oscar.

The next pictures shows the Oscar GUI shell before the service has been loaded.

June 2007 Confidential

Amigo IST-2004-004182 22/63

The following picture shows the Oscar GUI shell after the service has been started (see location named “Tutorial-Server” at the bottom of the list).

June 2007 Confidential

Amigo IST-2004-004182 23/63

3.2.3 Client implementation .NET

Now that we successfully designed the first simple Web Service our next step is to write an appropriate client application. The purpose of the client program is to lookup the service on the network and to call both of the exported Web methods. Note that a major step during client development is the lookup process as we typically in an Amigo network do not know in advance on which machine a specific service is executed. Moreover, this may even change during time.

An important step for client programming is to tell the compiler how the interface for the Web method looks like. As we will seen below, the call to the remote method looks like a call to a locally defines method. To make this work, we need a representation of the remote service on the local machine. This representation is called “proxy” in the following. The proxy takes the method calls that are actually targeting at the remote service and forwards them to the remote machine. Note that this mechanism are hidden from the caller. That is, with the exception of the time it may take to proceed the call, there is no difference for the caller.

In order to be able to call the appropriate methods on the proxy, the proxy must have the same signature (interface) as the original methods of the remote service. Hence, we need to generate the proxy code for each service individually. Fortunately, we do not have to do this by ourselves. Instead, we can use the appropriate tools that come with the programming frameworks.

3.2.3.1 Importing the web service

In order to generate the proxy code for .NET, the service must be running on the machine. To this end, go the “Web Services\Code\DOT_NET\Server\bin\Release” folder and double-clock on the “Server.exe” icon. This will bring up the server as a console application. In the console, the server will print the URL that can be used to query information about the service:

Copy or write down the URL (here: http://AV73/MyService/service.asmx). Do not close the window!

Note that the service is running and we know its URL turn to the IDE. Select “Web Reference” in the project explorer window and activate the context menu. Next, select “Add Web Reference”.

June 2007 Confidential

Amigo IST-2004-004182 24/63

Thus will bring up a window that can be use to browse the available web services. Paste the URL of your service into the URL bar and activate the “green arrow”. As a result, the service browser will parse the information provided by the Service and display a short summary of it in the main window.

June 2007 Confidential

Amigo IST-2004-004182 25/63

The window below shows the results of the service query:

June 2007 Confidential

Amigo IST-2004-004182 26/63

Next, select a name for the web reference. Please use AmigoDemo as reference name. Note that his name will be used within the client code to access the proxy.

Finally, create the appropriate Web Reference entry in the visual studio IDE. As a result, now you can access the interfaces of the service as if it were a local class.

3.2.3.2 Definition of the major client class

The following C# example shows the external libraries that are used by our code. Note that especially EMIC.WSDiscovery. and EMIC.WSDiscoveryClient are needed to process the web service related messaged as well as to control the lookup process.

using System;

using System.Collections.Generic;

using System.Text;

using System.Web.Services;

using EMIC.WSDiscovery;

using EMIC.WSDiscovery.Client;

With the code shown below we define a new namespace (called Client) that is going to hold our client code. The actual class that implements the client is called Program. In our case, we only need a main rountine that processes the required lookup and calling operation:

namespace Client

{

class Program

{

static void Main(string[] args)

June 2007 Confidential

Amigo IST-2004-004182 27/63

{

...

}

}

}

3.2.3.3 Looking up the service on the network

The first step is to find the service on the network. In our case, the service is running on the same machine as the client. However, this will typically not the case in real world.

For lookup, we first create a probe client named probeClient that is intended to collect and filter all the available services on the network. The next step is to define what we are looking for. In this case, we define the service scope as well as the service type using the methods AddServiceScope and AddServiceType of the probe client. Note that we may use different parameters for lookup. Note further, that we must be prepared that there may be more than a single service that matches our profile.

ProbeClient probeClient = new ProbeClient();

Console.WriteLine("Please wait, discovering services ...");

probeClient.AddServiceScope(new ServiceScope("urn:AmigoDemo"));

probeClient.AddServiceType(

new ServiceType("MySimpleAddService"));

After specifying what we are looking for, the actual lookup process is initiated. It is important to give the lookup process some time as this is a rather time consuming procedure involving broadcasts that are send over the network. In this case we wait for 10000 milliseconds (= 10 seconds).

probeClient.SendProbe();

System.Threading.Thread.Sleep(10000);

After waiting for 10 seconds, the probe client is queried for the number of appropriate matches. The result is printed on the screen.

if (probeClient.ProbeMatches != null)

Console.WriteLine("Found " +

probeClient.ProbeMatches.Count.ToString() +

" matches.");

Next, we process the found services. Note that there may be the case that no appropriate services are found (possible reasons: service not started, service type or scope names do not match, network not available, service too busy to reply, etc.). Hence, the first step is always to check whether there has been found a service at all:

if (probeClient.ProbeMatches != null &&

probeClient.ProbeMatches.Count > 0)

{

Console.WriteLine("Found " + probeClient.ProbeMatches.Count +

" service(s)!");

for (int index = 0;

index < probeClient.ProbeMatches.Count;

index++)

Console.WriteLine("Server " + index + " has address: " +

probeClient.ProbeMatches[index].FirstXaddr);

June 2007 Confidential

Amigo IST-2004-004182 28/63

To see what service we found, the code above cycles over the matches and prints the network addresses of the machines the services are running on.

3.2.3.4 Calling the service

As there may be more than a single service on the network, the next step is to choose the appropriate service that is going to be used. Please write a piece of code that ask the user which service to call.

Solution:

Console.Write("Please choose a server to call: ");

int server = int.Parse(Console.ReadLine());

Once this selection has been done, the next step is to generate a proxy for the service that in fact forwards all the local calls to the appropriate remote service.

AmigoDemo.Service proxy = new AmigoDemo.Service();

proxy.Url =

probeClient.ProbeMatches[server].FirstXaddr.AbsoluteUri;

Note that we have to select the appropriate match from the list of matches that were returned by the probe client. Note further, that in the code above we wrote AmigoDemo as namespace of the proxy. This has been chosen during the import of the service (see Section 3.2.3.1).

To call the service we just use the proxy which has the same methods as the original service on the remote host.

int result = proxy.add(1, 3);

Console.WriteLine("Server replies: 1 + 3 = " +

result.ToString());

Console.WriteLine(proxy.ping(System.Net.Dns.GetHostName()));

Note that the proxy will collect the arguments, encode them, send them over the remote host where they are decoded and passed to the appropriate method. The result value is encoded again and send back to the proxy which decodes it and returns it to the caller.

The only thing left is to output an error message in case the client did not find an appropriate service and to wait for the user to press “enter”:

}

else

{

Console.WriteLine("No service found!");

}

Console.ReadLine();

This completes the code for class Program.

3.2.3.5 Complete client code

For your convenience, the complete client code is printed below.

using System;

using System.Collections.Generic;

using System.Text;

using System.Web.Services;

using EMIC.WSDiscovery;

using EMIC.WSDiscovery.Client;

June 2007 Confidential

Amigo IST-2004-004182 29/63

namespace Client

{

class Program

{

static void Main(string[] args)

{

ProbeClient probeClient = new ProbeClient();

Console.WriteLine("Please wait, discovering services ...");

probeClient.AddServiceScope(new ServiceScope("urn:AmigoDemo"));

probeClient.AddServiceType(

new ServiceType("MySimpleAddService"));

probeClient.SendProbe();

System.Threading.Thread.Sleep(10000);

if (probeClient.ProbeMatches != null)

Console.WriteLine("Found " +

probeClient.ProbeMatches.Count.ToString() +

" matches.");

if (probeClient.ProbeMatches != null &&

probeClient.ProbeMatches.Count > 0)

{

Console.WriteLine("Found " +

probeClient.ProbeMatches.Count + " service(s)!");

for (int index = 0;

index < probeClient.ProbeMatches.Count;

index++)

Console.WriteLine("Server " + index + " has address: " +

probeClient.ProbeMatches[index].FirstXaddr);

Console.Write("Please choose a server to call: ");

int server = int.Parse(Console.ReadLine());

AmigoDemo.Service proxy = new AmigoDemo.Service();

proxy.Url =

probeClient.ProbeMatches[server].FirstXaddr.AbsoluteUri;

int result = proxy.add(1, 3);

Console.WriteLine("Server replies: 1 + 3 = " +

result.ToString());

Console.WriteLine(proxy.ping(System.Net.Dns.GetHostName()));

}

else

{

Console.WriteLine("No service found!");

}

Console.ReadLine();

}

}

}

June 2007 Confidential

Amigo IST-2004-004182 30/63

3.2.3.6 Executing the client

If the service is running and the client program is executed, the following text should show up on the console:

After entering the number of the service to use (in this case, there is only single service that matches our profile), the following output is generated:

June 2007 Confidential

Amigo IST-2004-004182 31/63

3.2.4 Client implementation Java / OSGi

Now that we successfully designed the first simple Web Service our next step is to write an appropriate client application. The purpose of the client program is to lookup the service on the network and to call both of the exported Web methods. Note that a major step during client development is the lookup process as we typically do not know in advance in an Amigo network on which machine a specific service is executed. Moreover, this may even change during time.

3.2.4.1 Implementation of the client class

The client class implementation should go into file Web Services\Code\Java\tutorial_client\src\de\ims\fraunhofer\amigo\tutorial\client\ClientComponent.java.

First, some packages that are needed must be imported:

packagepackagepackagepackage de.ims.fraunhofer.amigo.tutorial.client; importimportimportimport java.net.InetAddress; importimportimportimport java.net.UnknownHostException; importimportimportimport org.apache.log4j.Logger; importimportimportimport org.ungoverned.gravity.servicebinder.Lifecycle; importimportimportimport com.francetelecom.amigo.core.AmigoException; importimportimportimport com.francetelecom.amigo.core.AmigoLdapLookup; importimportimportimport com.francetelecom.amigo.core.AmigoService;

The client class is named ClientComponent. All following code (if not stated different) goes into this class. Note that this class also implements the LifeCycle interface. As a result, some special mechanism can be used to handle cases like discovering an Amigo lookup service:

publicpublicpublicpublic classclassclassclass ClientComponent implementsimplementsimplementsimplements Lifecycle { privateprivateprivateprivate AmigoLdapLookup m_lookup; privateprivateprivateprivate finalfinalfinalfinal Logger m_logger = Logger.getLogger(ClientComponent.classclassclassclass); publicpublicpublicpublic voidvoidvoidvoid bindLookupService(AmigoLdapLookup lookup) { m_lookup = lookup; } publicpublicpublicpublic voidvoidvoidvoid unbindLookupService(AmigoLdapLookup lookup) { m_lookup = nullnullnullnull; } publicpublicpublicpublic voidvoidvoidvoid deactivate() { ; } … }

3.2.4.2 The client core method

The core method that actually handles all the steps that are needed to lookup and call the remote service are embedded in the activate method. Note that the activate method is something like the main routine in C program. However, note that the method in not allowed to do a busy wait.

publicpublicpublicpublic voidvoidvoidvoid activate() { … }

June 2007 Confidential

Amigo IST-2004-004182 32/63

The first step in the core client code is to lookup the service. This is done by:

trytrytrytry { host = InetAddress.getLocalHost().getHostAddress(); } catchcatchcatchcatch (UnknownHostException ex) { throwthrowthrowthrow newnewnewnew RuntimeException(ex); } finalfinalfinalfinal AmigoService[] services; trytrytrytry { services = m_lookup .lookup("urn:AmigoDemo", "MySimpleAddService", 1); } catchcatchcatchcatch (AmigoException e) { m_logger.error("Error occurred while looking up services", e); throwthrowthrowthrow newnewnewnew RuntimeException(e); } …

Mind to use the correct names as parameter for the lookup method.

As there may be more than one service that matches the searched profile, call the methods on all the services that were found (note that we cannot have an console input here, as the client is also running as an OSGi bundle; hence, we call the methods on all services, instead of letting the user choose one of the services). To this end, iterate over the services and call the Web methods add and ping.

To do the actual call to the Web Service method use the invoke method that operates on a generic stub. That is, in contrast to the C# implementation, use a generic method to call the remote method instead of generating a specific proxy class that may be generated from the service definition. Here is an example to call method add with parameters 1 and 3:

finalfinalfinalfinal Object result = amigoService.getGenericStub().invoke( "add", newnewnewnew String[] { "a", "b" }, newnewnewnew Object[] { newnewnewnew Integer(1), newnewnewnew Integer(3) });

Parameter passing for the remote method is a little bit more complex when using generic mechanisms. The first parameter to the invoke method is the name of the remote method. The second parameter is an array of strings, where each string defines the names of the parameters of the Web Service method. Finally, the last parameter is an array ob objects that take the actual parameter value. Note that the first parameter of the object array is associated with the first name of the parameter name array, and so on. In this case, “1” is passed over to a and “3” is send to b.

Please write the appropriate code.

Solution:

ifififif (services != nullnullnullnull && services.length > 0) { m_logger.info("'" + services.length + "' services have been discovered!"); forforforfor (intintintint i = 0; i < services.length; i++) { finalfinalfinalfinal AmigoService amigoService = services[i]; finalfinalfinalfinal String url = amigoService.getReference().getUrl(); trytrytrytry { finalfinalfinalfinal Object result = amigoService.getGenericStub().invoke( "add", newnewnewnew String[] { "a", "b" }, newnewnewnew Object[] { newnewnewnew Integer(1), newnewnewnew Integer(3) });

June 2007 Confidential

Amigo IST-2004-004182 33/63

m_logger.info( "Server @ '" + url + "' replies: 1 + 3 = " + result); } catchcatchcatchcatch (AmigoException ex) { m_logger.error( "Calling method 'add' on Server @ '" + url + "' failed!", ex); } ttttryryryry { finalfinalfinalfinal Object result = amigoService.getGenericStub().invoke( "ping", newnewnewnew String[] { "parameter" }, newnewnewnew Object[] { host }); m_logger.info("Server @ '" + url + "' replies to ping: " + result); } catchcatchcatchcatch (AmigoException ex) { m_logger.error( "Calling method 'ping' on Server @ '" + url + "' failed!", ex); } } } elseelseelseelse { m_logger.info("No services have been discovered!"); }

3.2.4.3 Complete client code

For your convenience, the complete client code is printed below.

packagepackagepackagepackage de.ims.fraunhofer.amigo.tutorial.client; importimportimportimport java.net.InetAddress; importimportimportimport java.net.UnknownHostException; importimportimportimport org.apache.log4j.Logger; importimportimportimport org.ungoverned.gravity.servicebinder.Lifecycle; importimportimportimport com.francetelecom.amigo.core.AmigoException; importimportimportimport com.francetelecom.amigo.core.AmigoLdapLookup; importimportimportimport com.francetelecom.amigo.core.AmigoService; publicpublicpublicpublic classclassclassclass ClientComponent implementsimplementsimplementsimplements Lifecycle { privateprivateprivateprivate AmigoLdapLookup m_lookup; privateprivateprivateprivate finalfinalfinalfinal Logger m_logger = Logger.getLogger(ClientComponent.classclassclassclass); publicpublicpublicpublic voidvoidvoidvoid bindLookupService(AmigoLdapLookup lookup) { m_lookup = lookup; } publicpublicpublicpublic voidvoidvoidvoid unbindLookupService(AmigoLdapLookup lookup) { m_lookup = nullnullnullnull; } publicpublicpublicpublic voidvoidvoidvoid activate() { finalfinalfinalfinal String host; trytrytrytry { host = InetAddress.getLocalHost().getHostAddress(); } catchcatchcatchcatch (UnknownHostException ex) { throwthrowthrowthrow newnewnewnew RuntimeException(ex); } finalfinalfinalfinal AmigoService[] services; trytrytrytry { services = m_lookup .lookup("urn:AmigoDemo", "MySimpleAddService", 1); } ccccaaaattttcccchhhh (AmigoException e) {

June 2007 Confidential

Amigo IST-2004-004182 34/63

m_logger.error("Error occurred while looking up services", e); throwthrowthrowthrow newnewnewnew RuntimeException(e); } ifififif (services != nullnullnullnull && services.length > 0) { m_logger.info("'" + services.length + "' services have been discovered!"); forforforfor (intintintint i = 0; i < services.length; i++) { finalfinalfinalfinal AmigoService amigoService = services[i]; finalfinalfinalfinal String url = amigoService.getReference().getUrl(); trytrytrytry { finalfinalfinalfinal Object result = amigoService.getGenericStub().invoke( "add", newnewnewnew String[] { "a", "b" }, newnewnewnew Object[] { newnewnewnew Integer(1), newnewnewnew Integer(3) }); m_logger.info( "Server @ '" + url + "' replies: 1 + 3 = " + result); } catchcatchcatchcatch (AmigoException ex) { m_logger.error( "Calling method 'add' on Server @ '" + url + "' failed!", ex); } trytrytrytry { finalfinalfinalfinal Object result = amigoService.getGenericStub().invoke( "ping", newnewnewnew String[] { "parameter" }, newnewnewnew Object[] { host }); m_logger.info("Server @ '" + url + "' replies to ping: " + result); } catchcatchcatchcatch (AmigoException ex) { m_logger.error( "Calling method 'ping' on Server @ '" + url + "' failed!", ex); } } } elseelseelseelse { m_logger.info("No services have been discovered!"); } } publicpublicpublicpublic voidvoidvoidvoid deactivate() { ; // do nothing } }

3.2.4.4 Client bundle metadata

In order to run this software as an OSGi bundle, information is needed that describes some dependencies:

<?xml version="1.0" encoding="UTF-8" ?> <bundle> <component class="de.ims.fraunhofer.amigo.tutorial.client.ClientComponent"> <requires

service="com.francetelecom.amigo.core.AmigoLdapLookup"

filter=""

cardinality="1..1"

policy="dynamic"

bind-method="bindLookupService"

unbind-method="unbindLookupService" /> </component>

</bundle>

June 2007 Confidential

Amigo IST-2004-004182 35/63

Note that the this data structure states that there is one services required in order to run our Web Service client: AmigoLdapLookup. Further, the metadata also defined which routines shall be called in case of one of this services becomes available or unavailable.

The XML data is stored in Web Services\Code\Java\tutorial_client\res\metadata.xml.

3.2.4.5 Client bundle manifest

Each OSGi bundle requires a “manifest”. This a plain text file that stores some general information bout the bundle. For example, it also includes the name of the metadata file:

Manifest-Version: 1.0

Bundle-Description: Bundle discovers and utilizes the sample Amigo service

Bundle-Name: Tutorial-Client

Bundle-Activator: de.ims.fraunhofer.amigo.tutorial.client.AmigoCoreActivator

Created-By: Fraunhofer IMS

Bundle-Vendor: Fraunhofer IMS

Import-Package: com.francetelecom.amigo.core,

org.ungoverned.gravity.servicebinder,

org.apache.log4j

Bundle-Copyright: Less General Public License

Bundle-ClassPath: .

Metadata-location: metadata.xml

This manifest (named manifest.mf) is stored in the same directory as the metadata file.

3.2.4.6 Client activator

To start or service as a OSGi bundle a activator class is needed. This class will be used by OSGi to start the service. This file is Web Services\Code\Java\tutorial_server\src\de\ims\fraunhofer\amigo\tutorial\client\AmigoCoreActivator.java.

packagepackagepackagepackage de.ims.fraunhofer.amigo.tutorial.client; importimportimportimport org.apache.log4j.Logger; importimportimportimport org.osgi.framework.BundleContext; importimportimportimport org.ungoverned.gravity.servicebinder.GenericActivator; publicpublicpublicpublic classclassclassclass AmigoCoreActivator extendsextendsextendsextends GenericActivator { privateprivateprivateprivate finalfinalfinalfinal Logger m_logger = Logger.getLogger(AmigoCoreActivator.classclassclassclass); publicpublicpublicpublic voidvoidvoidvoid start(BundleContext context) throwsthrowsthrowsthrows Exception { supersupersupersuper.start(context); m_logger.info("Server started."); } }

3.2.4.7 Run the client

In order to run the client, it must be loaded into Oscar as described in Section 3.2.2.11. If everything runs fine, then In the log window you will see the messages from server and client:

June 2007 Confidential

Amigo IST-2004-004182 36/63

June 2007 Confidential

Amigo IST-2004-004182 37/63

June 2007 Confidential

Amigo IST-2004-004182 38/63

3.3 How to develop a service using events: example 2

After completion of the first simple Web Service and client, we are now going to implement an service that uses events to asynchronously inform a client. To this end, the client must register with the service and leave information about which method shall be executed in case of an event. Further, the server must be capable of sending out events.

In this example, we are going to implement a service that periodically sends out an event each second. Further, the service shall contain a method that can be used to activate or deactivate event firing. However, note that usage of this method is different for the .NET and the Java application. This is due to the fact that in .NET clients are run as a stand-alone application while in Java the client is running on behalf of the OSGi framework.

June 2007 Confidential

Amigo IST-2004-004182 39/63

3.3.1 Service implementation .NET

First, implementation of the service as a .NET C# application is analyzed.

3.3.1.1 Definition of the major server class

The following C# example shows the external libraries that is used by our code. Note that especially EMIC.WebServer, EMIC.WSDiscovery.Server, and EMIC.WSAddressing are needed to process the web service related messaged and to provide the code with mechanisms to run and control a web server.

using System;

using System.Collections.Generic;

using System.Text;

using System.Web.Services;

using EMIC.WebServer;

using EMIC.WSDiscovery.Server;

using EMIC.WSAddressing;

Next, we define the namespace that will hold our new service. Here it is called ServerWithEventing. The actual name of the new service class shall be EventingService. In order to make a service from the class, we need to provide some additional information to the system. This information is scope, type, and namespace of the service (this is in fact the same as our simple service developed in the previous section). An additional information we have to provide is name of the event that is going to be fired by the service. Here it is named aNewEvent and we pass over the name to the system by using [WSEvent(“aNewEvent”)].

namespace ServerWithEventing

{

[ScopeAttribute("urn:AmigoDemo")]

[TypeAttribute("MyEventingService")]

[WebService(Namespace = "http://amigoproject.org")]

[WSEvent("aNewEvent")]

public class EventingService : DiscoverableService

{

Additionally, within the class we define the event

public event EventHandler<WSNotificationEventArgs> aNewEvent;

and a private member that will be used to control event firing:

private bool fireState = false;

3.3.1.2 Event firing

As the events shall be periodically fired, define a separate method called fireEvent. Here, we want to transmit two parameters with each event: a string value named stringValue and an integer called intValue. These both parameters are also parameters to the fireEvent method.

public void fireEvent(string stringValue, int intValue)

{

if (this.aNewEvent != null && fireState)

{

EMIC.WSEventing.NotificationData data =

new EMIC.WSEventing.NotificationData();

data[new System.Xml.XmlQualifiedName("stringValue")] =

stringValue;

data[new System.Xml.XmlQualifiedName("intValue")] =

June 2007 Confidential

Amigo IST-2004-004182 40/63

intValue.ToString ();

this.aNewEvent(this,

new WSNotificationEventArgs("aNewEvent", data));

Console.WriteLine("EventingService: (" + stringValue + "," +

intValue.ToString () + ") event fired!");

}

}

In the code above, if fireState is false, then no events are fired. Note that parameters of a event are transmitted using EMIC.WSEventing.NotificationData objects. In principle, this data structure is a map which associates a parameter string with an appropriate identifier name. Note that the parameters must be converted to string before they are stored to the data structure. Mind to chose appropriate names for the identifiers of the map in order to successfully retrieve the parameter at event destination.

The actual event firing is done by

this.aNewEvent(this,

new WSNotificationEventArgs("aNewEvent", data));

Note that the name of the parameter is passed over as first argument to the WSNotificationEventArgs constructor while the actual event parameter are passed over using the map described above.

3.3.1.3 Defining a Web method setFireState

In order to control event firing remotely, define a method setFireState which takes a Boolean as a parameter. Depending on this parameter, member fireState will be true or false. Mind the string [WebMethod] in order to export this method as a service.

[WebMethod]

public void setFireState(bool b)

{

fireState = b;

}

3.3.1.4 Service deployment

The service is deployed as in the previous example. That is, define a new class Program that includes the main routine.

class Program

{

static void Main(string[] args)

{

...

}

}

Next, add the code to create the service into the main method. In fact this code is very similar to the previous server example. However, mind to name the virtual server directory MyEventingService in this case. After initialization has been done, enter an endless loop within main which fires an event every second:

int i = 0;

while (true)

{

service.fireEvent(System.Net.Dns.GetHostName(), i++);

System.Threading.Thread.Sleep(1000);

}

June 2007 Confidential

Amigo IST-2004-004182 41/63

As a result, the complete class Program is:

class Program

{

static void Main(string[] args)

{

EventingService service = new EventingService();

WebServer.Port = 8080;

WebServer.AddWebService("MyEventingService", service);

Console.WriteLine("The MyEventingService Web Service is now" +

" running and ready to be discovered (also WSDL)" +

"as http://" + System.Net.Dns.GetHostName() + ":" +

WebServer.Port + "/MyEventingService/service.asmx");

int i = 0;

while (true)

{

service.fireEvent(System.Net.Dns.GetHostName(), i++);

System.Threading.Thread.Sleep(1000);

}

}

}

3.3.1.5 Complete server code

For your convenience, the complete server code is printed below.

using System;

using System.Collections.Generic;

using System.Text;

using System.Web.Services;

using EMIC.WebServer;

using EMIC.WSDiscovery.Server;

using EMIC.WSAddressing;

namespace ServerWithEventing

{

[ScopeAttribute("urn:AmigoDemo")]

[TypeAttribute("MyEventingService")]

[WebService(Namespace = "http://amigoproject.org")]

[WSEvent("aNewEvent")]

public class EventingService : DiscoverableService

{

public event EventHandler<WSNotificationEventArgs> aNewEvent;

private bool fireState = false;

public void fireEvent(string stringValue, int intValue)

{

if (this.aNewEvent != null && fireState)

{

EMIC.WSEventing.NotificationData data =

new EMIC.WSEventing.NotificationData();

data[new System.Xml.XmlQualifiedName("stringValue")] =

stringValue;

data[new System.Xml.XmlQualifiedName("intValue")] =

intValue.ToString ();

June 2007 Confidential

Amigo IST-2004-004182 42/63

this.aNewEvent(this,

new WSNotificationEventArgs("aNewEvent", data));

Console.WriteLine("EventingService: (" + stringValue + "," +

intValue.ToString () + ") event fired!");

}

}

[WebMethod]

public void setFireState(bool b)

{

fireState = b;

}

}

class Program

{

static void Main(string[] args)

{

EventingService service = new EventingService();

WebServer.Port = 8080;

WebServer.AddWebService("MyEventingService", service);

Console.WriteLine("The MyEventingService Web Service is now" +

" running and ready to be discovered (also WSDL)" +

"as http://" + System.Net.Dns.GetHostName() + ":" +

WebServer.Port + "/MyEventingService/service.asmx");

int i = 0;

while (true)

{

service.fireEvent(System.Net.Dns.GetHostName(), i++);

System.Threading.Thread.Sleep(1000);

}

}

}

}

3.3.1.6 Executing the service

In order to run the service, go to directory “ServerWithEventing\bin\Release” and execute the program. This will bring up the following messages:

June 2007 Confidential

Amigo IST-2004-004182 43/63

Write down the URL of the Web Service. You may also use browser to display information about the service.

June 2007 Confidential

Amigo IST-2004-004182 44/63

3.3.2 Service implementation Java / OSGi

The actual server class definition goes into file Web Services\Code\Java\tutorial_server_eventing\src\de\ims\fraunhofer\amigo\tutorial\server_eventing\ServerComponent.java

3.3.2.1 Definition of the major server class

First, of all we again define the exports that are needed by our implementation.

package package package package de.ims.fraunhofer.amigo.tutorial.server_eventing; importimportimportimport java.net.InetAddress; importimportimportimport java.net.UnknownHostException;

importimportimportimport org.apache.log4j.Logger; importimportimportimport org.ungoverned.gravity.servicebinder.Lifecycle; importimportimportimport com.francetelecom.amigo.core.AmigoException; importimportimportimport com.francetelecom.amigo.core.AmigoExportedService; importimportimportimport com.francetelecom.amigo.core.AmigoLdapLookup; importimportimportimport com.francetelecom.amigo.core.AmigoReference; importimportimportimport com.francetelecom.amigo.core.AmigoServiceExporter; importimportimportimport com.francetelecom.amigo.core.ExportedMethod; importimportimportimport com.francetelecom.amigo.core.NotificationData;

The class to be defined is name SeverComponent. Further, some routines that handle special events like an lookup services becoming available, are needed.

publicpublicpublicpublic classclassclassclass ServerComponent implementsimplementsimplementsimplements Lifecycle, IServerInterface { privateprivateprivateprivate AmigoServiceExporter m_serviceExporter; pripripriprivatevatevatevate AmigoExportedService m_myService; privateprivateprivateprivate AmigoLdapLookup m_lookup; publicpublicpublicpublic voidvoidvoidvoid bindLookupService(AmigoLdapLookup lookup) { m_lookup = lookup; } publicpublicpublicpublic voidvoidvoidvoid unbindLookupService(AmigoLdapLookup lookup) { m_lookup = nullnullnullnull; } publicpublicpublicpublic voidvoidvoidvoid bindServiceExporter(AmigoServiceExporter serviceExporter) { m_serviceExporter = serviceExporter; } publicpublicpublicpublic voidvoidvoidvoid unbindServiceExporter( AmigoServiceExporter serviceExporter) { m_serviceExporter = nullnullnullnull; } privateprivateprivateprivate finalfinalfinalfinal Logger m_logger = Logger.getLogger(ServerComponent.classclassclassclass);

3.3.2.2 Firing events

The event code shall fire event periodically. Hence, the server code is implemented as a thread. To this end, we first define the event thread code: Implement it using a while loop that as long as the thread is not interrupted:

privateprivateprivateprivate finalfinalfinalfinal Runnable m_eventSender = newnewnewnew Runnable() { publicpublicpublicpublic voidvoidvoidvoid run() { finalfinalfinalfinal String hostname; trytrytrytry { hostname = InetAddress.getLocalHost().getHostName(); } catchcatchcatchcatch (UnknownHostException e) { throwthrowthrowthrow newnewnewnew RuntimeException(e);

June 2007 Confidential

Amigo IST-2004-004182 45/63

} intintintint count = 0; whilewhilewhilewhile (!Thread.currentThread().isInterrupted()) { trytrytrytry { Thread.sleep(1000); } catchcatchcatchcatch (InterruptedException ex) { breakbreakbreakbreak; } finalfinalfinalfinal NotificationData event = newnewnewnew NotificationData(); event.setProperty("stringValue", hostname); event.setProperty("intValue", "" + (++count)); m_myService.getEventSender(). notify("aNewEvent", event); } } };

Note that the parameters to the event are specified using the setProperty method: i.e., for each parameter a separate call to this method must be done:

finalfinalfinalfinal NotificationData event = newnewnewnew NotificationData(); event.setProperty("stringValue", hostname); event.setProperty("intValue", "" + (++count));

The actual firing is then finally done by the code below. Note that the name of the event is passed over as a parameter to method notify.

m_myService.getEventSender(). notify("aNewEvent", event);

3.3.2.3 Activating the service

Activating the server is done via the activate method of the ServerComponent class. Like in the previous server example, this requires creating the service with the right properties, adding the methods to be public available and finally registering the service with the lookup service.

privateprivateprivateprivate Thread m_eventSenderThread = nullnullnullnull; publicpublicpublicpublic voidvoidvoidvoid activate() { m_myService = m_serviceExporter.createService(thisthisthisthis); m_myService.addProperty("namespace", "http://amigoproject.org"); m_myService.addProperty("oid", "Server-Eventing"); finalfinalfinalfinal ExportedMethod method = newnewnewnew ExportedMethod("setFireState", newnewnewnew String[] { "b" }); trytrytrytry { m_myService.exportMethods(AmigoReference.DEFAULT, newnewnewnew ExportedMethod[] { method }); m_logger.info("Exported service at address " + m_myService.getReference()); } catchcatchcatchcatch (Exception ex) { m_logger.error("Unable to export service!", ex); throwthrowthrowthrow newnewnewnew RuntimeException(ex); } m_myService.addProperty("ServiceType", "MyEventingService"); m_myService.addProperty("Scope", "urn:AmigoDemo"); trytrytrytry { m_lookup.registerService(m_myService); m_logger.info("Service registered!"); } catchcatchcatchcatch (AmigoException ex) { m_logger.error("Unable to register service!", ex); throwthrowthrowthrow newnewnewnew RuntimeException(ex); } …

June 2007 Confidential

Amigo IST-2004-004182 46/63

In addition, the eventing thread is created and started as shown below:

m_eventSenderThread = newnewnewnew Thread(m_eventSender); m_eventSenderThread.start(); }

This completes the activate method.

3.3.2.4 Deactivating the service

As the server is running as an OSGi bundle within some care should be taken during service shutdown to free all resources. To this end, the class should provide a deactivate method. In detail, the following should be done:

• Stopping and removing the event firing thread.

• Un-registering the service with the lookup service.

• Un-exporting the service.

The code to achieve this is shown below:

publicpublicpublicpublic voidvoidvoidvoid deactivate() { ifififif (m_eventSenderThread != nullnullnullnull) { m_eventSenderThread.interrupt(); trytrytrytry { m_eventSenderThread.join(); } catchcatchcatchcatch (InterruptedException ex) { } m_eventSenderThread = nullnullnullnull; } ifififif (m_myService != nullnullnullnull) { ifififif (m_lookup != nullnullnullnull) { trytrytrytry { m_lookup.unregisterService(m_myService); m_logger.info("Service unregistered!"); } catchcatchcatchcatch (AmigoException ex) { m_logger.error( "Unable to unregister service!", ex); } } ttttryryryry { m_myService.unexport(); } catchcatchcatchcatch (AmigoException ex) { m_logger.error("Unable to unexport service!", ex); } } }

3.3.2.5 Web method “setFireState”

The final task to do is to define the Web method setFireState. Note that for the OSGi implementation this method does nothing. The reason is that we here do not use this method to control event firing. Nevertheless, the definition is needed in order to match the signature of the exported Web Service:

publicpublicpublicpublic voidvoidvoidvoid setFireState(booleanbooleanbooleanboolean b) { }

3.3.2.6 Complete server code

For your convenience, the complete server code is printed below.

package package package package de.ims.fraunhofer.amigo.tutorial.server_eventing; importimportimportimport java.net.InetAddress; importimportimportimport java.net.UnknownHostException;

June 2007 Confidential

Amigo IST-2004-004182 47/63

importimportimportimport org.apache.log4j.Logger; importimportimportimport org.ungoverned.gravity.servicebinder.Lifecycle; importimportimportimport com.francetelecom.amigo.core.AmigoException; importimportimportimport com.francetelecom.amigo.core.AmigoExportedService; importimportimportimport com.francetelecom.amigo.core.AmigoLdapLookup; importimportimportimport com.francetelecom.amigo.core.AmigoReference; importimportimportimport com.francetelecom.amigo.core.AmigoServiceExporter; importimportimportimport com.francetelecom.amigo.core.ExportedMethod; importimportimportimport com.francetelecom.amigo.core.NotificationData; publicpublicpublicpublic classclassclassclass ServerComponent implementsimplementsimplementsimplements Lifecycle, IServerInterface { privateprivateprivateprivate AmigoServiceExporter m_serviceExporter; privateprivateprivateprivate AmigoExportedService m_myService; privateprivateprivateprivate AmigoLdapLookup m_lookup; publicpublicpublicpublic voidvoidvoidvoid bindLookupService(AmigoLdapLookup lookup) { m_lookup = lookup; } publicpublicpublicpublic voidvoidvoidvoid unbindLookupService(AmigoLdapLookup lookup) { m_lookup = nunununullllllll; } publicpublicpublicpublic voidvoidvoidvoid bindServiceExporter(AmigoServiceExporter serviceExporter) { m_serviceExporter = serviceExporter; } publicpublicpublicpublic voidvoidvoidvoid unbindServiceExporter( AmigoServiceExporter serviceExporter) { m_serviceExporter = nullnullnullnull; } privateprivateprivateprivate finalfinalfinalfinal Logger m_logger = Logger.getLogger(ServerComponent.classclassclassclass); privateprivateprivateprivate finalfinalfinalfinal Runnable m_eventSender = newnewnewnew Runnable() { publicpublicpublicpublic voidvoidvoidvoid run() { finalfinalfinalfinal String hostname; trytrytrytry { hostname = InetAddress.getLocalHost().getHostName(); } catchcatchcatchcatch (UnknownHostException e) { throwthrowthrowthrow newnewnewnew RuntimeException(e); } intintintint count = 0; whilewhilewhilewhile (!Thread.currentThread().isInterrupted()) { trytrytrytry { Thread.sleep(1000); } catchcatchcatchcatch (InterruptedException ex) { breakbreakbreakbreak; } finalfinalfinalfinal NotificationData event = nenenenewwww NotificationData(); event.setProperty("stringValue", hostname); event.setProperty("intValue", "" + (++count)); m_myService.getEventSender(). notify("aNewEvent", event); } } }; privateprivateprivateprivate Thread m_eventSenderThread = nullnullnullnull; publicpublicpublicpublic vvvvoidoidoidoid activate() { m_myService = m_serviceExporter.createService(thisthisthisthis); m_myService.addProperty("namespace", "http://amigoproject.org"); m_myService.addProperty("oid", "Server-Eventing"); finalfinalfinalfinal ExportedMethod method = newnewnewnew ExportedMethod("setFireState", newnewnewnew String[] { "b" }); trytrytrytry {

June 2007 Confidential

Amigo IST-2004-004182 48/63

m_myService.exportMethods(AmigoReference.DEFAULT, newnewnewnew ExportedMethod[] { method }); m_logger.info("Exported service at address " + m_myService.getReference()); } catchcatchcatchcatch (Exception ex) { m_logger.error("Unable to export service!", ex); throwthrowthrowthrow newnewnewnew RuntimeException(ex); } m_myService.addProperty("ServiceType", "MyEventingService"); m_myService.addProperty("Scope", "urn:AmigoDemo"); trytrytrytry { m_lookup.registerService(m_myService); m_logger.info("Service registered!"); } catchcatchcatchcatch (AmigoException ex) { m_logger.error("Unable to register service!", ex); throwthrowthrowthrow newnewnewnew RuntimeException(ex); } m_eventSenderThread = newnewnewnew Thread(m_eventSender); m_eventSenderThread.start(); } publicpublicpublicpublic voidvoidvoidvoid deactivate() { ifififif (m_eventSenderThread != nullnullnullnull) { m_eventSenderThread.interrupt(); trytrytrytry { m_eventSenderThread.join(); } catchcatchcatchcatch (InterruptedException ex) { } m_eventSenderThread = nullnullnullnull; } ifififif (m_myService != nullnullnullnull) { ifififif (m_lookup != nullnullnullnull) { trytrytrytry { m_lookup.unregisterService(m_myService); m_logger.info("Service unregistered!"); } catchcatchcatchcatch (AmigoException ex) { m_logger.error( "Unable to unregister service!", ex); } } trytrytrytry { m_myService.unexport(); } catchcatchcatchcatch (AmigoException ex) { m_logger.error("Unable to unexport service!", ex); } } } publicpublicpublicpublic voidvoidvoidvoid setFireState(booleanbooleanbooleanboolean b) { } }

3.3.2.7 Service bundle metadata

In order to run this software as an OSGi bundle, some information is needed that describes some dependencies of the software:

<?xml version="1.0" encoding="UTF-8" ?> <bundle> <component class= "de.ims.fraunhofer.amigo.tutorial.server_eventing.ServerComponent"> <requires service="com.francetelecom.amigo.core.AmigoServiceExporter" filter="" cardinality="1..1" policy="dynamic"

bind-method="bindServiceExporter" unbind-method="unbindServiceExporter" /> <requires service="com.francetelecom.amigo.core.AmigoLdapLookup"

June 2007 Confidential

Amigo IST-2004-004182 49/63

filter="" cardinality="1..1" policy="dynamic" bind-method="bindLookupService" unbind-method="unbindLookupService" />

</component>

</bundle>

Note that the this data structure states that there are two other services requires in order to run our Web Service. The first is the AmigoServiceExporter, the other is AmigoLdapLookup. Further, the metadata also defined which routines shall be called in case of one of these services becomes available or unavailable.

The XML data is stored in Web Services\Code\Java\tutorial_server_eventing\res\metadata.xml.

3.3.2.8 Service bundle manifest

Each OSGi bundle requires a “manifest”. This a plain text file that stores some general information bout the bundle. For example, it also includes the name of the metadata file:

Manifest-Version: 1.0

Bundle-Description: Bundle registers a simple Amigo service

Bundle-Name: Tutorial-Server-Eventing

Bundle-Activator:

de.ims.fraunhofer.amigo.tutorial.server_eventing.AmigoCoreActivator

Created-By: Fraunhofer IMS

Bundle-Vendor: Fraunhofer IMS

Import-Package: com.francetelecom.amigo.core,

org.ungoverned.gravity.servicebinder,

org.apache.log4j

Bundle-Copyright: Less General Public License

Bundle-ClassPath: .

Metadata-location: metadata.xml

This manifest (named manifest.mf) is stored in the same directory as the metadata file.

3.3.2.9 Service activator

To start or service as a OSGi bundle a activator class is needed. This class will be used by OSGi to start the service. This file is Web Services\Code\Java\tutorial_server\src\de\ims\fraunhofer\amigo\tutorial\server_eventing\AmigoCoreActivator.java.

packagepackagepackagepackage de.ims.fraunhofer.amigo.tutorial.server_eventing; importimportimportimport org.apache.log4j.Logger; importimportimportimport org.osgi.framework.BundleContext; importimportimportimport org.ungoverned.gravity.servicebinder.GenericActivator; publicpublicpublicpublic classclassclassclass AmigoCoreActivator extendsextendsextendsextends GenericActivator { privateprivateprivateprivate finalfinalfinalfinal Logger m_logger = Logger.getLogger(AmigoCoreActivator.classclassclassclass); publicpublicpublicpublic voidvoidvoidvoid start(BundleContext context) throwsthrowsthrowsthrows Exception { supersupersupersuper.start(context); m_logger.info("Server started."); } }

3.3.2.10 Run the service

In order to run the eventing service, it must be loaded into Oscar as described in Section 3.2.2.11.

June 2007 Confidential

Amigo IST-2004-004182 50/63

3.3.3 Client implementation .NET

The client of the event based service is very similar to a normal client. The major difference are the mechanisms to register for events and the actual event handler. Hence, in the following, description of the code will focus on these aspects.

3.3.3.1 Importing the web service

As for the sample service in the precious example, we have to import the interface of the service in order to access the exported methods. Please perform the appropriate steps according to the procedure described in Section 3.2.3.1. For the reference name of the Web Service use AmigoEventingDemo. This is the namespace the proxy class definition will be defined in.

3.3.3.2 Definition of the major client class

The following C# example shows the external libraries that is used by our code. Note that a special libraray EMIC.WSEventing must be made visible.

using System;

using System.Collections.Generic;

using System.Text;

using System.Web.Services;

using EMIC.WSDiscovery;

using EMIC.WSDiscovery.Client;

using EMIC.WSEventing;

Next, define a name space ClientWithEventing and within this namespace a class called Program.

namespace ClientWithEventing

{

class Program

{

...

}

}

3.3.3.3 Definition of the event handler

The event handler (i.e., the method that is called in case of an event is received by the client) is in fact a normal method of the Program class. It takes three parameter: the first one is the event name, the second store the event parameters that are send along with the event. Mind that we use a special map on the server to encode these parameters. In fact this map is passed over as an argument to the event handler. The final parameter subscriptionEnded states whether the subscription ends. This may be used as an reminder to renew the subscription.

private void EventReceived(string eventName,

NotificationData notificationData,

bool subscriptionEnded)

{

string stringValue = notificationData[

new System.Xml.XmlQualifiedName("stringValue")];

int intValue = int.Parse(notificationData[

new System.Xml.XmlQualifiedName("intValue")]);

Console.WriteLine("Received event (" + stringValue + "," +

intValue.ToString() + ")");

June 2007 Confidential

Amigo IST-2004-004182 51/63

}

Note that in the code above we use the identifier of the parameters in order to retrieve them form the data structure. Note further, that a message is printed to the console each time an event is received.

3.3.3.4 Service lookup

The actual work of the client shall be embedded into a simple method called work.

void work ()

{

...

}

The following code shall go into this method.

The service lookup is done as in the previous example. However, mind to adopt the service type accordingly. Add the appropriate routines to the method work.

3.3.3.5 The core loop

Within the work method define endless loop that flips the fire state value of the Web Server every 5 seconds. Note that setFireState is in fact a Web Service:

bool fireState = true;

while (true)

{

proxy.setFireState(fireState);

Console.WriteLine("Set fire state to " +

fireState.ToString());

System.Threading.Thread.Sleep(5000);

fireState = !fireState;

}

3.3.3.6 Starting the client

Finally, add a main routine to the class. Add code to create a new instance of the class Program and call method work on this instance.

static void Main(string[] args)

{

Program EventSink = new Program();

EventSink.work();

}

3.3.3.7 Complete client code

For your convenience, the complete client code is printed below.

using System;

using System.Collections.Generic;

using System.Text;

using System.Web.Services;

using EMIC.WSDiscovery;

using EMIC.WSDiscovery.Client;

using EMIC.WSEventing;

namespace ClientWithEventing

{

class Program

{

private void EventReceived(string eventName,

June 2007 Confidential

Amigo IST-2004-004182 52/63

NotificationData notificationData,

bool subscriptionEnded)

{

string stringValue = notificationData[

new System.Xml.XmlQualifiedName("stringValue")];

int intValue = int.Parse(notificationData[

new System.Xml.XmlQualifiedName("intValue")]);

Console.WriteLine("Received event (" + stringValue + "," +

intValue.ToString() + ")");

}

void work ()

{

ProbeClient probeClient = new ProbeClient();

Console.WriteLine("Please wait, discovering services ...");

probeClient.AddServiceScope(new ServiceScope("urn:AmigoDemo"));

probeClient.AddServiceType(

new ServiceType("MyEventingService"));

probeClient.SendProbe();

System.Threading.Thread.Sleep(10000);

if (probeClient.ProbeMatches != null)

Console.WriteLine("Found " +

probeClient.ProbeMatches.Count.ToString() +

" matches.");

if (probeClient.ProbeMatches != null &&

probeClient.ProbeMatches.Count > 0)

{

Console.WriteLine("Found " +

probeClient.ProbeMatches.Count + " service(s)!");

for (int index = 0;

index < probeClient.ProbeMatches.Count;

index++)

Console.WriteLine("Server " + index + " has address: " +

probeClient.ProbeMatches[index].FirstXaddr);

Console.Write("Please choose a server to call: ");

int server = int.Parse(Console.ReadLine());

AmigoEventingDemo.EventingService proxy =

new AmigoEventingDemo.EventingService();

proxy.Url =

probeClient.ProbeMatches[server].FirstXaddr.AbsoluteUri;

WSEventingSink sink = null;

try

{

sink = new WSEventingSink(

probeClient.ProbeMatches[server].FirstXaddr);

}

catch

{

Console.WriteLine(

"Could not register with event source " +

probeClient.ProbeMatches[server].

FirstXaddr.ToString());

}

if (sink != null)

{

June 2007 Confidential

Amigo IST-2004-004182 53/63

try {

sink.Subscribe(this.EventReceived);

Console.WriteLine(

"Successfully subscribed to event channel!");

}

catch {

Console.WriteLine(

"Cound not subscribe to event channel!");

}

}

bool fireState = true;

while (true)

{

proxy.setFireState(fireState);

Console.WriteLine("Set fire state to " +

fireState.ToString());

System.Threading.Thread.Sleep(5000);

fireState = !fireState;

}

}

else

{

Console.WriteLine("No service found! Type <Return>");

}

Console.ReadLine();

}

static void Main(string[] args)

{

Program EventSink = new Program();

EventSink.work();

}

}

}

3.3.3.8 Running the client

After completing the client code, we now execute the program. This will bring up the following output:

June 2007 Confidential

Amigo IST-2004-004182 54/63

After choosing the appropriate service number, the client will receive events every second with pauses of 5 seconds where no event is received:

The server output is

June 2007 Confidential

Amigo IST-2004-004182 55/63

June 2007 Confidential

Amigo IST-2004-004182 56/63

3.3.4 Client implementation Java / OSGi

The client of the event based service is very similar to a normal client. The major differences are the mechanisms to register for events and the actual event handler. Hence, in the following, description of the code will focus on these aspects.

3.3.4.1 Implementation of the event sink class

The actual event sink class should go to file \tutorial_client_eventing\src\de\ims\fraunhofer\amigo\tutorial\client_eventing\EventSinkImpl.java. It defines that code that is to be executed each time an appropriate event is received by the client. EventSinkImpl implements the EventSink interface. Its constructor takes the service URL as an argument, however, this is only needed to generate readable log output. For the actual processing, this URL is not needed.

Within the class define two methods: a method notify which will be called when a event arrives and subscriptionEnded which is executed when the eventing service will not deliver any events to this event sink. The parameter eventName denotes the name of the event. Here it will be “aNewEvent”. The parameter data stores the event arguments:

publicpublicpublicpublic vovovovoidididid notify(String eventName, NotificationData data) { … } publicpublicpublicpublic voidvoidvoidvoid subscriptionEnded(String eventName) { … }

Please fill code that logs information about which event has been received from which URL and what the call parameters are.

The complete code for the event sink is:

packagepackagepackagepackage de.ims.fraunhofer.amigo.tutorial.client_eventing; importimportimportimport org.apache.log4j.Logger; importimportimportimport com.francetelecom.amigo.core.EventSink; importimportimportimport com.francetelecom.amigo.core.NotificationData; classclassclassclass EventSinkImpl implementsimplementsimplementsimplements EventSink { privateprivateprivateprivate finalfinalfinalfinal Logger m_logger = Logger.getLogger(EventSinkImpl.classclassclassclass); privateprivateprivateprivate finalfinalfinalfinal String m_serviceUrl; publicpublicpublicpublic EventSinkImpl(String url) { m_serviceUrl = url; } publicpublicpublicpublic voidvoidvoidvoid notify(String eventName, NotificationData data) { m_logger.info("Received event '" + eventName + "' from Server @ '" + m_serviceUrl + "', data = " + data); } publicpublicpublicpublic voidvoidvoidvoid subscriptionEnded(String eventName) { m_logger.info("Subscription to Server @ '" + m_serviceUrl + "' for event '" + eventName + "' has ended!"); } }

June 2007 Confidential

Amigo IST-2004-004182 57/63

3.3.4.2 Implementation of the client class

The client class implementation should go into file Web Services\Code\Java\tutorial_client_eventing\src\de\ims\fraunhofer\amigo\tutorial\client_eventing\ClientComponent.java.

First, some packages that are needed must be imported:

packagepackagepackagepackage de.ims.fraunhofer.amigo.tutorial.client_eventing; importimportimportimport java.util.HashMap; importimportimportimport java.util.Iterator; importimportimportimport java.util.Map; importimportimportimport java.util.Map.Entry; importimportimportimport org.apache.log4j.Logger; importimportimportimport org.ungoverned.gravity.servicebinder.Lifecycle; importimportimportimport com.francetelecom.amigo.core.AmigoException; importimportimportimport com.francetelecom.amigo.core.AmigoLdapLookup; importimportimportimport com.francetelecom.amigo.core.AmigoService; importimportimportimport com.francetelecom.amigo.core.EventSink;

The client class is named ClientComponent. All following code (if not stated different) goes into this class. Note that this class also implements the LifeCycle interface. As a result, some special mechanism can be used to handle cases like discovering an Amigo lookup service:

publicpublicpublicpublic classclassclassclass ClientComponent implementsimplementsimplementsimplements Lifecycle { privateprivateprivateprivate AmigoLdapLookup m_lookup; privateprivateprivateprivate finalfinalfinalfinal Logger m_logger = Logger.getLogger(ClientComponent.classclassclassclass); publicpublicpublicpublic voidvoidvoidvoid bindLookupService(AmigoLdapLookup lookup) { m_lookup = lookup; } publpublpublpublicicicic voidvoidvoidvoid unbindLookupService(AmigoLdapLookup lookup) { m_lookup = nullnullnullnull; } …

Compared to the service client in the first example, we are now going to write a client that receives events from the Web Services. To maintain these services, a map is created that associates each services with an appropriate event sink (event destination instance):

privateprivateprivateprivate finalfinalfinalfinal Map<AmigoService, EventSink> m_service2sink = newnewnewnew HashMap<AmigoService, EventSink>(); … }

3.3.4.3 The client core method

The core method that actually handles all the steps that are needed to lookup and call the remote service are embedded in the activate method. Note that the activate method is something like the main routine in C program. However, note that the method in not allowed to do a blocking wait.

publicpublicpublicpublic voidvoidvoidvoid activate() { … }

The first step in the core client code is to lookup the service. This is done by:

June 2007 Confidential

Amigo IST-2004-004182 58/63

finalfinalfinalfinal AmigoService[] services; trytrytrytry { services = m_lookup.lookup("urn:AmigoDemo", "MyEventingService", 1); } catchcatchcatchcatch (AmigoException e) { m_logger.error("Error occurred while looking up services", e); throwthrowthrowthrow newnewnewnew RuntimeException(e); }

Mind to use the correct names for the lookup method.

As there may be more than one service that matches the searched profile, call the methods on all the services that were found (note that we cannot have an console input here, as the client is also running as an OSGi bundle; hence, we call the methods on all services, instead of letting the user choose one of the services):

ifififif (services != nullnullnullnull && services.length > 0) { m_logger.info("'" + services.length + "' services have been discovered!"); forforforfor (intintintint i = 0; i < services.length; i++) { finalfinalfinalfinal AmigoService amigoService = services[i]; finalfinalfinalfinal String url = amigoService.getReference().getUrl(); trytrytrytry { amigoService.getGenericStub() .invoke("setFireState", newnewnewnew String[] { "b" }, newnewnewnew Object[] { Boolean.TRUE }); } catchcatchcatchcatch (AmigoException ex) { m_logger.error( "Unable to subscribe to Server @ '" + url + "'", ex); } } } elseelseelseelse { m_logger.info("No services have been discovered!"); }

Note that the actual call of the Web Service method is done by the invoke methods that operates on a generic stub. That is, in contrast to the C# implementation, we use a generic method to call the remote method instead of generating a specific proxy class that may be generated from the service definition.

3.3.4.4 Creating the event sink

Up to now, we created a plain Web Service client as in the previous example. Now, we add the event sink by inserting the following code into the try section of the for loop:

finalfinalfinalfinal EventSink sink = newnewnewnew EventSinkImpl(url); amigoService.getSubscriptionManager(). subscribe(sink, "aNewEvent", -1); m_logger.info( "Successfully subscribed to Server @ '" + url + "'"); m_service2sink.put(amigoService, sink);

Note that EventSinkImpl is the class that we defined before. It takes the URL of the services as a parameter during construction. The actual subscription to the event service is done by calling the subscribe method on the subscription manager. Note that the correct name for the event type “aNewEvent” must be provided here. Finally, the codes stores the association between service and event sink for future reference.

June 2007 Confidential

Amigo IST-2004-004182 59/63

3.3.4.5 Deactivating the service

In order to clean up everything if the client shall be stopped, the deactivate method must be provided. The most important task during client shutdown is to unsubscribe from the event sources. To this end, iterate over the m_service2sink data structure:

publicpublicpublicpublic voidvoidvoidvoid deactivate() { forforforfor (Iterator<Map.Entry<AmigoService, EventSink>> iterator = m_service2sink.entrySet().iterator(); iterator.hasNext();) { finalfinalfinalfinal Entry<AmigoService, EventSink> entry = iterator.next(); trytrytrytry { entry.getKey().getSubscriptionManager().unsubscribe( entry.getValue()); m_logger.info("Unsubscribed from Server @ '" + entry.getKey().getReference().getUrl() + "'"); } catchcatchcatchcatch (AmigoException ex) { } } }

3.3.4.6 Complete client code

For your convenience, the complete client code is printed below.

packagepackagepackagepackage de.ims.fraunhofer.amigo.tutorial.client_eventing; importimportimportimport java.util.HashMap; importimportimportimport java.util.Iterator; importimportimportimport java.util.Map; importimportimportimport java.util.Map.Entry; importimportimportimport org.apache.log4j.Logger; importimportimportimport org.ungoverned.gravity.servicebinder.Lifecycle; importimportimportimport com.francetelecom.amigo.core.AmigoException; importimportimportimport com.francetelecom.amigo.core.AmigoLdapLookup; importimportimportimport com.francetelecom.amigo.core.AmigoService; importimportimportimport com.francetelecom.amigo.core.EventSink; publicpublicpublicpublic classclassclassclass ClientComponent implementsimplementsimplementsimplements Lifecycle { privateprivateprivateprivate AmigoLdapLookup m_lookup; privateprivateprivateprivate finalfinalfinalfinal Logger m_logger = Logger.getLogger(ClientComponent.classclassclassclass); privateprivateprivateprivate finafinafinafinallll Map<AmigoService, EventSink> m_service2sink = newnewnewnew HashMap<AmigoService, EventSink>(); publicpublicpublicpublic voidvoidvoidvoid bindLookupService(AmigoLdapLookup lookup) { m_lookup = lookup; } publicpublicpublicpublic voidvoidvoidvoid unbindLookupService(AmigoLdapLookup lookup) { m_lookup = nullnullnullnull; } publicpublicpublicpublic voidvoidvoidvoid activate() { finalfinalfinalfinal AmigoService[] services; trytrytrytry { services = m_lookup.lookup("urn:AmigoDemo", "MyEventingService", 1); } catchcatchcatchcatch (AmigoException e) { m_logger.error("Error occurred while looking up services", e); throwthrowthrowthrow newnewnewnew RuntimeException(e); } ifififif (services != nullnullnullnull && services.length > 0) {

June 2007 Confidential

Amigo IST-2004-004182 60/63

m_logger.info("'" + services.length + "' services have been discovered!"); forforforfor (intintintint i = 0; i < services.length; i++) { finalfinalfinalfinal AmigoService amigoService = services[i]; finalfinalfinalfinal String url = amigoService.getReference().getUrl(); trytrytrytry { amigoService.getGenericStub() .invoke("setFireState", newnewnewnew String[] { "b" }, newnewnewnew Object[] { Boolean.TRUE }); finalfinalfinalfinal EventSink sink = newnewnewnew EventSinkImpl(url); amigoService.getSubscriptionManager(). subscribe(sink, "aNewEvent", -1); m_logger.info( "Successfully subscribed to Server @ '" + url + "'"); m_service2sink.put(amigoService, sink); } catchcatchcatchcatch (AmigoException ex) { m_logger.error( "Unable to subscribe to Server @ '" + url + "'", ex); } } } elseelseelseelse { m_logger.info("No services have been discovered!"); } } publicpublicpublicpublic voidvoidvoidvoid deactivate() { forforforfor (Iterator<Map.Entry<AmigoService, EventSink>> iterator = m_service2sink.entrySet().iterator(); iterator.hasNext();) { finalfinalfinalfinal Entry<AmigoService, EventSink> entry = iterator.next(); trytrytrytry { entry.getKey().getSubscriptionManager().unsubscribe( entry.getValue()); m_logger.info("Unsubscribed from Server @ '" + entry.getKey().getReference().getUrl() + "'"); } catchcatchcatchcatch (AmigoException ex) { } } } }

3.3.4.7 Client bundle metadata

In order to run this software as an OSGi bundle, some information is needed that describes some dependencies:

<?xml version="1.0" encoding="UTF-8" ?> <bundle> <component class= "de.ims.fraunhofer.amigo.tutorial.client_eventing.ClientComponent"> <requires service="com.francetelecom.amigo.core.AmigoLdapLookup"

filter="" cardinality="1..1" policy="dynamic" bind-method="bindLookupService" unbind-method="unbindLookupService" /> </component> </bundle>

June 2007 Confidential

Amigo IST-2004-004182 61/63

Note that the this data structure states that there is one services required in order to run our Web Service client: AmigoLdapLookup. Further, the metadata also defined which routines shall be called in case of one of this services becomes available or unavailable.

The XML data is stored in Web Services\Code\Java\tutorial_client_eventing\res\metadata.xml.

3.3.4.8 Client bundle manifest

Each OSGi bundle requires a “manifest”. This a plain text file that stores some general information bout the bundle. For example, it also includes the name of the metadata file:

Manifest-Version: 1.0

Bundle-Description: Bundle discovers the sample Amigo service and registers

for event notification

Bundle-Name: Tutorial-Client-Eventing

Bundle-Activator:

de.ims.fraunhofer.amigo.tutorial.client_eventing.AmigoCoreActivator

Created-By: Fraunhofer IMS

Bundle-Vendor: Fraunhofer IMS

Import-Package: com.francetelecom.amigo.core,

org.ungoverned.gravity.servicebinder,

org.apache.log4j

Bundle-Copyright: Less General Public License

Bundle-ClassPath: .

Metadata-location: metadata.xml

This manifest (named manifest.mf) is stored in the same directory as the metadata file.

3.3.4.9 Client activator

To start or service as a OSGi bundle a activator class is needed. This class will be used by OSGi to start the service. This file is Web Services\Code\Java\tutorial_server\src\de\ims\fraunhofer\amigo\tutorial\client_eventing\AmigoCoreActivator.java.

packagepackagepackagepackage de.ims.fraunhofer.amigo.tutorial.client_eventing; importimportimportimport org.apache.log4j.Logger; importimportimportimport org.osgi.framework.BundleContext; importimportimportimport org.ungoverned.gravity.servicebinder.GenericActivator; publicpublicpublicpublic classclassclassclass AmigoCoreActivator exexexextendstendstendstends GenericActivator { privateprivateprivateprivate finalfinalfinalfinal Logger m_logger = Logger.getLogger(AmigoCoreActivator.classclassclassclass); publicpublicpublicpublic voidvoidvoidvoid start(BundleContext context) throwsthrowsthrowsthrows Exception { supersupersupersuper.start(context); m_logger.info("Client started."); } }

3.3.4.1 Run the client

In order to run the eventing client, it must be loaded into Oscar as described in Section 3.2.2.11. As a result, the log window will show how event are generated and processed by the application bundles:

June 2007 Confidential

Amigo IST-2004-004182 62/63

June 2007 Confidential

Amigo IST-2004-004182 63/63

4 Recommended next steps Now that you have learned about using Web Services in Amigo you may be interested in how to continue in the most efficient way in order to become more familiar with the Amigo middleware. To this end this document gives some recommended training documents you should consider to read:

� CMS Tutorial: a tutorial about writing Amigo middleware compatible context sources and context clients. It provides an in-depth view of how to implement a context source providing context information and how to implement a context client using this context information. A more complex example is followed by a simple tutorial, guiding the user step-by-step through the process of writing basic elements of a context source and a context client.


Recommended