Sail configuration files
The SAIL framework uses a configuration file to initialize and configure various core services at runtime. In order to support flexibility on customizing which services are used on an offering, the code in the SDS for generating those config files has been refactored. It is now possible to create many arbitrary versions of the configuration file, and associate those versions with a jnlp file. Then when the offering is run, the SDS will look at the jnlp file, and load the configuration template associated with that jnlp specifically, thus enabling per-offering customizations of the SAIL configuration.
ConfigVersions
The SDS model for describing these configuration templates is called ConfigVersion. A ConfigVersion has a name (string), version (float), and a template (string).
- name - A short string describing this ConfigVersion. This will be used to select it when associating with a jnlp.
- version - A version number for this ConfigVersion. It's currently not used for anything
- template - A ruby rxml template which will be eval'ed to generate the config file
Creating a ConfigVersion
Because the templates are eval'ed at runtime, and because templates can contain potentially damaging code, there is no web UI for creating/editing ConfigVersions. In order to create or edit a ConfigVersion, you will need to start up ruby's script/console and work from there. This does make it a tad bit more cumbersome to work with, however it does provide a barrier to just anyone creating a ConfigVersion.
Here's what to do to. Start out by opening up script/console:
cd <dir where the sds resides> script/console >
To create a ConfigVersion, in script/console do:
cv = ConfigVersion.new cv.name = "name" cv.version = 1.0 cv.template = "template" cv.save!
To edit an existing ConfigVersion, in script/console do:
cv = ConfigVersion.find(1) # replacing 1 with whatever id your ConfigVersion is # now do one or more of the following steps cv.name = "new name" cv.version = 1.1 cv.template = "new template" # and finally save it when you're done cv.save!
There are two default ConfigVersions which you can generate automatically by running 'rake sds:setup_config_versions' from the SDS application directory.
The template
The template for a ConfigVersion is simply a Ruby Builder script (http://builder.rubyforge.org/) which will output a well-formatted XML document. This document is then returned to the client.
The original template looks like this:
xml.instruct! :xml, :version=>"1.0", :encoding=>"UTF-8"
xml.java("version" => "1.4.0", "class" => "java.beans.XMLDecoder") {
xml.object("class" => "net.sf.sail.core.service.impl.CurnitUrlProviderImpl") {
xml.void("property" => "url") {
xml.string(@curnit_url)
}
}
xml.object("class" => "net.sf.sail.emf.launch.PortfolioManagerService") {
xml.void("property" => "portfolioUrlProvider") {
xml.object("class" => "net.sf.sail.emf.launch.XmlUrlStringProviderImpl") {
xml.void("property" => "urlString") {
xml.string(@controller.url_for(
:controller => "offering",
:action => "bundle",
:id => @offering.id,
:wid => @workgroup.id,
:version => @version,
:nobundles => @nobundles,
:only_path => false))
}
}
}
if @savedata
xml.void("property" => "bundlePoster") {
xml.object("class" => "net.sf.sail.emf.launch.BundlePoster") {
xml.void("property" => "postUrl") {
xml.string(@controller.url_for(
:controller => "offering",
:action => "bundle",
:id => @offering.id,
:wid => @workgroup.id,
:version => @version,
:nobundles => nil,
:only_path => false))
}
}
}
end
}
xml.object("class" => "net.sf.sail.core.service.impl.LauncherServiceImpl") {
xml.void("property" => "properties") {
xml.object("class" => "java.util.Properties") {
@offering_attributes.each do |k,v|
xml.void("method" => "setProperty") {
xml.string(k)
xml.string(v)
}
end
}
}
}
xml.object("class" => "net.sf.sail.emf.launch.EMFSailDataStoreService2")
xml.object("class" => "net.sf.sail.core.service.impl.UserServiceImpl") {
xml.void("property" => "participants") {
@sail_users = @workgroup.sail_users.version(@version)
@sail_users.each do |u|
add_user_to_config(xml, u)
end
}
xml.void("property" => "userLookupService") {
xml.object("class" => "net.sf.sail.core.service.impl.UserLookupServiceImpl")
}
}
xml.object("class" => "net.sf.sail.core.service.impl.SessionLoadMonitor")
xml.object("class" => "net.sf.sail.core.service.impl.SessionManagerImpl")
}
This will generate an XML document which looks similar to this:
<?xml version="1.0" encoding="UTF-8"?>
<java class="java.beans.XMLDecoder" version="1.4.0">
<object class="net.sf.sail.core.service.impl.CurnitUrlProviderImpl">
<void property="url">
<string>http://rails.dev.concord.org/sds/cache/3/curnits/19201/converted-wise-dev.berkeley.edu-24587.jar</string>
</void>
</object>
<object class="net.sf.sail.emf.launch.PortfolioManagerService">
<void property="portfolioUrlProvider">
<object class="net.sf.sail.emf.launch.XmlUrlStringProviderImpl">
<void property="urlString">
<string>http://rails.dev.concord.org/sds/3/offering/18360/bundle/18176/0</string>
</void>
</object>
</void>
<void property="bundlePoster">
<object class="net.sf.sail.emf.launch.BundlePoster">
<void property="postUrl">
<string>http://rails.dev.concord.org/sds/3/offering/18360/bundle/18176/0</string>
</void>
</object>
</void>
</object>
<object class="net.sf.sail.core.service.impl.LauncherServiceImpl">
<void property="properties">
<object class="java.util.Properties">
</object>
</void>
</object>
<object class="net.sf.sail.emf.launch.EMFSailDataStoreService2"/>
<object class="net.sf.sail.core.service.impl.UserServiceImpl">
<void property="participants">
<void method="add">
<object class="net.sf.sail.core.entity.User">
<object class="net.sf.sail.core.uuid.UserUuid">
<string>d9d90efa-7ae0-11dc-9c75-0014c2c34555</string>
</object>
<string>tests11a tests11a</string>
</object>
</void>
<void method="add">
<object class="net.sf.sail.core.entity.User">
<object class="net.sf.sail.core.uuid.UserUuid">
<string>c783a72e-7ae0-11dc-9c75-0014c2c34555</string>
</object>
<string>tests11 tests11</string>
</object>
</void>
</void>
<void property="userLookupService">
<object class="net.sf.sail.core.service.impl.UserLookupServiceImpl"/>
</void>
</object>
<object class="net.sf.sail.core.service.impl.SessionLoadMonitor"/>
<object class="net.sf.sail.core.service.impl.SessionManagerImpl"/>
</java>
Most of the Builder template is straightforward:
xml.__element__("attribute" => "attribute_value") {
xml.__child_element__("attribute" => "attribute_value", "attribute" => "attribute_value")
}
Some of it, however, may not seem as straightforward. There are several references to Ruby instance variables (these start with the @ sign), as well as references to the url_for method, which generates a url from a hash of arguments.
There are several instance variables available to your Builder script:
- @portal - This is a reference to the Portal object for the current Portal
- @offering - This is a reference to the Offering object for the current Offering
- @workgroup - This is a reference to the Workgroup object for the current Workgroup
- @version - This is the current Workgroup's version
- @savedata - A boolean reference to whether or not saving data is enabled
- @nobundles - A boolean reference to whether or not data should be loaded at client startup (see this page)
- @curnit_url - The url from which the curnit for this offering can be accessed
- @offering_attributes - A hash of OfferingAttributes defined in the SDS UI on an offering, AND key/value pairs which were appended to the jnlp url.