Level: Intermediate Andrew Borley (borley@uk.ibm.com), Software Engineer, IBM UK
03 May 2007 You can use your existing code to create service components. Learn how to expose your scripts as SCA components and Web services using the Python, Ruby,
and Web services support in Apache Tuscany SCA for C++. Create reusable, composable
SCA components that are linked together within composites and exposed and invoked
using whichever technologies are most suitable to the system being built.
Learning about Tuscany
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.
Introduction
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.
Testing the new
component
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
|
Conclusion
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.
Resources Learn
Get products and technologies
Discuss
About the author  | 
|  | 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. |
Rate this page
|