SCA

External service example

multiworldclock/multiworldclock.c

Note that even though similar to the previous service injection example, in this example the (base) worldclock is deployed as web service and accessed by multiworldclock via the network (rather than via loading the implementation lib into the process space). The calling component assesses endpoint details of the service/operation to invoke by inspecting the called component's WSDL (referenced in the wsdlElement attribute of the binding.ws element). This example also demonstrates using WS-Addressing.

multiworldclock/multiworldclock.wsdl
WSDL file for multiworldclock service
multiworldclock/multiworldclock.composite
SCA composite file for multiworldclock component
multiworldclock/META-INF/sca-contribution.xml
SCA contribution file for multiworldclock contribution
worldclock/worldclock.c
Implementation source for worldclock service (the same as in previous examples)
worldclock/worldclock.wsdl
WSDL file for worldclock service
worldclock/worldclock.composite
SCA composite file for worldclock component
worldclock/META-INF/sca-contribution.xml
SCA contribution file for worldclock contribution

multiworldclock/multiworldclock.c

#include "SCA.h"
#include "SDO.h"

static DATAOBJECT assembleBaseWorldclockRequest(char *city, int hour,
	char *cityToRequestTypeFor);
static DATAOBJECT extractResultTime(DATAOBJECT worldclockResponse);

DATAOBJECT getLocalTimesForCities(DATAOBJECT request) {

	SDOFACTORY df = getDataFactory();

	SDOTYPE requestType =
		findType(df,
			"http://www.example.com/multiworldclock.xsd",
			"worldclockRequest");

	SDOPROPERTY requestTimeProp =
		getTypePropertyByName(requestType, "localTime");

	DATAOBJECT requestTimeObj = getDataObject(request, requestTimeProp);

	SDOPROPERTY cityProp =
		getTypePropertyByName(getPropertyType(requestTimeProp), "city");
	SDOPROPERTY hourProp =
		getTypePropertyByName(getPropertyType(requestTimeProp), "hour");

	char* requestCity = getCString(requestTimeObj, cityProp);
	int requestTime = getInt(requestTimeObj, hourProp);

	SDOPROPERTY otherCitiesProp =
		getTypePropertyByName(requestType, "cityToRequestTimeFor");

	SDOLIST otherCities = getList(request, otherCitiesProp);

	// Locate service reference to invoke
	SCAREF worldclockServiceRef;
	token_t worldclockServiceRefToken;
	worldclockServiceRef = &worldclockServiceRefToken;
	int compCode;
	int reason;
	SCAGetReference("worldclock", worldclockServiceRef, &compCode, &reason);

	// TODO: handle error

	SDOLIST resultList = listAlloc();

	for (int i = 0; i < getCountList(otherCities); i++) {
		int compCode, reason;
		DATAOBJECT otherCity = getDataObjectList(otherCities, i);
		char *otherCityStr = getCString(otherCity, otherCitiesProp);

		// assemble (base, single-city) worldclockRequest for each requested
		// city in list
		DATAOBJECT baseWorldclockRequest =
			assembleBaseWorldclockRequest(requestCity, requestTime, otherCityStr);

		DATAOBJECT baseWorldclockResponse;
		SCAInvoke(worldclockServiceRef, "getLocalTimeForCity",
			0, baseWorldclockRequest,
			0, &baseWorldclockResponse,
			&compCode, &reason);

		// handle error: skip a "worldclock-fault" response
		// indicating an unknown city to request time for,
		// while keeping reporting results for known cities;
		// other errors will terminate the service invocation
		// and propgagte as failure of the aggregated service
		// as a whole
		if (compCode != SCACC_OK) {
			DATAOBJECT faultObj = doAlloc(df,
				"http://www.w3.org/2001/XMLSchema",
				"anyType");

			size_t buflen = 0;
			char *faultName;
			SCAGetFaultMessage(worldclockServiceRef,
				&buflen,
				&faultName,
				faultObj,
				&compCode, &reason);

			if (strcmp("worldclock-fault", faultName)) {
				DATAOBJECT faultResponse = doAlloc(df,
					"http://www.w3.org/2001/XMLSchema",
					"anyType");

				setCStringByName(faultResponse,
					"faultstring",
					"unrecognized city name");

				setCStringByName(faultResponse,
					"faultcode",
					"soap:Client");

				SCASetFaultMessage("worldclock",
					"getLocalTimeForCities",
					"worldclock-fault",
					0,
					faultResponse,
					&compCode,
					&reason);

                		return 0;
			}
			continue;
		}

		// dig out result (city, hour) pair,
		// re-assemble, and append to aggregate result 
		DATAOBJECT multiWorldclockTime = extractResultTime(baseWorldclockResponse);

		appendDataObjectList(resultList, multiWorldclockTime);
	}


	// construct aggregate response
	SDOTYPE multiWorldclockResponseType =
		findType(df,
			"http://www.example.com/multiworldclock.xsd",
			"worldclockResponse");

	SDOPROPERTY multiWorldclockResponseTimeProp =
		getTypePropertyByName(multiWorldclockResponseType, "remoteTime");

	DATAOBJECT multiWorldclockResponse =
		doAllocByType(getDataFactory(), multiWorldclockResponseType);
	setList(multiWorldclockResponse, multiWorldclockResponseTimeProp, resultList);

	return multiWorldclockResponse;
}

// returns a request obj for base (non-multi) worldclock service
static DATAOBJECT assembleBaseWorldclockRequest(char *city, int hour, char *cityToRequestTimeFor) {

	SDOFACTORY df = getDataFactory();

	SDOTYPE baseWorldclockRequestType =
		findType(df,
			"http://www.example.com/worldclock.xsd",
			"worldclockRequest");

	SDOTYPE baseWorldclockRequestTimeType =
		findType(df,
			"http://www.example.com/worldclock.xsd",
			"time");

	DATAOBJECT baseWorldclockRequest =
		doAllocByType(getDataFactory(), baseWorldclockRequestType);

	SDOPROPERTY baseWorldclockRequestTimeProp =
		getTypePropertyByName(baseWorldclockRequestType, "local-time");

	DATAOBJECT baseWorldclockRequestTime =
		doAllocByType(getDataFactory(), baseWorldclockRequestTimeType);

	setCStringByName(baseWorldclockRequestTime, "city", city);
	setIntByName(baseWorldclockRequestTime, "hour", hour);

	setDataObject(baseWorldclockRequest, baseWorldclockRequestTimeProp,
		baseWorldclockRequestTime);

	setCStringByName(baseWorldclockRequest, "city-to-request-time-for", cityToRequestTimeFor);

	return baseWorldclockRequest;
}

DATAOBJECT extractResultTime(DATAOBJECT worldclockResponse) {

	SDOFACTORY df = getDataFactory();

	SDOTYPE responseType = findType(df,
		"http://www.example.com/worldclock.xsd",
		"worldclockResponse");

	SDOTYPE timeType = findType(df,
		"http://www.example.com/worldclock.xsd",
		"time");

	SDOPROPERTY responseTimeProp = getTypePropertyByName(responseType, "remote-time");

	DATAOBJECT responseTime = getDataObject(worldclockResponse, responseTimeProp);

	char *city = getCStringByName(responseTime, "city");
	int hour = getIntByName(responseTime, "hour");

	SDOTYPE multiworldclockTimeType = findType(df,
		"http://www.example.com/multiworldclock.xsd",
		"time");

	DATAOBJECT multiworldclockTime =
		doAllocByType(getDataFactory(), multiworldclockTimeType);

	// set city, hour in result obj
	SDOPROPERTY multiworldclockTimeCityProp =
        	getTypePropertyByName(multiworldclockTimeType, "city");
	SDOPROPERTY multiworldclockTimeHourProp =
        	getTypePropertyByName(multiworldclockTimeType, "hour");
	setCString(multiworldclockTime, multiworldclockTimeCityProp, city);

	setInt(multiworldclockTime, multiworldclockTimeHourProp, hour);

	return multiworldclockTime;
}

multiworldclock/multiworldclock.wsdl

<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions
  targetNamespace="http://www.example.com/multiworldclock.wsdl"
  xmlns="http://www.example.com/multiworldclock.wsdl"
  xmlns:t="http://www.example.com/multiworldclock.xsd"
  xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
  xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl"
  xmlns:sca-c="http://docs.oasis-open.org/ns/opencsa/sca-c-cpp/c/200901"
  xsi:schemaLocation="http://www.w3.org/ns/wsdl http://www.w3.org/2007/06/wsdl/wsdl20.xsd">

	<wsdl:types>

		<xs:schema targetNamespace="http://www.example.com/multiworldclock.xsd"
			          elementFormDefault="qualified">

			<xs:import namespace="http://www.example.com/worldclock.xsd" schemaLocation="worldclock.xsd"/>

			<xs:complexType name="time">
				<xs:sequence>
					<xs:element name="city" type="xs:string"/>
					<xs:element name="hour" type="xs:int"/>
				</xs:sequence>
			</xs:complexType>

			<xs:complexType name="worldclockRequest">
				<xs:sequence>
					<xs:element name="localTime" type="t:time"/>
					<xs:element name="cityToRequestTimeFor" type="xs:string" maxOccurs="unbounded"/>
				</xs:sequence>
			</xs:complexType>

			<xs:complexType name="worldclockResponse">
				<xs:sequence>
					<xs:element name="remoteTime" type="t:time" maxOccurs="unbounded"/>
				</xs:sequence>
			</xs:complexType>

			<xs:element name="worldclockRequest" type="t:worldclockRequest"/>
			<xs:element name="worldclockResponse" type="t:worldclockResponse"/>
		</xs:schema>

	</wsdl:types>

	<wsdl:message name="worldclockRequest">
  		<wsdl:part name="request" element="t:worldclockRequest"/>
	</wsdl:message>

	<wsdl:message name="worldclockResponse">
  		<wsdl:part name="response" element="t:worldclockResponse"/>
	</wsdl:message>

	<wsdl:message name="worldclockFaultResponse">
  		<wsdl:part name="message" element="xs:string"/>
</wsdl:message>

<wsdl:portType name="worldclock-ops">
	<sca-c:bindings>
		<sca-c:enableWrapperStyle>false</sca-c:enableWrapperStyle>
	</sca-c:bindings>
	<wsdl:operation name="getLocalTimesForCities">
		<wsdl:input message="worldclock-request"
		            wsaw:Action="http://www.example.com/multiworldclock/getLocalTimesForCities"/>
		<wsdl:output message="worldclock-response"/>
		<wsdl:fault name="worldclock-fault" message="worldclock-fault-response"/>
	</wsdl:operation>
</wsdl:portType>

<wsdl:binding name="soap-binding" type="worldclock-ops">

  	<soap:binding style="document"
		              transport="http://schemas.xmlsoap.org/soap/http"/>

		<wsdl:operation name="getLocalTimesForCities">
    			<soap:operation soapAction="http://www.example.com/multiworldclock.wsdl/getLocalTimesForCities"/>
			<wsdl:input>
				<soap:body use="literal"/>
			</wsdl:input>
			<wsdl:output>
				<soap:body use="literal"/>
    			</wsdl:output>
			<wsdl:fault name="worldclock-fault">
				<soap:fault name="worldclock-fault" use="literal"/>
			</wsdl:fault>
		</wsdl:operation>
</wsdl:binding>

<wsdl:service name="worldclock-service">
	<wsdl:port name="workdclock-ops" binding="soap-binding">
		<soap:address location="http://localhost/services/multiworldclock"/>
	</wsdl:port>
</wsdl:service>

</wsdl:definitions>

multiworldclock/multiworldclock.composite

<?xml version="1.0" encoding="ASCII"?>
<composite xmlns="http://docs.oasis-open.org/ns/opencsa/sca/200912" targetNamespace="http://www.pmsca.com" name="WorldclockComposite">

	<!-- only contains multiworldclock stuff; is expected to be included
	     into a composite where base worldlock is defined -->

	<!-- expose multiworldclock via SOAP -->
	<service name="multiworldclock" promote="multiworldclock-impl">
		<interface.wsdl interface="http://www.pmsca.com/multiworldclock.wsdl#wsdl.interface(multiworldclock-ops)"/>
		<binding.ws wsdlElement="http://www.pmsca.com/multiworldclock.wsdl#wsdl.port(worldclock-service/workdclock-ops)"/>
	</service>

	<!-- multiworldclock impl (aggregates worldclock service) -->
	<component name="multiworldclock-impl">
		<implementation.c module="multiworldclock" library="true" componentType=""/>
		<service name="multiworldclock"/>
		<reference name="worldclock">
			<binding.ws wsdlElement="http://www.pmsca.com/worldclock.wsdl#wsdl.port(worldclock-service/worldclock-ops)"/>
		</reference>
	</component>
</composite>

multiworldclock/META-INF/sca-contribution.xml

<?oasis-xml-catalog catalog="catalog.xml"?>
<contribution xmlns="http://docs.oasis-open.org/ns/opencsa/sca/200912"
              xmlns:multiworldclock="http://www.pmsca.com/multiworldclock">
	<deployable composite="multiworldclock:MultiWorldclockComposite.composite"/>
	<import namespace="http://www.pmsca.com/worldclock.wsdl" location="../worldclock.wsdl"/>
	<!-- TODO: FIXME: export composite namespace automatically -->
	<export namespace="http://www.pmsca.com/multiworldclock"/>

	<!-- WSDL -->
	<export namespace="http://www.pmsca.com/multiworldclock.wsdl"/>
</contribution>

worldclock/worldclock.c

#include <stdio.h>

#include "SDO.h"
#include "SCA.h"

static int utc_timezone_offset(char *city);
static int calculate_time_diff(int hour, char *city, char *otherCity);

/*
 * Implementation of worldclock service. 
 */
DATAOBJECT getLocalTimeForCity(DATAOBJECT request) {
		
	SDOFACTORY df = getDataFactory();

	SDOTYPE requestType = getType(request);
	if (requestType == 0) {
		requestType = findType(df,
			"http://www.example.com/worldclock.xsd",
			"worldclockRequest");
	}
	
	SDOPROPERTY requestTimeProp = getTypePropertyByName(requestType, "local-time");
	//fprintf(stderr, "got requestTimeProp=%p\n", requestTimeProp);
	DATAOBJECT requestTimeObj = getDataObject(request, requestTimeProp);
	//TODO: use/support short form eg.
	//DATAOBJECT requestTime = getDataObjectByName(request, "local-time");

	//fprintf(stderr, "got requestTimeObj=%p\n", requestTimeObj);

	SDOPROPERTY cityProp = getTypePropertyByName(getPropertyType(requestTimeProp), "city");
	//fprintf(stderr, "got cityProp=%p\n", cityProp);
	char* requestCity = getCString(requestTimeObj, cityProp);
	//was:char* requestCity = getCStringByName(requestTimeObj, "city");

	//fprintf(stderr, "got requestCity=%s\n", requestCity);

	SDOPROPERTY hourProp = getTypePropertyByName(getPropertyType(requestTimeProp), "hour");
	//fprintf(stderr, "got hourProp=%p\n", hourProp);
	int requestTime = getInt(requestTimeObj, hourProp);
	//was: int requestTime = getIntByName(requestTimeObj, "hour");
	//fprintf(stderr, "got hour=%d\n", requestTime);

	SDOPROPERTY otherCityProp = getTypePropertyByName(requestType, "city-to-request-time-for");
	//fprintf(stderr, "got city-to-request-time-for prop=%p\n", otherCityProp);
	char *requestOtherCity = getCString(request, otherCityProp);
	//fprintf(stderr, "get other city=%s\n", requestOtherCity);
	//was:char* requestOtherCity =
	//	getCStringByName(request, "city-to-request-time-for");

	int time_diff = calculate_time_diff(requestTime, requestCity, requestOtherCity);

	if (time_diff < -24 || time_diff > 24) {
		int compcode, reason;
		DATAOBJECT faultResponse = doAlloc(df,
			"http://www.w3.org/2001/XMLSchema",
			"anyType");
		setCStringByName(faultResponse,
			"faultstring",
			"unrecognized city name");
		setCStringByName(faultResponse,
			"faultcode",
			"soap:Client");
		SCASetFaultMessage("worldclock",
			"getLocalTimeForCity",
			"worldclock-fault",
			0,
			faultResponse,
			&compcode,
			&reason);
 		return 0;
	}

	SDOTYPE responseType = findType(df,
		/* "http://www.example.com/worldclock.xsd", */
		getURI(requestType),
		"worldclockResponse");

	SDOTYPE timeType = findType(df,
		/* "http://www.example.com/worldclock.xsd", */
		getURI(requestType),
		"time");

	DATAOBJECT response = doAllocByType(getDataFactory(), responseType);
	DATAOBJECT responseTime = doAllocByType(getDataFactory(), timeType);

	setCStringByName(responseTime, "city", requestOtherCity);
	setIntByName(responseTime, "hour", time_diff);

	SDOPROPERTY responseTimeProp = getTypePropertyByName(responseType, "remote-time");
	setDataObject(response, responseTimeProp, responseTime);
	/* short form:
	setDataObjectByName(response, "remote-time", responseTime);
	*/

	return response;
}

static int calculate_time_diff(int hour, char *city, char *otherCity) {
	return hour + utc_timezone_offset(city) - utc_timezone_offset(otherCity); 
}

static int utc_timezone_offset(char *city) {
	if (!strcmp("Bangalore", city)) return +5;
	if (!strcmp("Berlin", city)) return +1;
	if (!strcmp("San Francisco", city)) return -8;

	// ...

	// return 100 to indicate unknown argument city
	return 100;
}

worldclock/worldclock.wsdl

<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions
  targetNamespace="http://www.example.com/worldclock.wsdl"
  xmlns="http://www.example.com/worldclock.xsd"
  xmlns:t="http://www.example.com/worldclock.xsd"
  xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
  xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl"
  xmlns:sca-c="http://docs.oasis-open.org/ns/opencsa/sca-c-cpp/c/200901"
  xsi:schemaLocation="http://www.w3.org/ns/wsdl http://www.w3.org/2007/06/wsdl/wsdl20.xsd">

	<wsdl:types>
		<xs:schema targetNamespace="http://www.example.com/worldclock.xsd"
			          elementFormDefault="qualified">

			<!--
			<xs:include schemaLocation="worldclock.xsd"/>
			-->

			<xs:complexType name="time">
				<xs:sequence>
					<xs:element name="city" type="xs:string"/>
					<xs:element name="hour" type="xs:int"/>
				</xs:sequence>
			</xs:complexType>

			<xs:complexType name="worldclockRequest">
				<xs:sequence>
					<xs:element name="local-time" type="t:time"/>
					<xs:element name="city-to-request-time-for" type="xs:string"/>
				</xs:sequence>
			</xs:complexType>

			<xs:complexType name="worldclockResponse">
				<xs:sequence>
					<xs:element name="remote-time" type="t:time"/>
				</xs:sequence>
			</xs:complexType>

			<xs:element name="worldclockRequest" type="t:worldclockRequest"/>
			<xs:element name="worldclockResponse" type="t:worldclockResponse"/>
		</xs:schema>

	</wsdl:types>

	<wsdl:message name="worldclockRequest">
  		<wsdl:part name="request" element="t:worldclockRequest"/>
	</wsdl:message>

	<wsdl:message name="worldclockResponse">
  		<wsdl:part name="response" element="t:worldclockResponse"/>
	</wsdl:message>

	<wsdl:message name="worldclockFaultResponse">
  		<wsdl:part name="message" element="xs:string"/>
</wsdl:message>

<wsdl:portType name="worldclock-ops">
	<sca-c:bindings>
		<sca-c:enableWrapperStyle>false</sca-c:enableWrapperStyle>
	</sca-c:bindings>
	<wsdl:operation name="getLocalTimeForCity">
		<wsdl:input message="worldclock-request"
		            wsaw:Action="http://www.example.com/worldclock/getLocalTimeForCity"/>
		<wsdl:output message="worldclock-response"/>
		<wsdl:fault name="worldclock-fault" message="worldclock-fault-response"/>
	</wsdl:operation>
</wsdl:portType>

<wsdl:binding name="soap-binding" type="worldclock-ops">

  	<soap:binding style="document"
		              transport="http://schemas.xmlsoap.org/soap/http"/>

		<wsdl:operation name="getLocalTimeForCity">
    			<soap:operation soapAction="http://www.example.com/worldclock.wsdl/getLocalTimeForCity"/>
			<wsdl:input>
				<soap:body use="literal"/>
			</wsdl:input>
			<wsdl:output>
				<soap:body use="literal"/>
    			</wsdl:output>
			<wsdl:fault name="worldclock-fault">
				<soap:fault name="worldclock-fault" use="literal"/>
			</wsdl:fault>
		</wsdl:operation>
</wsdl:binding>

<wsdl:service name="worldclock-service">
	<wsdl:port name="worldclock-ops" binding="soap-binding">
		<soap:address location="http://localhost:8080/services/worldclock"/>
	</wsdl:port>
</wsdl:service>

</wsdl:definitions>

worldclock/worldclock.composite

<composite xmlns="http://docs.oasis-open.org/ns/opencsa/sca/200912"
           targetNamespace="http://www.pmsca.com"
           name="WorldclockComposite">

	<!-- expose base worldclock via SOAP -->
	<service name="worldclock" promote="worldclock-impl">
		<interface.wsdl interface="http://www.pmsca.com/worldclock.wsdl#wsdl.interface(worldclock-ops)"/>
		<binding.ws wsdlElement="http://www.pmsca.com/worldclock.wsdl#wsdl.port(worldclock-service/worldclock-ops)"/>
	</service>

	<!-- worldclock impl -->
	<component name="worldclock-impl">
		<implementation.c module="worldclock" library="true" componentType=""/>
		<service name="worldclock"/>
	</component>

</composite>

worldclock/META-INF/sca-contribution.xml

<?oasis-xml-catalog catalog="catalog.xml"?>
<contribution xmlns="http://docs.oasis-open.org/ns/opencsa/sca/200912"
	      xmlns:worldclock="http://www.pmsca.com/worldclock">
	<!-- make component available for deployment/to other components -->
	<deployable composite="worldclock:WorldclockComposite.composite"/>
	<!-- TODO: FIXME: export composite namespace automatically -->
	<export namespace="http://www.pmsca.com/worldclock"/>
	<!-- export namespaces of wsdl and xsd -->
	<export namespace="http://www.pmsca.com/worldclock.wsdl"/>
</contribution>