Supporting Multiple Versions of Ruby on OSX 10.6.7

April 25, 2011

I wanted a clean way of upgrading the default ruby 1.8.7 installation on my new Mac OSX 10.6.7 host, and couldn’t locate a decent explanation. As a result I’ve spent surprisingly little time unpicking the dependencies and getting my shiny new ruby 1.9.x installation sorted. Here’s how I achieved that. (Please note you need to have XCode installed if you plan to build the ruby installation from source.)

Firstly it’s important to understand the landscape when considering this upgrade. Firstly, the existing 1.8.x ruby installation exists in the /System/Library/Frameworks/Ruby.framework location. Beneath that root folder is a Versions folder. Versions contained another folder called 1.8 (referring to the version of ruby contained therein) and a symlink by the name of Current, which pointed to that 1.8 folder.

Decision 1: Seemed like a good idea to place all my future versions of ruby in version-aware subfolders in this …/Ruby.framework/Versions folder.

Next thing to understand is that all of the key commands relating to using the ruby framework (e.g. ruby, gem, irb, erb, …) live in the /usr/bin folder. Each of these commands are symlinks to executable files within the current ruby framework subtree. The convention here can be seen in this example:

/usr/bin/ruby -> /System/Library/Frameworks/Ruby.framework/Versions/Current/usr/bin/ruby

Next thing to understand is that all of the key commands relating to using the ruby framework (e.g. ruby, gem, irb, erb, …) live in the /usr/bin folder. Each of these commands are symlinks to executable files within the

Decision 2: If I correctly install all my subsequent versions of ruby into this same location then it should be possible for me to simply modify the Current symlink in the …/Ruby.framework/Versions folder to set the active ruby version I wish to use. All /usr/bin dependencies can simply resolve to the correct subfolder beneath the frameworks root.

Next I downloaded the latest source distribution from somewhere, in my case I used the 1.9.2 source from here (http://www.ruby-lang.org/en/news/2010/12/25/ruby-1-9-2-p136-is-released/). After downloading and unpacking into a temporary folder, I then executed the following commands from the root of the ruby source folder structure:

  1. sudo mkdir /System/Frameworks/Library/Ruby.framework/Versions/1.9.2/usr to create the new version subtree root.
  2. ./configure –prefix=/System/Frameworks/Library/Ruby.framework/Versions/1.9.2/usr to instruct the make utility to use my new installation folder for the deployment of the newly compiled binaries.
  3. sudo make && make install to create my new subtree and binaries beneath my newly created target.
When all that is done, assuming it goes OK, you will have all the necessary ruby executables in the subfolders beneath …/1.9.2/usr location but your system will still be set to use the ‘Current’ version which in my case was the pre-installed 1.8.7 version.
Final thing to do is to re-link the …/Ruby.framework/Versions/Current link to the 1.9.2 folder. I simply executed the following from the …/Ruby.framework/Versions folder:
  1. sudo unlink Current
  2. sudo ln -s 1.9.2 Current
When I now run my ruby commands – they report the correct version of 1.9.2. When and if I decide to revert to another version I can simply modify this Current link, and that can be made even easier with some alias’s and other bash helpers.

Installing Metric_Fu on Centos/Red-Hat Linux

July 22, 2009

To gain the benefit of integrated code-quality analysis within my continuous integration process for my Ruby projects, I’ve been integrating the excellent Metric_Fu package by Jake Scruggs. I’ve now completed an installation on OS-X, CentOS and Red-Hat where I had some quirks to resolve on CentOS and RedHat. This post provides my view on the best install procedure I can offer.

dependencies

First the underlying OS package pre-requisites…

sudo yum install libxml2           => v2.6.26-2.1.2.1
sudo yum install libxml2-devel     => v2.6.26-2.1.2
sudo yum install libxslt           => v1.1.17-2
sudo yum install libxslt-devel     => v1.1.17-2
sudo yum install ImageMagick       => v6.2.8.0-3.el5.4(see note)
sudo yum install ImageMagick-devel => v6.2.8.0-3.el5.4

Note the version of the ImageMagick package I have installed. There is a version depenency here with the rmagick gem which is installed in the next section. As far as I can see, for this version of the ImageMagick package, you must install the v1.x gem. As such I installed the highest version of the gem v1.15.17 below the v2.x.x baseline. If you have later versions of this ImageMagick package then I believe you can avoid this particular issue.

The TrueType Fonts dependency…

The rmagick gem has an installation dependency on the the presence of a set of true-type fonts in the base OS installation. On both the CentOS and RedHat installs I have done, this issue has been present. So before we go ahead and install the gems, we need to rectify this issue.The dependency relates to the presence of the /usr/share/fonts/default/TrueType folder which needs to be created with the following steps. (Note – you don’t need to install these fonts if you already have TrueType fonts installed – in which case skip to the next section).

yum install rpm-build wget http://www.cabextract.org.uk/cabextract-1.2-1.i386.rpm
rpm -ivh cabextract-1.2-1.i386.rpm 
wget http://corefonts.sourceforge.net/msttcorefonts-2.0-1.spec
rpmbuild -ba msttcorefonts-2.0-1.spec
rpm -ivh /usr/src/redhat/RPMS/noarch/msttcorefonts-2.0-1.noarch.rpm
cd /usr/share/fonts/default
ln -s /usr/share/fonts/msttcorefonts/ TrueType

Now the Gems…

Then install the gems:

sudo gem sources -a http://gems.github.com
sudo gem install jscruggs-metric_fu

Note at this point one of the installed depenencies, the ‘flog’ gem at v2.1.2, has an issue that will prevent it from running. I have explicitly modified the following file to get this to work:

vi /usr/local/lib/ruby/gems/1.8/gems/flog-2.1.2/bin/flog

Change the line: flogger.flog_files ARGV to flogger.flog ARGV and save the file. This is only a quick-n-dirty solution to get the flog integration to work within metric_fu.

sudo gem install reek
sudo gem install roodi
sudo gem install gruff
sudo gem install rmagick -v 1.15.17

When these gem installations complete you’re now ready to play with Metric_Fu. For more information on next steps refer to this article about some more version specific fixes or Jakes Metric_fu home.


Ruby Code Quality and Metric_Fu

July 9, 2009

I’ve been putting together a continuous integration framework for my Ruby projects and was very pleased to find the great work by Jake Scruggs (http://metric-fu.rubyforge.org/ ) by the name of Metric_Fu. On the whole this aggregation of tools covers all the key ‘quality’ bases and generates some elegant graphical reports that can be simply published alongside other build artifacts in CruiseControl.rb for example. On the whole the installation is very simple, but I hit some issues with the code and had to apply some class method overrides to get it to work.

Inspector

My base environment is OSX 10.5.7, Ruby 1.8.6, Gem 1.3.3. I installed the gems jscruggs-metric_fu (1.1.1), rcov (0.8.1.2.0), flog (2.1.2), flay (1.3.0), reek (1.1.3), roodi (1.4.0) and Saikuro (1.1.0). Following the instructions at http://metric-fu.rubyforge.org/ I was complete in short order, and ran the rake task as per the example.

The first problem I hit was a method naming issue where the a Flog#flog_files method was being called which didn’t exist in the versions I was using. I ended up with a quick-n-dirty fix which involved amending the /gems/flog-2.1.2/bin/flog script to remove the call to flog_files, replacing it with the correct method name flog:

...
flogger = Flog.new options
flogger.flog ARGV
flogger.report
...

Second problem I hit was some floating point and divide-by-zero issue in the MetricFu::Generator#round_to_tenths method. I don’t profess to understand the core issue here, but the value of ‘NaN’ was appearing in the calculation – so I added a ‘very’ primitve override to filter this condition (elegance is not my middle name I you hadn’t realised already):

module MetricFu
  class Generator
    def round_to_tenths(decimal)
      decimal=0.0 if decimal.to_s.eql?('NaN')
      (decimal.to_i * 10).round / 10.0
    end
  end
end

Next I hit an issue with rcov not returning any results into the dashboard? I checked this out and located an error in the rcov.txt file generated as part of the execution. This simply informed me that the rcov commandline generated within the metric_fu rcov generator class was incorrect. After a little experimentation I worked out that the –include-file parameter was the issue…so I again redefined the problematic class method in my Rakefile to remove this issue:

Module MetricFu
  class Rcov
    def emit
      begin
        FileUtils.rm_rf(MetricFu::Rcov.metric_directory, :verbose => false)
        Dir.mkdir(MetricFu::Rcov.metric_directory)
        test_files = FileList[*MetricFu.rcov[:test_files]].join(' ')
        rcov_opts = MetricFu.rcov[:rcov_opts].join(' ')
        output = ">> #{MetricFu::Rcov.metric_directory}/rcov.txt"
        #PROBLEM: `rcov --include-file #{test_files}  #{rcov_opts} #{output}`
        `rcov #{test_files}  #{rcov_opts} #{output}`
      rescue LoadError
        if RUBY_PLATFORM =~ /java/
          puts 'running in jruby - rcov tasks not available'
        else
          puts 'sudo gem install rcov # if you want the rcov tasks'
        end
      end
    end
  end
end

Next I hit a problem when modifying my declared MetricFu::Configuration object to change the location of the outputs of the analysis, to reduce the pollution in my project folder hierarchy (yep…I probably do have some deeper issues). I had added the following lines to the sample provide by Jake:

MetricFu::Configuration.run do |config|
  config.base_directory = ENV['CC_BUILD_ARTIFACTS'] || 'quality'
  config.scratch_directory = File.join(config.base_directory, 'scratch')
  config.output_directory = File.join(config.base_directory, 'output')
  config.data_directory = File.join(config.base_directory, '_data')
  etc

With this new folder structure I also had to add a tweek to the Saikuro configuration entry in the same sample, to make sure the Saikuro outputs are picked up by the report aggregator. Without this tweek I was just getting a blank Saikuro template page offered by Metric_Fu:

(Ignore the space between the colon and the ‘o’ – it’s giving me smilies for some reason)

config.saikuro  = { : output_directory => 
                              File.join(config.scratch_directory, 'saikuro'), 

This change caused a failure in the MetricFu::Graph#generate method – which on closer inspection appeared to be hardwired in expecting a depth of 3 folders in the locations specified for these outputs? So again I re-declared this class in my Rakefile to make the specific change necessary to support any level of nesting. This fixed the problem and my folder feng-shui returned to equilibrium…mmmmmmm.

Module MetricFu
  class Graph
    def generate
      puts "Generating graphs"
      Dir[File.join(MetricFu.data_directory, '*.yml')].sort.each do |metric_file|
        puts "Generating graphs for #{metric_file}"
        #PROBLEM: date = metric_file.split('/')[3].split('.')[0]
        date = metric_file.split('/').last.split('.')[0]
        metrics = YAML::load(File.open(metric_file))
        self.clazz.each do |grapher|
          grapher.get_metrics(metrics, date)
        end
      end
      self.clazz.each do |grapher|
        grapher.graph!
      end
    end
  end
end

So my installation of metric_fu works perfectly now and I’m very impressed with the output it’s providing so long as I don’t dwell too much on the fact that it’s telling me my code quality is somewhere between criminal and insane !!!

I hope these notes may help others experiencing similar frustration in trying to get the install working…and again my thanks to Jake Scruggs for this piece of goodness.

Cheers all !


Validating XML with Schema in Ruby

October 10, 2008

I posted a long time back about my troubles in finding a way of performing schema validation in ruby (see Ruby and Xml Schema). At that time I was using REXML and only able to perform well-formedness checks based on basic structural integrity, but had no way to take an XSD and validate an instance document.

I’m pleased to say that there is a way to do this now, namely libxml-ruby. It’s available as a gem (gem install libxml-ruby) and the process is pretty simple:

  document = LibXML::XML::Document.file(@xml_filename)
  schema = LibXML::XML::Schema.new(@xsd_filename)
  result = document.validate_schema(schema) do |message,flag|
    log.debug(message)
    puts message
  end

I’ve found this to be a very neat piece of code for dealing with the kind of schema integrity checking I’m looking for, and as I blend this with a number of other java-based parses using the Ruby Java Bridge I get a pretty good, consistent perspective on validity.


Ruby Wrapper for WS-I Analyzer tools

August 19, 2008

I’ve been developing some scripting to enable me to assess the integrity of integration artifacts created across a range of development teams in large scale SOA integration programmes. One of the most basic forms of assessment is the WS-Interoperability (WS-I) analysis of WSDL, to identify any specific non-compliances at grass-roots. (I can hear the RESTian hordes tooling up to brow-beat me into submission as I type this….and YES I understand, YOU don’t like WSDL, nor do I, but they DO exist so I’m gonna analyse em ! Right – now that’s out of the way on with the WSDL baiting…).

There’s a set of java/c# tools out there from the clever folk over at WS-I (http://www.ws-i.org/deliverables/workinggroup.aspx?wg=testingtools) which are already used informally within our organisation, and I’ve created a ruby-based wrapper for this. It’s not mature enough to be packaged as a Ruby Gem, and it does rely on an installation of the java tools in some folder on your machine – but the following ruby source does allow you to spin up objects in your Ruby code and kick it to analyse your WSDL artifacts without being exposed to java code. In line with my usual train-of-thought programming style and my few months of Ruby exposure, it’s not pretty but it works well enough to be parked for a while… Usage is pretty simple. The analyzer object can be initialised once and used thereafter to manage the analysis of many discrete WSDL resources. With each call to WSIAnalyser.analyze_wsdl() the first parameter is a file: or http: URI to a WSDL, and the second is a path/folder into which the outputs from this analysis will be placed. In any given WSDL the analyzer iterates through every <definitions/services> node, creating a report for each node. The outputs (in the output folder) are:

  1. A copy of the WSDL source
  2. A copy of the WS-I Tools configuration file generated by this script, one per <definitions/service> element in the source
  3. A WS-I analysis report generated by the underlying WS-I tool – one per <definitions/service> element in the source WSDL

a=WSITools::WSIAnalyzer.new
a.analyze("http://www.domain.com/someservice.wsdl", "c:/dev/wsdloutputs")
a.reports.each_pair do |r,s|
puts "WSDL [#{a.wsdl_uri}] Report [#{r}] Status [#{s}]"
end

The source:

WSI_ANALYZER_CONFIG_TEMPLAGE = %{<?xml version="1.0" encoding="UTF-8"?>
<wsi-analyzerConfig:configuration name="WS-I Basic Profile Analyzer Configuration" xmlns:wsi-analyzerConfig="http://www.ws-i.org/testing/2004/07/analyzerConfig/">
<wsi-analyzerConfig:description />
<wsi-analyzerConfig:verbose>
false
</wsi-analyzerConfig:verbose>
<wsi-analyzerConfig:assertionResults type="all" messageEntry="true" failureMessage="true"/>
<wsi-analyzerConfig:reportFile replace="true" location="xxxxxxxx">
<wsi-analyzerConfig:addStyleSheet href="" type="text/xsl"/>
</wsi-analyzerConfig:reportFile>
<wsi-analyzerConfig:testAssertionsFile />
<wsi-analyzerConfig:wsdlReference>
<wsi-analyzerConfig:wsdlElement type="port" parentElementName="" namespace="" />
<wsi-analyzerConfig:wsdlURI />
</wsi-analyzerConfig:wsdlReference>
</wsi-analyzerConfig:configuration>
}

#As we are modifying the path environment variable, we need to ensure that the correct delimiter is used
UNIX_PATH_DELIMITER=":"
WIN_PATH_DELIMITER=";"

#This must be changed to point to the physical root of the wsi-installation
WSI_HOME_TAG = "WSI_HOME"
WSI_HOME_VAL = "c:/dev/wsi-test-tools"
WSI_JAVA_HOME_TAG = "WSI_JAVA_HOME"
WSI_JAVA_HOME_VAL = "#{WSI_HOME_VAL}/java"
WSI_JAVA_OPTS_TAG = "WSI_JAVA_OPTS"
WSI_JAVA_OPTS_VAL = " -Dorg.xml.sax.driver=org.apache.xerces.parsers.SAXParser"
WSI_CLASSPATH_TAG = "WSI_CP"
WSI_TEST_ASSERTIONS_FILE = "#{WSI_HOME_VAL}/common/profiles/SSBP10_BP11_TAD.xml"
WSI_STYLESHEET_FILE = "#{WSI_HOME_VAL}/common/xsl/report.xsl"
WSI_EXECUTION_COMMAND = "#{WSI_JAVA_HOME_VAL}/bin/Analyzer.bat -config "

WSIClasspath=[
"#{WSI_JAVA_HOME_VAL}/lib/wsi-test-tools.jar",
"#{WSI_JAVA_HOME_VAL}/lib",
"#{WSI_JAVA_HOME_VAL}/lib/xercesImpl.jar",
"#{WSI_JAVA_HOME_VAL}/lib/xmlParserAPIs.jar",
"#{WSI_JAVA_HOME_VAL}/lib/wsdl4j.jar",
"#{WSI_JAVA_HOME_VAL}/lib/uddi4j.jar",
"#{WSI_JAVA_HOME_VAL}/lib/axis.jar",
"#{WSI_JAVA_HOME_VAL}/lib/jaxrpc.jar",
"#{WSI_JAVA_HOME_VAL}/lib/saaj.jar",
"#{WSI_JAVA_HOME_VAL}/lib/commons-discovery.jar",
"#{WSI_JAVA_HOME_VAL}/lib/commons-logging.jar"
]

class WSIAnalyzer
VERSION = "1.0.0"

attr_reader :wsdl_uri
attr_reader :wsdl_name
attr_reader :wsdl_source
attr_reader :analyzer_config_uri
attr_reader :analyzer_config_source
attr_reader :wsdl_namespace
attr_reader :wsdl_service_name
attr_reader :wsdl_port_name
attr_reader :workspace
attr_reader :report_filename
attr_reader :wsi_approved
attr_reader :wsdl_service_declarations
attr_reader :reports
attr_reader :errors

def initialize
begin
@log=Logger.new("analyzer.log")
@log.progname="WsiAnalyzer"
@log.level = Logger::DEBUG
@wsdl_uri=nil
@wsdl_name=nil
@wsdl_source=nil
@analyzer_config_uri=nil
@analyzer_config_source=nil
@wsdl_namespace=nil
@wsdl_service_name=nil
@wsdl_port_name=nil
@workspace=nil
@report_filename=nil
@wsdl_service_declarations={}
@reports={}
@errors=[]
#Check the installation location to ensure the wsi-test-tools are installed on the host
if not File.exists?(WSI_HOME_VAL)
@log.fatal("Unable to locate WSI-Test-Tools installation at [#{WSI_HOME_VAL}]")
return nil
end
#Ensure environment variables are in place
if(ENV[WSI_HOME_TAG].nil?)
@log.warn("No WSI-Test-Tools environment variables present [#{WSI_HOME_TAG}]")
configure_environment
end
rescue => ex
@log.error("Unable to initialise WSIAnalyzer: Ex #{ex.message}\n"+ex.backtrace.join("\n"))
return nil
end
end

def analyze_wsdl(wsdl_uri, workspace)
begin
@wsdl_uri=wsdl_uri
@workspace=workspace
segments=@wsdl_uri.split('/')
@wsdl_name=segments.last
segments=@wsdl_name.split('.')
@wsdl_name=segments.first
@log.info("Analyzing WSDL[#{@wsdl_name}] from URI[#{@wsdl_uri}] in workspace [#{@workspace}]")
#obtain the wsdl source
acquire_wsdl
#obtain key wsdl attributes required to create ws-i configuration
extract_target_elements
#There may be more than one pass to make here so iterate
@wsdl_service_declarations.each_pair do |svc, port|
#create dynamic ws-i configuration
@log.info("Excuting WS-I analysis for WSDL [#{@wsdl_name}] Service[#{svc}] Port[#{port}]")
@report_filename="#{@workspace}/ws-i-report-for-wsdl-#{@wsdl_name}-svc-#{svc}-port-#{port}.xml"
create_dynamic_wsi_config(svc, port)
#execute the analysis
execute_wsi_analyzer
end
rescue => ex
@log.error("Unable to analyze WSDL [#{@wsdl_uri}]: Ex #{ex.message}\n"+ex.backtrace.join("\n"))
return false
end
end

private

def execute_wsi_analyzer
begin
#Now kick the external WSI script to generate a report
commandline="#{WSI_EXECUTION_COMMAND} #{@analyzer_config_uri}"
@log.info("Execution WS-I Analyzer with shell [#{commandline}]")
@reports={}
@errors=[]
system(commandline)
#Verify if a report has been created
if(not File.exists?(@report_filename))
@log.warn("No report file [#{@report_filename}] produced by WS-I Analyzer")
else
@log.info("Scanning for WS-I summary status...")
begin
dom=REXML::Document.new File.new(@report_filename)
if dom.elements["report/summary"].nil?
if not dom.elements["report/analyzerFailure"].nil?
#A failure code has been identified in the report doccument
msg=dom.elements["report/analyzerFailure/failureDetail"]
@log.warn("WSDL [#{@wsdl_name}] WS-I Failure Report [#{msg}] ")
@errors << msg
status="failed"
end
else
#Summary status is passed (not sure if summary can be failed/error
status=dom.elements["report/summary"].attributes["result"].to_s
end
@log.info("WS-I Approval Status [#{status}] for WSDL [#{@wsdl_uri}]")
#Add a report summary to the list of reports
@reports[@report_filename]=status
end
end
rescue => ex
#Unable to acquire status - treat report as invalid/missing
@log.error("Unable to complete WS-I Analysis of [#{@wsdl_name}]: Ex #{ex.message}\n"+ex.backtrace.join("\n"))
raise
end
end

def create_dynamic_wsi_config(svc, port)
begin
cdom=REXML::Document.new(WSI_ANALYZER_CONFIG_TEMPLAGE)
#cdom.elements.each do |e|
# puts e.inspect
#end
@log.info("WS-I configured to report to [#{@report_filename}]")
cdom.elements["wsi-analyzerConfig:configuration/wsi-analyzerConfig:reportFile"].attributes["location"]=@report_filename
cdom.elements["wsi-analyzerConfig:configuration/wsi-analyzerConfig:reportFile/wsi-analyzerConfig:addStyleSheet"].attributes["href"]=WSI_STYLESHEET_FILE
cdom.elements["wsi-analyzerConfig:configuration/wsi-analyzerConfig:testAssertionsFile"].text=WSI_TEST_ASSERTIONS_FILE
cdom.elements["wsi-analyzerConfig:configuration/wsi-analyzerConfig:wsdlReference/wsi-analyzerConfig:wsdlElement"].attributes["namespace"]=@wsdl_namespace
cdom.elements["wsi-analyzerConfig:configuration/wsi-analyzerConfig:wsdlReference/wsi-analyzerConfig:wsdlElement"].attributes["parentElementName"]=svc
cdom.elements["wsi-analyzerConfig:configuration/wsi-analyzerConfig:wsdlReference/wsi-analyzerConfig:wsdlElement"].text=port
cdom.elements["wsi-analyzerConfig:configuration/wsi-analyzerConfig:wsdlReference/wsi-analyzerConfig:wsdlURI"].text=@wsdl_uri
#Now write the configuration file into the workspace of the artifact
#@log.debug(cdom.to_s)
@analyzer_config_source=cdom.to_s
@analyzer_config_uri="#{@workspace}/wsiAnalyzerConfig-svc-#{svc}-port-#{port}.xml"
open("#{@analyzer_config_uri}",'w'){ |f| f << cdom.to_s}
@log.info("Written dynamic WS-I config into [#{@analyzer_config_uri}]")
rescue => ex
@log.error("Unable to generate dynamic config for WS-I: Ex #{ex.message}\n"+ex.backtrace.join("\n"))
raise
end
end

def extract_target_elements
#Need the servicename, portname and other stuff from the wsdl
#Get the DEFINITIONS.TARGETNAMESPACE attribute
#Get the DEFINITIONS/SERVICE.NAME attribute
#Get the DEFINITIONS/SERVICE/PORT.name attribute
dom=REXML::Document.new(@wsdl_source)
begin
#TODO: Need to ensure I can handle a WSDL with multiple SERVICE/PORT combinations
@wsdl_namespace=dom.elements["definitions"].attributes["targetNamespace"]
#@wsdl_service_name=dom.elements["definitions/service"].attributes["name"]
@wsdl_service_declarations={}
dom.elements.each("definitions/service") do |servicenode|
svc=servicenode.attributes["name"]
port=servicenode.elements[port].attributes["name"]
@wsdl_service_declarations[svc]=port
end
#@wsdl_port_name=dom.elements["definitions/service/port"].attributes["name"]
@log.info("Extracted Namespace[#{@wsdl_namespace}] Services[#{@wsdl_service_declarations.inspect}] from WSDL")
if @wsdl_service_declarations.size<1
raise "No Service elements [definitions/service] detected in WSDL [#{@wsdl_name}]"
end
rescue => ex
@log.error("Unable to extract WSDL markers for WS-I: Ex #{ex.message}\n"+ex.backtrace.join("\n"))
raise
end
end

def acquire_wsdl
begin
@wsdl_source = open(@wsdl_uri).read
@log.info "Acquired WSDL [#{@wsdl_uri}] [#{@wsdl_source.length}] bytes of WSDL"
rescue => ex
@log.error("Unable to acquire WSDL [#{@wsdl_uri}]: Ex #{ex.message}\n"+ex.backtrace.join("\n"))
raise
end
end

def configure_environment

#display WSI environment variables
path_delim=UNIX_PATH_DELIMITER
unless ENV["OS"].nil?
unless ENV["OS"].downcase.index("windows").nil?
path_delim=WIN_PATH_DELIMITER
end
end
ENV[WSI_HOME_TAG]=WSI_HOME_VAL
ENV[WSI_JAVA_HOME_TAG]=WSI_JAVA_HOME_VAL
ENV[WSI_JAVA_OPTS_TAG]=WSI_JAVA_OPTS_VAL
#Now add the path segments
WSIClasspath.each do |val|
if(ENV[WSI_CLASSPATH_TAG].nil?)
ENV[WSI_CLASSPATH_TAG]="#{val}"
else
ENV[WSI_CLASSPATH_TAG]="#{ENV[WSI_CLASSPATH_TAG]}#{path_delim}#{val}"
end
end
end
end
end

Powered by Qumana


Ruby/Rails URI Patterns and JSR311

January 10, 2008

I had been working with a rails project mapping a uniform REST interace onto complex resource representations assembled at runtime, and as such no object/relational mapping was used to pull information from a local datasource.  Instead I use ‘virtual’ resource representations which are fulfilled on-demand, within synch or asynch interactions, from back-end complex back-end data sources and interfaces.

As such the primary Rails convention of mapping resource URI’s to controllers and actions, and through an object/relational layer to a datasource is effectively useless in my scenario. Instead of mapping resource to a datasource, I instead have to  map URI’s to a generic controller, still supporting the GET, POST, PUT, DELETE semantics, but essentially data-driving a dynamic integration tier with the resource context.

So if I were resource-enabling a database with  2 tables, customers and orders, then I’d use the following routes.rb entries (along with a rails resource scaffold) to build my URI to datasource relationships.

map.resources :customers
map.resources :orders

This would route requests for http://domain:port/customers to the Customers controller, and route the http://domain:port/orders to the Orders controller, where each would sit across O/R mappings to specific tables in my database. Nice and easy !

However in my world the Customers resource and the Orders resource are virtual within the front-end runtime, and must be instantiated by using a dynamic mapping algorithm to initiate back-end integration transactions over a range of mechanisms to instantiate the correct representations for each resource type (focusing on GETS’s for this sake of this example). As such it makes  sense for me to have a single VirtualResource Controller, which uses request context information to infer the correct representation algorithm. Rails has a neat way of enabling this in the routing table:

map.resources :operations, :controller=>"virtualresources"
map.resources :customers, :controller=>"virtualresources"

This enables me to pipe a range of URI patterns into my virtual handler and manage a lot of complexity through very few generalised routines.

At this point I’m considering the migration across to Jersey on Glassfish – using the JSR311 annotation scheme for this kind of URI pattern matching.

 @UriTemplate("widgets/{id}")
 public class Widget {
 ...
 }

Only problem is – at the moment I see no way of assigning multiple URI patterns to a single Virtual Resource class in the way I have with Rails. Only one URI pattern is enabled for each POJO class – which would mean I’d have to create many proxy classes into my virtual resource handler? I’m gonna have to dig a little deeper here but hope this kind of flexibility and learning from the Rails community will be factored into the JSR spec?


REST – Am I moving from ruby to java…?

December 21, 2007

So I’ve concluded some deep investigations around RESTful service applied to the big nasty Enterprise. I’ve been easing myself back into hacksville with Windows/Ruby/Rails/MySQL and have had an altogeher positive experience. However I’m now at a point of considering my next steps – do I go forward on Ruby/Rails or do I take this opportunity to hop onto an alternative framework.

The answer to this relates to the nature of the challenge in applying REST to the Enteprise as opposed to RESTifying a database or application instance. In the former the primary challenge relates to the formation of resoruces from potentially complex components – not atomic records or aggregations of information from a local-datasource (I’m using a trivial scenarion here as an example – not disresepcting restful applications !!).

 I have hit problems with managing synchronous and asynchronous RESTful interactions within rails to date, and these challenges are purely down to the complex linkage beween a RESTful presentation and a complex implementation landscape.

Whilst this kind or perspective may be off the mainstream from a rails-applcations perspective, it’s core to my world – and as such I benefit less from the excellent front-end rails conventions, given I’m assembling resource representations which don’t exist in an atomic sense, in a local DB.

So the key issues I now face in my next iteration (and the Rails/Java JSR311 question) are:

  1. I need a more seamless mechanism for managing undeterministic concurrency (by virtue of possibly long-running resource operations), and where dynamic threading seems cleaner than pre-defining a mongrel cluster for a known concurrency limit up front – along with a fron-end load-balancer configuration.
  2.  I need to introduce efficient, selective asynchronous interactions based around the 202-Accepted response, enabling the resource server to work on an operation whilst pushing a client away with a URI.
  3. XML Schema and validation support. I understand, to some extent, the position offered by the Ruby/REXML community in that XML Schema is over complicated and useless….and not worth implementing within the REXML library. However XML Schema is a mainstream currency in my integration (Enterprise) landscape and as such I have to deal with it. The lack of support in ruby has hampered my efforts to date.

I stress again that this is not a gripe about ruby/rails by any means – simply an observation that my Enterprise/REST context is emphasising certain integration challenges which may not be front-and-centre in the emerging waves of RESTful applications successfully launching on Rails.

So I’m looking at Jersey (JSR-311 Reference Implementation) and Noelios Restlet as options for my next leap. Both options will cover all 3 of my pinch points (I believe), and the thing I’m unsure about is the extent to which I’ll be exposed on the presentation stuff which the rails convention eats for lunch….

An interesting junction….