Apache Tuscany is a project undergoing incubation at the Apache Software Foundation. One of the goals of the project is to produce a C++ runtime that implements the following Service Component Architecture (SCA) specifications (see Resources for more information):
- SCA Assembly Model
- SCA C++ Client and Implementation
This article describes the development and deployment of Python and Ruby service components, and it explains how to expose service components as Web services using the Apache Tuscany C++ runtime, based on the Milestone 2 release.
The Tuscany C++ Service Component Architecture (SCA) runtime enables you to build SCA components using standard C++, Python, or Ruby code. You can then deploy them to a location where the SCA runtime can locate and load them. These components can be wired together, reused, replaced with other components written in different languages, or exposed as Apache Axis Web services
You create a simple SCA component in Python. Then, you create a second component in Ruby and wire the two together. You then expose the components as a Web service, and you create a client (again using SCA components) to call the service.
Begin by downloading the Tuscany C++ Milestone 2 release from the Tuscany Web site (see Resources) and getting the PythonCalculator and the RubyCalculator samples working by following the documentation. The working samples ensure that sure your SCA, Python, Ruby, and Axis2/C installations are valid, that you have the TUSCANY_SCACPP, TUSCANY_SDOCPP and AXIS2C_HOME environment variables set correctly, and that you have enabled the Tuscany Python and Ruby extensions.
Starting with a Python component
Start with a simple Python script that you enable, such as an SCA component. The module below provides some useful methods for discovering the number of e-mails available on a POP3 e-mail server and for retrieving the contents of those e-mails one by one.
Listing 1. pop3access.py Python script
import poplib
# Returns the number of messages on the POP3 server
def getNumOfMessages():
mail = connect()
numOfMessages = mail.stat()[0]
mail.quit()
return numOfMessages
# Returns a single message from the POP3 server
def getMessageData(msgNum):
mail = connect()
data = "Message number " + str(msgNum) + "\n"
for line in mail.retr(msgNum)[1]:
data += str(line)
data += "\n"
mail.quit()
return data
# Used by the 2 methods above to connect to the e-mail server
def connect():
# Use poplib.POP3_SSL for POP3 servers that require security
mail = poplib.POP3("pop.mycompany.com")
mail.user("myusername")
mail.pass_("mypassword")
return mail
|
So, you have a Python module that provides some useful function, but as it is, there is not much that you can do with it, aside from writing a Python script that uses the methods. As an SCA component within an SCA composite, it could do much more. Here is how to turn it into a component.
- Create a directory called emailsample, with a subdirectory called emailcomposite.
- Copy the pop3access.py script into the emailcomposite directory.
- Create an XML file called email.composite in the emailcomposite directory
containing the following XML:
Listing 2. email.composite SCA composite file<?xml version="1.0" encoding="UTF-8"?> <composite xmlns="http://www.osoa.org/xmlns/sca/1.0" name="email"> <component name="email.pop3access.component"> <implementation.python module="pop3access"/> </component> </composite>
- Create an XML file called pop3access.componentType in the emailcomposite
directory containing the following XML:
Listing 3. pop3access.componentType SCA ComponentType file<?xml version="1.0" encoding="UTF-8"?> <componentType xmlns="http://www.osoa.org/xmlns/sca/1.0"> <service name="POP3AccessService"> <interface.python/> </service> </componentType>
The Python script is now enabled as an SCA component. The email.composite file defines the components (and, as shown later, references and services) and their implementations. In this example, the component has a Python implementation that the SCA runtime finds using the module attribute. The pop3access.componentType file tells the SCA runtime which services (and, later, references and properties) the implementation script provides. In this example, the implementation provides a service named POP3AccessService.
So now test the component with a quick SCA client, written in Python, again.
First, enable your composite in the SCA runtime, using another composite file! Define a root composite file that specifies which components are used in the entire SCA system and that generally contains components implemented as composites themselves. This enables the real function to be separated into discrete parts that can then be assembled up into the entire system. In this example, create an email.solution.composite file in the emailsample directory containing the following XML:
Listing 4. email.solution.composite SCA composite file
<?xml version="1.0" encoding="UTF-8"?>
<composite xmlns="http://www.osoa.org/xmlns/sca/1.0" name="email.solution">
<component name="EmailComponent">
<implementation.composite name="email"/>
</component>
</composite>
|
This file makes the newly created composite (containing the Python component) into a component that a client can reach. Now you can write a client that can find and invoke the functions in the Python component. Create a new subdirectory in the email directory called client, and create a Python script there called client.py. You can use this code:
Listing 5. client.py Python script
import sca
# Locate the email service
emailService = sca.locateservice("email.pop3access.component")
print "You have", emailService.getNumOfMessages(), "messages available"
print "First message:"
print emailService.getMessageData(1)
|
Aside from that locateservice call, it looks like you are just using that pop3access module directly. Now, to run this, set up a few Tuscany environment variables, and then run the script. On Windows®, open a command prompt, and use the following commands:
Listing 6. Running the client on Windows
set TUSCANY_SCACPP_SYSTEM_ROOT=C:\path\to\emailsample\
set TUSCANY_SCACPP_DEFAULT_COMPONENT=EmailComponent
set PATH=%TUSCANY_SCACPP%\bin;%PATH%
set PATH=%TUSCANY_SDOCPP%\bin;%PATH%
set PATH=%TUSCANY_SCACPP%\extensions\python\bin;%PATH%
set PYTHONPATH=%TUSCANY_SCACPP%\extensions\python\bin
cd C:\path\to\emailsample\client
python client.py
|
On Linux®, use the following commands:
Listing 7. Running the client on Linux
export TUSCANY_SCACPP_SYSTEM_ROOT=/path/to/emailsample
export TUSCANY_SCACPP_DEFAULT_COMPONENT=EmailComponent
export LD_LIBRARY_PATH=$TUSCANY_SCACPP/bin:$LD_LIBRARY_PATH
export LD_LIBRARY_PATH=$TUSCANY_SCACPP/extensions/python/bin:$LD_LIBRARY_PATH
export LD_LIBRARY_PATH=$TUSCANY_SDOCPP/bin:$LD_LIBRARY_PATH
export PYTHONPATH=$TUSCANY_SCACPP/extensions/python/bin
cd /path/to/emailsample/client
python client.py
|
These environment variables set the root path that Tuscany loads any composite files from, sets the default component (which is the component you created in the email.solution.composite file that links to your Python composite), and makes sure the path is correct. Your results should be like the following:
Listing 8. Client results
You have 8 messages available
First message:
Message number 1
X-Gmail-Received: 12345678905be7fdffc1ba5c3cea07
Delivered-To: username@mycompany.com
Received: by 169.78.50.5 with SMTP id x5cs2436hux;
Fri, 5 May 2006 12:38:06 -0700 (PDT)
Received: by 169.35.66.12 with SMTP id 1234asd;
Fri, 05 May 2006 12:38:05 -0700 (PDT)
Return-Path: <myproject-dev-return-2512-username=mycompany.com@ws.mycompany.com>
Received: from mail.mycompany.com (mail.mycompany.com [192.237.227.199])
by mx.mycompany.com with SMTP id f19si2206285pyf.2006.05.05.12.38.05;
Fri, 05 May 2006 12:38:05 -0700 (PDT)
Received-SPF: pass (mycompany.com: domain of
myproject-dev-return-2512-username=mycompany.com@ws.mycompany.com designates
192.237.227.199 as permitted sender)
Received: (qmail 51644 invoked by uid 500); 5 May 2006 19:38:04 -0000
Mailing-List: contact myproject-dev-help@ws.mycompany.com; run by ezmlm
Precedence: bulk
List-Help: <mailto:myproject-dev-help@ws.mycompany.com>
List-Unsubscribe: <mailto:myproject-dev-unsubscribe@ws.mycompany.com>
List-Post: <mailto:myproject-dev@ws.mycompany.com>
List-Id: <myproject-dev.ws.mycompany.com>
Reply-To: myproject-dev@ws.mycompany.com
Delivered-To: mailing list myproject-dev@ws.mycompany.com
Received: (qmail 51634 invoked by uid 99); 5 May 2006 19:38:04 -0000
Received: from dev.mycompany.com (HELO dev.mycompany.com) (1192.211.166.49)
by mycompany.com (qpsmtpd/0.29) with ESMTP; Fri, 05 May 2006 12:38:04 -0700
X-ASF-Spam-Status: No, hits=0.0 required=10.0
tests=
X-Spam-Check-By: mycompany.com
Received: from [192.237.227.198] (HELO mailserver.mycompany.com)
by mycompany.com (qpsmtpd/0.29) with ESMTP; Fri, 05 May 2006 12:38:03 -0700
Received: from mailserver (localhost [127.0.0.1])
by mailserver.mycompany.com (Postfix) with ESMTP id C70F87142BB
for <myproject-dev@ws.mycompany.com>; Fri, 5 May 2006 19:37:27 +0000 (GMT)
Message-ID: <123456789@mailserver>
Date: Fri, 5 May 2006 19:37:27 +0000 (GMT+00:00)
From: "Andy" <andy@ws.mycompany.com>
To: myproject-dev@ws.mycompany.com
Subject: Apache Tuscany
In-Reply-To: <1234.56789@mailserver>
MIME-Version: 1.0
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: 7bit
X-Virus-Checked: Checked by ClamAV on mycompany.com
The Apache Tuscany project could help in myproject - check it out
at http://incubator.apache.org/tuscany
Cheers!
Andy
|
Wow, that is a lot of data for one simple e-mail! Chances are that you are not interested in most of that stuff. You just want to see the Date, Subject, From, and To fields, and the message itself. Read on.
Connecting up a Ruby component
There is a chunk of Ruby code that can run a quick regular expression over some data and return the results. You could recode this in Python and add it into your script, but using Tuscany means we do not have to!
Put this Ruby script, named emailregex.rb, into the emailcomposite directory.
Listing 9. emailregex.rb Ruby script
# Gets the value of a named field from an e-mail
def getEmailField(fieldName, data)
# Construct a regular expression to find the field
pattern = '^'+fieldName+': (.*?)$'
re = Regexp.new(pattern)
re =~ data
# If no data, return an empty string
if $1 == nil
return ''
end
return $1
end
# Gets the message data from an e-mail
def getEmailMessage(data)
# Get the message via a regular expression
re = Regexp.new('$^$^(.*)', Regexp::MULTILINE)
re =~ data
# If no data, return an empty string
if $1 == nil
return ''
end
return $1
end
|
Turning a Ruby script into an SCA component is even easier than it is for Python code. You only need to define the component element in your composite file. Ruby does not require the componentType file. The services, references, and properties that a component provides are automatically detected based on the Ruby script and the composite file. (In the next Tuscany release, Python will not require the componentType file, either).
Edit the email.composite file to include the new Ruby component. Also, add a reference element to the Python component definition to wire it to the new component. And, replace those hard-coded POP3 server username and password variables in the Python script with SCA properties defined here. Here is the updated XML:
Listing 10. Updated email.composite SCA composite file
<?xml version="1.0" encoding="UTF-8"?>
<composite xmlns="http://www.osoa.org/xmlns/sca/1.0" name="email">
<component name="email.pop3access.component">
<implementation.python module="pop3access"/>
<reference name="regexService">email.regex.component</reference>
<property name="servername">pop.mycompany.com</property>
<property name="username">myusername</property>
<property name="password">mypassword</property>
</component>
<component name="email.regex.component">
<implementation.ruby script="emailregex.rb"/>
</component>
</composite>
|
To get the Python script to call the Ruby component and use those properties, add a couple of bits to the pop3access.py script and the pop3access.componentType file. Here is the updated script:
Listing 11. Updated pop3access.py Python script
import poplib
# Returns the number of messages on the POP3 server
def getNumOfMessages():
mail = connect()
numOfMessages = mail.stat()[0]
mail.quit()
return numOfMessages
# Returns a single message from the POP3 server
def getMessageData(msgNum):
mail = connect()
data = "Message number " + str(msgNum) + "\n"
for line in mail.retr(msgNum)[1]:
data += str(line)
data += "\n"
mail.quit()
# Use the regexService reference
toResult = regexService.getEmailField("To", data)
fromResult = regexService.getEmailField("From", data)
dateResult = regexService.getEmailField("Date", data)
subjectResult = regexService.getEmailField("Subject", data)
messageData = regexService.getEmailMessage(data)
# Construct the result
result = "Date: " + str(dateResult) +"\n"
result += "To: " + str(toResult) +"\n"
result += "From: " + str(fromResult) +"\n"
result += "Subject: " + str(subjectResult) +"\n"
result += str(messageData)
return result
# Used by the 2 methods above to connect to the e-mail server
def connect():
# Use the servername, username and password SCA properties
mail = poplib.POP3(servername)
mail.user(username)
mail.pass_(password)
return mail
|
And here is the updated componentType file:
Listing 12. Updated pop3access.componentType SCA ComponentType file
<?xml version="1.0" encoding="UTF-8"?>
<componentType xmlns="http://www.osoa.org/xmlns/sca/1.0"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<service name="POP3AccessService">
<interface.python/>
</service>
<reference name="regexService">
<interface.python/>
</reference>
<property name="servername" type="xsd:string"/>
<property name="username" type="xsd:string"/>
<property name="password" type="xsd:string"/>
</componentType>
|
With some fairly simple changes, you made a Python script call a Ruby script, and it just looks like a normal call to another Python module. The regexService reference and the servername, username, and password properties look like they are uninitialized variables that will cause the script to fail, but do not worry: they will not fail. They get initialized and assigned the value given in the composite by the Tuscany runtime.
Run that client again (without any changes) with the same commands as above. The only change you need to make is to add the Tuscany Ruby extension onto your path:
Listing 13. Running the client on Windows
...
set PATH=%TUSCANY_SCACPP%\extensions\ruby\bin;%PATH%
...
python client.py
|
You should now see something like the following output:
Listing 14. Client results
You have 8 messages available
First message:
Date: Fri, 5 May 2006 19:37:27 +0000 (GMT+00:00)
To: myproject-dev@ws.mycompany.com
From: "Andy" <andy@ws.mycompany.com>
Subject: Apache Tuscany
The Apache Tuscany project could help in myproject - check it out
at http://incubator.apache.org/tuscany
Cheers!
Andy
|
This is much better!
Exposing the composite as a Web service
So now you have a couple of SCA components providing a useful service. Now, make that service available beyond your local machine by exposing it as a Web service. Tuscany uses Apache Axis2/C (see Resources) to provide its Web service function. In this example, use the Axis simple HTTP server to host your service.
If you ran the PythonCalculator and RubyCalculator samples (as we suggested above), you should have seen them exposed and invoked as Web services under Axis2C. This means you have set up your AXIS2C_HOME environment variable, and you enabled Axis2C to run Tuscany SCA services by deploying the Tuscany Web service extension in to Axis.
The changes you need to make to the example SCA composite are tiny: just one update to the email.composite SCA composite file, as shown in Listing 15.
Listing 15. Updated email.composite SCA composite file
<?xml version="1.0" encoding="UTF-8"?>
<composite xmlns="http://www.osoa.org/xmlns/sca/1.0" name="email">
<service name="EmailWebService">
<binding.ws/>
<reference>email.pop3access.component</reference>
</service>
<component name="email.pop3access.component">
<implementation.python module="pop3access"/>
<reference name="regexService">email.regex.component</reference>
<property name="servername">pop.mycompany.com</property>
<property name="username">myusername</property>
<property name="password">mypassword</property>
</component>
<component name="email.regex.component">
<implementation.ruby script="emailregex.rb"/>
</component>
</composite>
|
Adding that service element to your composite and specifying that it uses the Web service binding (binding.ws) makes the composite expose a service with a Web services interface, similar to how adding a service element in the componentType file exposes the component to other components.
Now you can start the Axis2C simple HTTP server with the following commands on Windows:
Listing 16. Starting the Axis2C Simple HTTP server on Windows
set TUSCANY_SCACPP_SYSTEM_ROOT=C:\path\to\emailsample\
set TUSCANY_SCACPP_DEFAULT_COMPONENT=EmailComponent
set PATH=%TUSCANY_SCACPP%\bin;%PATH%
set PATH=%TUSCANY_SDOCPP%\bin;%PATH%
set PATH=%TUSCANY_SCACPP%\extensions\python\bin;%PATH%
set PATH=%TUSCANY_SCACPP%\extensions\ruby\bin;%PATH%
set PATH=%AXIS2C_HOME%\lib;%PATH%
set PYTHONPATH=%TUSCANY_SCACPP%\extensions\python\bin
cd %AXIS2C_HOME%\bin
.\axis2_http_server.exe
|
On Linux, use the following commands:
Listing 17. Starting the Axis2C Simple HTTP server on Linux
export TUSCANY_SCACPP_SYSTEM_ROOT=/path/to/emailsample
export TUSCANY_SCACPP_DEFAULT_COMPONENT=EmailComponent
export LD_LIBRARY_PATH=$TUSCANY_SCACPP/bin:$LD_LIBRARY_PATH
export LD_LIBRARY_PATH=$TUSCANY_SDOCPP/bin:$LD_LIBRARY_PATH
export LD_LIBRARY_PATH=$TUSCANY_SCACPP/extensions/python/bin:$LD_LIBRARY_PATH
export LD_LIBRARY_PATH=$TUSCANY_SCACPP/extensions/python/bin:$LD_LIBRARY_PATH
export LD_LIBRARY_PATH=$AXIS2C_HOME/lib:$LD_LIBRARY_PATH
export PYTHONPATH=$TUSCANY_SCACPP/extensions/python/bin
cd $AXIS2C_HOME/bin
./axis2_http_server
|
The environment variables you set up here are almost the same as those required for the local client you ran earlier. Just add the Axis2C libraries onto the path.
So you now have Axis providing your SCA composite as a Web service. You need a Web service client to invoke it. You could write a client using the Axis APIs (in Java™ or C), or you could use some other Web service library, such as php_soap for PHP or SOAPpy for Python. Instead, you can simply use SCA to make Web service calls.
First, create another SCA composite that can run on the client computer. Do this by creating a new subdirectory in the emailsample directory named wsclient and creating a wsclient.composite file within it containing the following XML:
Listing 18. wsclient.composite SCA composite file
<?xml version="1.0" encoding="UTF-8"?>
<composite xmlns="http://www.osoa.org/xmlns/sca/1.0" name="wsclient">
<reference name="EmailService">
<binding.ws uri="http://localhost:9090/axis2/services/EmailWebService"/>
</reference>
</composite>
|
This time, you just have a reference element that is wired to the service you have exposed as an Axis2C Web service using the specified address. Make sure you change this address if you are running the server on a different computer or port.
Now, write another root composite file to allow a local client to access the reference, and write the client code itself. This time, write and run a Ruby client. Put the following wsclient.solution.composite XML into the wsclient directory:
Listing 19. wsclient.solution.composite SCA composite file
<?xml version="1.0" encoding="UTF-8"?>
<composite xmlns="http://www.osoa.org/xmlns/sca/1.0" name="wsclient.solution">
<component name="WSClientComponent">
<implementation.composite name="wsclient"/>
</component>
</composite>
|
And Listing 20 shows the Ruby client code. Again, put this file into your wsclient directory.
Listing 20. WSClient.rb Ruby script
require("tuscany_sca_ruby")
emailService = SCA::locateService("EmailService")
puts "You have " << (emailService.getNumOfMessages()).to_s << " messages available"
puts "First message:"
puts emailService.getMessageData(1)
|
You might notice that this code looks almost identical to the Python client you wrote earlier. You are correct: you could, in fact, use that client (with the tweak of changing the name in the sca.locateservice method from email.pop3access.component to EmailService). Instead, you can invoke the Ruby client. You need to set up some environment variables again before running the script. Here are the commands on Windows:
Listing 21. Running the Web service client on Windows
set TUSCANY_SCACPP_SYSTEM_ROOT=C:\path\to\emailsample\wsclient
set TUSCANY_SCACPP_DEFAULT_COMPONENT=WSClientComponent
set PATH=%TUSCANY_SCACPP%\bin;%PATH%
set PATH=%TUSCANY_SDOCPP%\bin;%PATH%
set PATH=%AXIS2C_HOME%\lib;%PATH%
cd C:\path\to\emailsample\wsclient
ruby -I%TUSCANY_SCACPP%\extensions\ruby\bin WSClient.rb
|
Here are the commands for Linux:
Listing 22. Running the Web service client on Linux
export TUSCANY_SCACPP_SYSTEM_ROOT=/path/to/emailsample/wsclient
export TUSCANY_SCACPP_DEFAULT_COMPONENT=WSClientComponent
export LD_LIBRARY_PATH=$TUSCANY_SCACPP/bin:$LD_LIBRARY_PATH
export LD_LIBRARY_PATH=$TUSCANY_SDOCPP/bin:$LD_LIBRARY_PATH
export LD_LIBRARY_PATH=$AXIS2C_HOME/lib:$LD_LIBRARY_PATH
cd /path/to/emailsample/wsclient
ruby -I$TUSCANY_SCACPP/extensions/ruby/bin WSClient.rb
|
The environment variables you set up here direct the Tuscany runtime to use the wsclient.solution.composite and the linked wsclient.composite. Running this client invokes the Web service running on the Axis2C server. You see something similar to the following results returned again:
Listing 23. Web service client results
You have 8 messages available
First message:
Date: Fri, 5 May 2006 19:37:27 +0000 (GMT+00:00)
To: myproject-dev@ws.mycompany.com
From: "Andy" <andy@ws.mycompany.com>
Subject: Apache Tuscany
The Apache Tuscany project could help in myproject - check it out
at http://incubator.apache.org/tuscany
Cheers!
Andy
|
With the help of this article, you took some existing Python code, turned it into an SCA component, and then wired it to a second component that provides some useful function implemented in Ruby. You then exposed your system as a Web service and used a second SCA composite to invoke that service.
This article offered you a taste of the power of Service Component Architecture and the Apache Tuscany implementation, which enables you to use your skills and existing code to create reusable, composable components that are linked together within composites and exposed and invoked using whichever technologies are most suitable to the system being built.
Learn
-
RSS
feed for this series: Request notification for the upcoming articles in this series. (Find out more about RSS feeds of developerWorks content.)
- See Apache Axis2/C for Tuscany's Web services
function.
- Go to the Tuscany Web site to offer input,
suggestions, or contributions to the Apache Tuscany project.
- Refer to the SCA Assembly
Model.
- See the
SCA C++ Client and Implementation for more information.
-
Stay current with developerWorks technical events and webcasts.
-
The IBM SOA Web site offers an overview of SOA and how IBM can help you to get there.
-
Visit the SOA and Web services zone on developerWorks to learn more about SOA.
Get products and technologies
- Download the Tuscany C++ Milestone 2 release from the
Tuscany Web site.
Discuss
-
Participate in developerWorks blogs and get involved in the developerWorks community.
-
Collaborate with a community of architects and developers in the SOA and Web services discussion forums.

Andrew Borley is an IT Specialist at IBM Hursley, UK. He has held various roles including developer, team leader, and project manager and has worked with various technologies in his 7 years at IBM. Since 2001 he has been working with customers on Service-Oriented Architecture projects, and his current role is developing the SCA for C++ implementation within the Apache Tuscany open source SOA project.
Comments (Undergoing maintenance)





