CSC/ECE 517 Fall 2009/wiki2 17 f1

From Expertiza_Wiki
Revision as of 03:35, 10 October 2009 by Maverick (talk | contribs) (→‎SOAP)
Jump to navigation Jump to search

Service Oriented Architecture provides another view of providing functionality based upon services offered in terms of protocols and a specific API. To provide services, platforms rely upon principles and the power that can be expressed through reflection and meta programming. Research and report how these critical concepts relate to and support SOA.

Service Oriented Architectures

Service Oriented Architecture is not a new thing, but has been there since around 1991 by virtue of the CORBA specifications. The earliest SOA architectures were, DCOM and CORBA 1. Service Oriented Architecture (SOA) is a paradigm for organizing and utilizing distributed capabilities that may be under the control of different ownership domains. SOA provides a uniform means to offer, discover, interact with and use capabilities to produce desired effects consistent with measurable preconditions and expectations. It is a collection of services. These services communicate with each other either by simple data passing or it could involve two or more services coordinating some activity by means of some predefined standard connecting services. This makes it easier for software integration of different modules developed by different people. Rather than defining an [http://en.wikipedia.org/wiki/API API], SOA defines the interface in terms of protocols and functionality such that modules following these protocols can easily be integrated together. The typical SOA implementation can be illustrated in the figure:


SOA Functioning


The main characteristics of SOA are:

  • Based on open standards - The governing principles are free for all to use and not protected by intellectual property.
  • Interoperable - Diverse systems should be able to work together (inter-operate)
  • Autonomous - Services have control over the logic they encapsulate.
  • Reusable - Logic is divided into services with the intention of promoting reuse.
  • Reliable - It should not have many flaws
  • Discoverable - Services are designed to be outwardly descriptive so that they can be found and assessed via available discovery mechanisms
  • Loosely-Coupled - Loose coupling occurs when the dependent class contains a pointer only to an interface, which can then be implemented by one or many concrete classes.
  • Stateless - Should exhibit the same behaviour and yield the same result whether called once, or 100 times
  • Composable - Collections of services can be coordinated and assembled to form composite services.
  • Manageable - It should be easy to maintain it
  • Secure - It should not be easily hackable

Core Components of SOA

As mentioned earlier, the services communicate with each other using a predefined protocol. The techniques used for protocol selection and execution form the core components of SOA. While using SOA, we have a service that has tells the type of service its provides using Web Services Description Language (WSDL) and a directory, like Universal Description, Discovery, and Integration (UDDI) that holds this information. The service consumer (one who wants some service) will lookup this directory for the name of the service that can fulfill the consumer's request. The consumer and the service provider communicate using some format (in layman terms a language) like SOAP. These ideology leads to the following components of a SOA implementation.

  • SOAP: Simple Object Access Protocol - As a layman's example of how SOAP procedures can be used, a SOAP message could be sent to a web service enabled web site (for example, a house price database) with the parameters needed for a search. The site would then return an XML-formatted document with the resulting data (prices, location, features, etc). Because the data is returned in a standardized machine-parseable format, it could then be integrated directly into a third-party site.
  • WSDL: Web Services Description Language - Its an XML-based language that provides a model for describing Web Services.
  • UDDI: Universal Discovery, Definition and Integration - Its an XML-based database for companies world-wide to list themselves on the internet
  • BPEL: Business Process Execution Language - Its a language for specifying interaction with web services.
  • ESB: Enterprise Service Bus
  • WS-*: Web Services Standards - a collection of protocols and standards used for exchanging data between applications

Benefits of SOA

The benefits offered by SOA can be directly derived from the characteristics of SOA. The features like loose coupling between the service requests and service provider implies flexibility and reuse of services for different service consumers. The benefits can be summarized as below:

  • Improve long-term value of software assets
  • Improve quality through modularity and testability
  • Reduce development time with composition and re-usability
  • Leverage heterogeneous development environments
  • Avoid vendor and platform lock-in
  • Integrate with ERPs and Enterprise infrastructure software

Use cases for SOA with Ruby

Ruby can be a productive way to:

  • Build web applications over web services
  • Write web services tests with minimal code
  • Write custom web services monitoring and management
  • Integrate ERPs, COTS and custom applications
  • Write SOA glue-code, e.g. custom transformation
  • Perform programmatic web services orchestration
  • Develop basic web services

Reflection in Ruby

One of the many advantages of dynamic languages such as Ruby is the ability to introspect—to examine aspects of the program from within the program itself. This is called reflection. At runtime, we can discover the following things about our ruby program -

  • what objects it contains,
  • the class hierarchy,
  • the attributes and methods of objects, and
  • information on methods.

Ruby provide a module called "ObjectSpace" that lets you to use reflection and see all the above mentioned information. So if you say,

ObjectSpace.each_object { |x| puts x }

will print all living, nonimmediate objects in Ruby process.


ObjectSpace.each_object(Class) { |x| puts x}

will print all classes in Ruby process.

The following code iterates over all the classes, compares their name. If name matches then create object and execute whichever function you like.

    class ClassFromString
    @@counter = 0
    def initialize
    @@counter += 1
    end
    def getCounterValue
    puts @@counter
    end
    end


    def createClassFromString(classname)
    ObjectSpace.each_object(Class) do |x|
    if x.name == classname
    object = x.new
    object.getCounterValue
    object = x.new
    object.getCounterValue
    end
    end
    end
    createClassFromString("ClassFromString")

Once you get the object you can use any method of that object. For e.g. you can use superclass method to get the name of the parent class and so on and can build complete hierarchy dynamically. You can get the information about methods of a given class using methods like private_methods(), protected_methods() which are defined in Object class which is base class for each object.

Distributed Ruby and Marshalling are two reflection-based technologies that let us send objects around the world and through time, which enables use of Service Oriented Architectures.

Marshalling

Java features the ability to serialize objects, letting you store them somewhere and reconstitute them when needed. To "serialize" an object means to convert its state into a byte stream in such a way that the byte stream can be converted back into a copy of the object. You can use this facility, for instance, to save a tree of objects that represent some portion of application state—a document, a CAD drawing, a piece of music, and so on. Ruby calls this kind of serialization marshaling (think of railroad marshaling yards where individual cars are assembled in sequence into a complete train, which is then dispatched somewhere). Saving an object and some or all of its components is done using the method Marshal.dump. Typically, you will dump an entire object tree starting with some given object. Later, you can reconstitute the object using Marshal.load.

Example -

We have a class Chord that holds a collection of musical notes. We’d like to save away a particularly wonderful chord so we can e-mail it to friends. They can then load it into their copy of Ruby and savor it too. Let’s start with the classes for Note and Chord.


Note = Struct.new(:value)
class Note
def to_s
value.to_s
end
end

class Chord
def initialize(arr)
@arr = arr
end
def play
@arr.join('')
end
end


Now we’ll create our masterpiece and use Marshal.dump to save a serialized version of it to disk.


c = Chord.new( [ Note.new("G"),
Note.new("Bb"),
Note.new("Db"),
Note.new("E") ] )
File.open("posterity", "w+") do |f|
Marshal.dump(c, f)
end

Finally, it can be read by our friends.

File.open("posterity") do |f|
chord = Marshal.load(f)
end
chord.play ! "GBbDbE"


Distributed Ruby

Distributed Ruby or DRb allows Ruby programs to communicate with each other on the same machine or over a network. DRb uses remote method invocation (RMI) to pass commands and data between processes. Since we can serialize an object or a set of objects into a form suitable for out-ofprocess storage, we can use this capability for the transmission of objects from one process to another. Using drb, a Ruby process may act as a server, as a client, or as both. A drb server acts as a source of objects, while a client is a user of those objects. To the client, it appears that the objects are local, but in reality the code is still being executed remotely. This can be found in the SOAP implementation of RUBY libraries.


Metaprogramming and SOA

The non - dependence of service consumers with service providers during compile time causes a dynamic runtime binding. Invocation of a wide range of service providers calls for techniques that can help creation of service requests at runtime for compatibility with the binding identified during runtime. This can be achieved by using the technique of Metaprogramming. "Meta" means "self" and hence meta programming can be stated as self programming or program that codes itself. This means that there will be some set of code which will create code at runtime. Using this we can create code that binds the service consumer with service provider. The concept of meta programming is explained by using Ruby language.

Metaprogramming in Ruby

Metaprogramming is inbuilt and used extensively by the language itself. Consider the following standard used syntax to create getters and setters in ruby: "attr_writer :a", "attr_reader :a", "attr_accessor :b". Each of the single lines of code creates the accessor or/and mutators methods for the variables. This is an example of Metaprogramming. The logic written by the base ruby code (in object.c file in the ruby directory) uses the information about the variable name provided by the developer and creates at runtime the accessor and mutator methods. This is at runtime as physically there is no code which is present. Metaprogramming is achieved by the eval method provided by the Ruby language.

class GreetPeople
 eval %{def greeting puts "Hello All!" end}
end
me = GreetPeople.new
puts me.hi

produces Hello All!

One can use define_method to generate classes and methods at runtime. The usage is like below

class Logger
  def self.add_logging(id_string)
    define_method(:log) do |msg|
      now = Time.now.strftime("%H:%M:%S") 
      STDERR.puts "#{now}-#{id_string}: #{self} (#{msg})"
    end
  end
end

class Song < Logger
 add_logging "Tune"
end

class Album < Logger
 add_logging "CD"
end

song = Song.new
song.log("rock on")

The output produced is:

13:26:13-Tune: #<Song:0x0a20e4> (rock on)

Look the part #<Song:0x0a20e4> even if there is no mention of the class Song, the logger method printed the name of the class as it picked it up at runtime from the keyword "self".

Other methods like add_method adds method to the a class at runtime.

SOA with Ruby

Ruby has some standard libraries for each functional block in the SOA architecture. These libraries are

XML 1.0 - REXML, others
SOAP 1.1 - SOAP4R, AWS
WSDL 1.1 - WSDL4R, AWS
UDDI V2 - UDDI4R

Each of these libraries use Metaprogramming in some form or the other. Below is the description for each of the libraries

XML Parsing

XML is the foundation for the web services. The request, identification of service, method and its parameters of the service, the response, all are in the form of an xml. Thus XML parsing and construction is very important part for effective SOA implementation. The XML parsing is handled by the REXML library which is coded in native Ruby code and hence is slow.

The sample code for parsing is

require 'rexml/document'
xml = "<xml><person>Abc Xyz</person></xml>
begin
  REXML::Document.new(xml)
rescue REXML::ParseException
  puts "Error occured"
end

At runtime the code identifies the nodes and children for each node which can be converted into a collections object for usage and identifying the names and values for each element.

While reading an XML, the use of REXML results in a complex piece of code. Life can be made much simpler by use of Builder::XmlMarkup libraries. The code by use of this library is just like one-one mapping between the Domain object and the elements of the XML.

WSDL, SOAP and UDDI in Ruby

Consider the following example of a wsdl file.

<?xml version="1.0"?>
<definitions name="StockQuote"
            targetNamespace="http://example.com/stockquote.wsdl"
            xmlns:tns="http://example.com/stockquote.wsdl"
            xmlns:xsd1="http://example.com/stockquote.xsd"
            xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
            xmlns="http://schemas.xmlsoap.org/wsdl/">
 <types>
   <schema targetNamespace="http://example.com/stockquote.xsd"
           xmlns="http://www.w3.org/2000/10/XMLSchema">
     <element name="TradePriceRequest">
       <complexType>
         <all>
           <element name="tickerSymbol" type="string"/>
         </all>
       </complexType>
     </element>
     <element name="TradePrice">
        <complexType>
          <all>
            <element name="price" type="float"/>
          </all>
        </complexType>
     </element>
   </schema>
 </types>
 <message name="GetLastTradePriceInput">
   <part name="body" element="xsd1:TradePriceRequest"/>
 </message>
 <message name="GetLastTradePriceOutput">
   <part name="body" element="xsd1:TradePrice"/>
 </message>
 <portType name="StockQuotePortType">
   <operation name="GetLastTradePrice">
     <input message="tns:GetLastTradePriceInput"/>
     <output message="tns:GetLastTradePriceOutput"/>
   </operation>
 </portType>
 <binding name="StockQuoteSoapBinding" type="tns:StockQuotePortType">
   <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
   <operation name="GetLastTradePrice">
     <soap:operation soapAction="http://example.com/GetLastTradePrice"/>
     <input>
       <soap:body use="literal"/>
     </input>
     <output>
       <soap:body use="literal"/>
     </output>
   </operation>
 </binding>
 <service name="StockQuoteService">
   <documentation>My first service</documentation>
   <port name="StockQuotePort" binding="tns:StockQuoteSoapBinding">
     <soap:address location="http://example.com/stockquote"/>
   </port>
 </service>
</definitions>