[Swift-commit] r6314 - in branches/faster: resources src/org/globus/swift/catalog/site src/org/griphyn/vdl/karajan/lib

hategan at ci.uchicago.edu hategan at ci.uchicago.edu
Tue Feb 26 21:22:36 CST 2013


Author: hategan
Date: 2013-02-26 21:22:36 -0600 (Tue, 26 Feb 2013)
New Revision: 6314

Added:
   branches/faster/resources/swift-sites-2.0.xsd
   branches/faster/src/org/globus/swift/catalog/site/SiteCatalogParser.java
Removed:
   branches/faster/src/org/globus/swift/catalog/site/Parser.java
Modified:
   branches/faster/src/org/griphyn/vdl/karajan/lib/SiteCatalog.java
Log:
better sites file parsing

Added: branches/faster/resources/swift-sites-2.0.xsd
===================================================================
--- branches/faster/resources/swift-sites-2.0.xsd	                        (rev 0)
+++ branches/faster/resources/swift-sites-2.0.xsd	2013-02-27 03:22:36 UTC (rev 6314)
@@ -0,0 +1,243 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xs:schema 
+	xmlns="http://www.ci.uchicago.edu/swift/SwiftSites" 
+	xmlns:xs="http://www.w3.org/2001/XMLSchema" 
+	targetNamespace="http://www.ci.uchicago.edu/swift/SwiftSites" 
+	elementFormDefault="qualified" 
+	attributeFormDefault="unqualified" version="1.5">
+	<xs:simpleType name="ProfileNamespace">
+		<xs:annotation>
+			<xs:documentation>defines the legal namespaces of execution profiles.</xs:documentation>
+		</xs:annotation>
+		<xs:restriction base="xs:NMTOKEN">
+			<xs:enumeration value="vds"/>
+			<xs:enumeration value="swift"/>
+			<xs:enumeration value="env"/>
+			<xs:enumeration value="hints"/>
+			<xs:enumeration value="globus"/>
+			<xs:enumeration value="karajan"/>
+		</xs:restriction>
+	</xs:simpleType>
+	<xs:attributeGroup name="GlobusVersionInfo">
+		<xs:annotation>
+			<xs:documentation>A set of attributes to define a Globus version.</xs:documentation>
+		</xs:annotation>
+		<xs:attribute name="major" type="xs:pos
 itiveInteger" use="required"/>
+		<xs:attribute name="minor" type="xs:nonNegativeInteger" use="required"/>
+		<xs:attribute name="patch" type="xs:nonNegativeInteger" use="optional"/>
+	</xs:attributeGroup>
+	<xs:simpleType name="OSType">
+		<xs:restriction base="xs:NMTOKEN">
+			<xs:enumeration value="INTEL32::LINUX"/>
+			<xs:enumeration value="INTEL64::LINUX"/>
+			<xs:enumeration value="INTEL32::WINDOWS"/>
+			<xs:enumeration value="INTEL64::WINDOWS"/>
+			<xs:enumeration value="INTEL32::GENERIC_UNIX"/>
+			<xs:enumeration value="INTEL64::GENERIC_UNIX"/>
+		</xs:restriction>
+	</xs:simpleType>
+	<xs:element name="sites">
+		<xs:annotation>
+			<xs:documentation>Root element aggregating all sites information there is. This is the
+			preferred choice for version 2.0 of the sites file</xs:documentation>
+		</xs:annotation>
+		<xs:complexType>
+			<xs:sequence maxOccurs="unbounded">
+				<xs:element name="site" minOccurs="0" maxOccurs="unbounded">
+					<xs:annotation>
+				
 		<xs:documentation>Describes a single site.</xs:documentation>
+					</xs:annotation>
+					<xs:complexType>
+						<xs:sequence maxOccurs="unbounded">
+							<xs:element ref="profile" minOccurs="0" maxOccurs="unbounded"/>
+							<xs:element ref="env" minOccurs="0" maxOccurs="unbounded"/>
+							<xs:element ref="workdirectory" minOccurs="0" maxOccurs="1"/>
+							<xs:element ref="scratch" minOccurs="0" maxOccurs="1"/>
+							<xs:element ref="execution" minOccurs="0" maxOccurs="1"/>
+							<xs:element ref="filesystem" minOccurs="0" maxOccurs="1"/>
+							<xs:element ref="application" minOccurs="0" maxOccurs="unbounded"/>
+						</xs:sequence>
+						<xs:attribute name="name" type="xs:string" use="required"/>
+						<xs:attribute name="sysinfo" type="OSType" use="optional" default="INTEL32::LINUX"/>
+					</xs:complexType>
+				</xs:element>
+			</xs:sequence>
+		</xs:complexType>
+	</xs:element>
+	<xs:element name="config">
+		<xs:annotation>
+			<xs:documentation>roo
 t element aggregating all sites information there is.</xs:documentation>
+		</xs:annotation>
+		<xs:complexType>
+			<xs:sequence maxOccurs="unbounded">
+				<xs:element name="pool" minOccurs="0" maxOccurs="unbounded">
+					<xs:annotation>
+						<xs:documentation>Describes a single site.</xs:documentation>
+					</xs:annotation>
+					<xs:complexType>
+						<xs:sequence maxOccurs="unbounded">
+							<xs:element ref="profile" minOccurs="0" maxOccurs="unbounded"/>
+							<xs:element ref="env" minOccurs="0" maxOccurs="unbounded"/>
+							<xs:element ref="workdirectory" minOccurs="0" maxOccurs="1"/>
+							<xs:element ref="scratch" minOccurs="0" maxOccurs="1"/>
+							<xs:element ref="execution" minOccurs="0" maxOccurs="1"/>
+							<xs:element ref="filesystem" minOccurs="0" maxOccurs="1"/>
+							<xs:element ref="gridftp" minOccurs="0" maxOccurs="1"/>
+							<xs:element ref="jobmanager" minOccurs="0" maxOccurs="1"/>
+						</xs:sequence>
+						<xs:attribute name="han
 dle" type="xs:ID" use="required"/>
+						<xs:attribute name="sysinfo" type="xs:string" use="optional" default="INTEL32::LINUX"/>
+						
+					</xs:complexType>
+				</xs:element>
+			</xs:sequence>
+			<xs:attribute name="version" type="xs:decimal" use="optional" default="1.0"/>
+		</xs:complexType>
+	</xs:element>
+	<xs:element name="profile">
+		<xs:annotation>
+			<xs:documentation>Administrative profile defaults associated with a pool.</xs:documentation>
+		</xs:annotation>
+		<xs:complexType>
+			<xs:simpleContent>
+				<xs:extension base="xs:string">
+					<xs:attribute name="namespace" type="ProfileNamespace" use="required"/>
+					<xs:attribute name="key" type="xs:string" use="required"/>
+				</xs:extension>
+			</xs:simpleContent>
+		</xs:complexType>
+	</xs:element>
+	<xs:element name="workdirectory" default="/tmp">
+		<xs:annotation>
+			<xs:documentation>Each pool may have one and only one work directory mount point.</xs:documentation>
+		</xs:annotation>
+		<
 xs:complexType>
+			<xs:simpleContent>
+				<xs:extension base="xs:string">
+					<xs:attribute name="total-size" type="xs:decimal" use="optional"/>
+					<xs:attribute name="free-size" type="xs:decimal" use="optional"/>
+				</xs:extension>
+			</xs:simpleContent>
+		</xs:complexType>
+	</xs:element>
+	
+	<xs:element name="scratch">
+		<xs:annotation>
+			<xs:documentation>TODO</xs:documentation>
+		</xs:annotation>
+	</xs:element>
+	
+	<xs:element name="execution">
+		<xs:annotation>
+			<xs:documentation>Describes the mechanism used to submit jobs to the pool</xs:documentation>
+		</xs:annotation>
+		<xs:complexType>
+			<xs:attribute name="provider" type="xs:string" use="required"/>
+			<xs:attribute name="jobManager" type="xs:string" use="optional"/>
+			<xs:attribute name="jobmanager" type="xs:string" use="optional"/>
+			<xs:attribute name="url" type="xs:string" use="optional" default="localhost"/>
+		</xs:complexType>
+	</xs:element>
+	
+	<xs:element name="filesystem
 ">
+		<xs:annotation>
+			<xs:documentation>Describes the mechanism used to interact with the filesystem on the given pool</xs:documentation>
+		</xs:annotation>
+		<xs:complexType>
+			<xs:attribute name="provider" type="xs:string" use="required"/>
+			<xs:attribute name="url" type="xs:string" use="optional" default="localhost"/>
+		</xs:complexType>
+	</xs:element>
+	
+	<xs:element name="gridftp">
+		<xs:annotation>
+			<xs:documentation>Each pool may have multiple gridftp servers.</xs:documentation>
+		</xs:annotation>
+		<xs:complexType>
+			<xs:sequence>
+				<xs:element name="bandwidth" minOccurs="0" maxOccurs="unbounded">
+					<xs:annotation>
+						<xs:documentation>Stores the bandwidth informaion related to each gridftp server.</xs:documentation>
+					</xs:annotation>
+					<xs:complexType>
+						<xs:attribute name="dest-subnet" type="xs:string" use="required"/>
+						<xs:attribute name="avg-bandwidth" type="xs:float" use="required"/>
+						<xs:attribute name="
 max-bandwidth" type="xs:float" use="required"/>
+						<xs:attribute name="min-bandwidth" type="xs:float" use="required"/>
+						<xs:attribute name="avg-bandwidth-range1" type="xs:float" use="optional"/>
+						<xs:attribute name="avg-bandwidth-range2" type="xs:float" use="optional"/>
+						<xs:attribute name="avg-bandwidth-range3" type="xs:float" use="optional"/>
+						<xs:attribute name="avg-bandwidth-range4" type="xs:float" use="optional"/>
+					</xs:complexType>
+				</xs:element>
+			</xs:sequence>
+			<xs:attribute name="url" type="xs:anyURI" use="required">
+				<xs:annotation>
+					<xs:documentation>The URL (actually, it may be more a URI, but hey, so what) is the access URL to the gridftp server. Each pool may have multiple gridftp servers, or run multiple versions of Globus on different ports.</xs:documentation>
+				</xs:annotation>
+			</xs:attribute>
+			<xs:attribute name="storage" type="xs:string" use="optional" default="/">
+				<xs:annotation>
+					<xs
 :documentation>This element is the storage mount point prefix. Of course, this may get turned over into other things, augmented by user and system requirements etc. I believe that default works quite well for default Globus setups. </xs:documentation>
+				</xs:annotation>
+			</xs:attribute>
+			<xs:attributeGroup ref="GlobusVersionInfo"/>
+			<xs:attribute name="total-size" type="xs:decimal" use="optional"/>
+			<xs:attribute name="free-size" type="xs:decimal" use="optional"/>
+		</xs:complexType>
+	</xs:element>
+	<xs:element name="jobmanager">
+		<xs:annotation>
+			<xs:documentation>Each pool supports various (usually two) jobmanagers.</xs:documentation>
+		</xs:annotation>
+		<xs:complexType>
+			<xs:attribute name="universe" type="xs:token" use="required">
+				<xs:annotation>
+					<xs:documentation>The universe name is actually the primary key for the jobmanager identification.</xs:documentation>
+				</xs:annotation>
+			</xs:attribute>
+			<xs:attribute name="url"
  type="xs:string" use="required">
+				<xs:annotation>
+					<xs:documentation>The contact string is the secondary key for any job manager.</xs:documentation>
+				</xs:annotation>
+			</xs:attribute>
+			<xs:attributeGroup ref="GlobusVersionInfo">
+				<xs:annotation>
+					<xs:documentation>Any pool may have multiple versions of Globus installed, and these versions may have multiple jobmanagers listening on different ports.</xs:documentation>
+				</xs:annotation>
+			</xs:attributeGroup>
+			<xs:attribute name="free-mem" type="xs:string" use="optional"/>
+			<xs:attribute name="total-mem" type="xs:string" use="optional"/>
+			<xs:attribute name="max-count" type="xs:string" use="optional"/>
+			<xs:attribute name="max-cpu-time" type="xs:string" use="optional"/>
+			<xs:attribute name="running-jobs" type="xs:nonNegativeInteger" use="optional"/>
+			<xs:attribute name="jobs-in-queue" type="xs:nonNegativeInteger" use="optional"/>
+			<xs:attribute name="idle-nodes" type="xs:n
 onNegativeInteger" use="optional"/>
+			<xs:attribute name="total-nodes" type="xs:nonNegativeInteger" use="optional"/>
+			<xs:attribute name="os" type="xs:string" use="optional"/>
+			<xs:attribute name="arch" type="xs:string" use="optional"/>
+			<xs:attribute name="type" type="xs:string" use="optional"/>
+			<xs:attribute name="subnet" type="xs:string" use="optional"/>
+		</xs:complexType>
+	</xs:element>
+	<xs:element name="env">
+		<xs:annotation>
+			<xs:documentation>Allows specifying an environment variable that all applications on this site will see</xs:documentation>
+		</xs:annotation>
+		<xs:complexType>
+			<xs:attribute name="key" type="xs:string" use="required"/>
+		</xs:complexType>
+	</xs:element>
+	<xs:element name="application">
+		<xs:annotation>
+			<xs:documentation>Specifies the details of an application installed on a site</xs:documentation>
+		</xs:annotation>
+		<xs:complexType>
+			<xs:sequence maxOccurs="unbounded">
+				<xs:element ref="profile" 
 minOccurs="0" maxOccurs="unbounded"/>
+			</xs:sequence>
+			<xs:attribute name="name" type="xs:string" use="required"/>
+			<xs:attribute name="executable" type="xs:string" use="required"/>
+		</xs:complexType>
+	</xs:element>
+</xs:schema>
\ No newline at end of file

Deleted: branches/faster/src/org/globus/swift/catalog/site/Parser.java
===================================================================
--- branches/faster/src/org/globus/swift/catalog/site/Parser.java	2013-02-24 05:21:18 UTC (rev 6313)
+++ branches/faster/src/org/globus/swift/catalog/site/Parser.java	2013-02-27 03:22:36 UTC (rev 6314)
@@ -1,55 +0,0 @@
-//----------------------------------------------------------------------
-//This code is developed as part of the Java CoG Kit project
-//The terms of the license can be found at http://www.cogkit.org/license
-//This message may not be removed or altered.
-//----------------------------------------------------------------------
-
-/*
- * Created on Jan 7, 2013
- */
-package org.globus.swift.catalog.site;
-
-import java.io.File;
-import java.io.IOException;
-import java.net.URL;
-
-import javax.xml.XMLConstants;
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.parsers.ParserConfigurationException;
-import javax.xml.transform.dom.DOMSource;
-import javax.xml.validation.Schema;
-import javax.xml.validation.SchemaFactory;
-import javax.xml.validation.Validator;
-
-import org.w3c.dom.Document;
-import org.xml.sax.SAXException;
-
-public class Parser {
-    public static final String SCHEMA_RESOURCE = "swift-sites-1.0.xsd";
-    
-    private File src;
-    
-    public Parser(String fileName) {
-        this.src = new File(fileName);
-    }
-    
-    public Document parse() throws ParserConfigurationException, SAXException, IOException {
-        URL schemaURL = Parser.class.getClassLoader().getResource(SCHEMA_RESOURCE);
-        
-        if (schemaURL == null) {
-            throw new IllegalStateException("Sites schema not found in resources: " + SCHEMA_RESOURCE);
-        }
-        
-        SchemaFactory sfactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
-        Schema schema = sfactory.newSchema(schemaURL);
-        
-        DocumentBuilderFactory dfactory = DocumentBuilderFactory.newInstance();
-        dfactory.setSchema(schema);
-        DocumentBuilder dbuilder = dfactory.newDocumentBuilder();
-        Document doc = dbuilder.parse(src); 
-        
-        return doc;
-    }
-    
-}

Added: branches/faster/src/org/globus/swift/catalog/site/SiteCatalogParser.java
===================================================================
--- branches/faster/src/org/globus/swift/catalog/site/SiteCatalogParser.java	                        (rev 0)
+++ branches/faster/src/org/globus/swift/catalog/site/SiteCatalogParser.java	2013-02-27 03:22:36 UTC (rev 6314)
@@ -0,0 +1,93 @@
+//----------------------------------------------------------------------
+//This code is developed as part of the Java CoG Kit project
+//The terms of the license can be found at http://www.cogkit.org/license
+//This message may not be removed or altered.
+//----------------------------------------------------------------------
+
+/*
+ * Created on Jan 7, 2013
+ */
+package org.globus.swift.catalog.site;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+
+import javax.xml.XMLConstants;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.validation.Schema;
+import javax.xml.validation.SchemaFactory;
+
+import org.apache.log4j.Logger;
+import org.w3c.dom.Document;
+import org.xml.sax.ErrorHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+
+public class SiteCatalogParser {
+    public static final Logger logger = Logger.getLogger(SiteCatalogParser.class);
+    
+    public static final String SCHEMA_RESOURCE = "swift-sites-2.0.xsd";
+    static final String JAXP_SCHEMA_LANGUAGE = "http://java.sun.com/xml/jaxp/properties/schemaLanguage";
+    
+    private File src;
+    
+    public SiteCatalogParser(String fileName) {
+        this.src = new File(fileName);
+    }
+    
+    public Document parse() throws ParserConfigurationException, SAXException, IOException {
+        URL schemaURL = SiteCatalogParser.class.getClassLoader().getResource(SCHEMA_RESOURCE);
+        
+        if (schemaURL == null) {
+            throw new IllegalStateException("Sites schema not found in resources: " + SCHEMA_RESOURCE);
+        }
+        
+        SchemaFactory sfactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
+        Schema schema = sfactory.newSchema(schemaURL);
+        
+        DocumentBuilderFactory dfactory = DocumentBuilderFactory.newInstance();
+        dfactory.setNamespaceAware(true);
+        dfactory.setSchema(schema);
+        
+        DocumentBuilder dbuilder = dfactory.newDocumentBuilder();
+        dbuilder.setErrorHandler(new CErrorHandler());
+        Document doc = dbuilder.parse(src);
+                  
+        return doc;
+    }
+    
+    private class CErrorHandler implements ErrorHandler {
+
+        @Override
+        public void warning(SAXParseException e) throws SAXException {
+            print(e, "Warning", false);
+        }
+
+        @Override
+        public void error(SAXParseException e) throws SAXException {
+            print(e, "Error", true);
+        }
+
+        @Override
+        public void fatalError(SAXParseException e) throws SAXException {
+            print(e, "Fatal", true);
+        }
+        
+        private void print(SAXParseException e, String header, boolean err) {
+            String msg = "[" + header + "] " + src.getName() + ", line " + 
+                e.getLineNumber() + ", col " + e.getColumnNumber() + ": " + e.getMessage();
+            if (err) {
+                System.err.println(msg);
+            }
+            else {
+                System.out.println(msg);
+            }
+            if (logger.isInfoEnabled()) {
+                logger.info(msg);
+            }
+        }
+    }
+}

Modified: branches/faster/src/org/griphyn/vdl/karajan/lib/SiteCatalog.java
===================================================================
--- branches/faster/src/org/griphyn/vdl/karajan/lib/SiteCatalog.java	2013-02-24 05:21:18 UTC (rev 6313)
+++ branches/faster/src/org/griphyn/vdl/karajan/lib/SiteCatalog.java	2013-02-27 03:22:36 UTC (rev 6314)
@@ -26,7 +26,7 @@
 import org.globus.cog.karajan.compiled.nodes.functions.AbstractSingleValuedFunction;
 import org.globus.cog.karajan.util.BoundContact;
 import org.globus.cog.karajan.util.ContactSet;
-import org.globus.swift.catalog.site.Parser;
+import org.globus.swift.catalog.site.SiteCatalogParser;
 import org.w3c.dom.Document;
 import org.w3c.dom.NamedNodeMap;
 import org.w3c.dom.Node;
@@ -34,6 +34,10 @@
 
 public class SiteCatalog extends AbstractSingleValuedFunction {
     private ArgRef<String> fileName;
+    
+    private enum Version {
+        V1, V2;
+    }
 
     @Override
     protected Param[] getParams() {
@@ -43,7 +47,7 @@
     @Override
     public Object function(Stack stack) {
         String fn = fileName.getValue(stack);
-        Parser p = new Parser(fn);
+        SiteCatalogParser p = new SiteCatalogParser(fn);
         try {
             Document doc = p.parse();
             return buildResources(doc);
@@ -54,27 +58,68 @@
     }
 
     private Object buildResources(Document doc) {
+        Node root = getRoot(doc);
+        
+        if (root.getLocalName().equals("config")) {
+            return parse(root, Version.V1);
+        }
+        else if (root.getLocalName().equals("sites")) {
+            return parse(root, Version.V1);
+        }
+        else {
+            throw new IllegalArgumentException("Illegal sites file root node: " + root.getLocalName());
+        }
+    }
+    
+    
+
+    private Object parse(Node config, Version v) {
         ContactSet cs = new ContactSet();
-        NodeList pools = doc.getElementsByTagName("config").item(0).getChildNodes();
+        NodeList pools = config.getChildNodes();
         for (int i = 0; i < pools.getLength(); i++) {
-            try {
-                BoundContact bc = pool(pools.item(i));
-                if (bc != null) {
-                    cs.addContact(bc);
+            Node n = pools.item(i);
+            if (n.getNodeType() == Node.ELEMENT_NODE) {
+                try {
+                    BoundContact bc = pool(n, v);
+                    if (bc != null) {
+                        cs.addContact(bc);
+                    }
                 }
+                catch (Exception e) {
+                    throw new ExecutionException(this, "Invalid site entry '" + poolName(n, v) + "': ", e);
+                }
             }
-            catch (Exception e) {
-                throw new ExecutionException(this, "Invalid pool entry '" + attr(pools.item(i), "handle") + "': ", e);
-            }
         }
         return cs;
     }
 
-    private BoundContact pool(Node n) throws InvalidProviderException, ProviderMethodException {
+    private String poolName(Node site, Version v) {
+       if (site.getLocalName().equals("pool")) {
+           return attr(site, "handle");
+       }
+       else if (site.getLocalName().equals("site")) {
+           return attr(site, "name");
+       }
+       else {
+           throw new IllegalArgumentException("Invalid node: " + site.getLocalName());
+       }
+    }
+
+    private Node getRoot(Document doc) {
+        NodeList l = doc.getChildNodes();
+        for (int i = 0; i < l.getLength(); i++) {
+            if (l.item(i).getNodeType() == Node.ELEMENT_NODE) {
+                return l.item(i);
+            }
+        }
+        throw new IllegalArgumentException("Missing root element");
+    }
+
+    private BoundContact pool(Node n, Version v) throws InvalidProviderException, ProviderMethodException {
         if (n.getNodeType() != Node.ELEMENT_NODE) {
             return null;
         }
-        String name = attr(n, "handle");
+        String name = poolName(n, v);
         BoundContact bc = new BoundContact(name);
         
         String sysinfo = attr(n, "sysinfo", null);
@@ -91,10 +136,10 @@
             }
             String ctype = c.getNodeName();
             
-            if (ctype.equals("gridftp")) {
+            if (v == Version.V1 && ctype.equals("gridftp")) {
                 bc.addService(gridftp(c));
             }
-            else if (ctype.equals("jobmanager")) {
+            else if (v == Version.V1 && ctype.equals("jobmanager")) {
                 bc.addService(jobmanager(c));
             }
             else if (ctype.equals("execution")) {
@@ -115,13 +160,19 @@
             else if (ctype.equals("profile")) {
                 profile(bc, c);
             }
+            else if (v == Version.V2 && ctype.equals("application")) {
+                application(bc, c);
+            }
             else {
-                System.err.println("Unknown node type: " + ctype);
+                throw new IllegalArgumentException("Unknown node type: " + ctype);
             }
         }
         return bc;
     }
 
+    private void application(BoundContact bc, Node c) {
+    }
+
     private Service jobmanager(Node n) throws InvalidProviderException, ProviderMethodException {
         String provider;
         String url = attr(n, "url");
@@ -163,6 +214,9 @@
         String provider = attr(n, "provider");
         String url = attr(n, "url", null);
         String jobManager = attr(n, "jobManager", null);
+        if (jobManager == null) {
+            jobManager = attr(n, "jobmanager", null);
+        }
         
         ExecutionService s = new ExecutionServiceImpl();
         s.setProvider(provider);
@@ -230,7 +284,7 @@
                 throw new IllegalArgumentException("Missing " + name);
             }
             else {
-                return attr.getNodeValue();
+                return expandProps(attr.getNodeValue());
             }
         }
         else {
@@ -246,11 +300,47 @@
                 return defVal;
             }
             else {
-                return attr.getNodeValue();
+                return expandProps(attr.getNodeValue());
             }
         }
         else {
             return defVal;
         }
     }
+
+    private String expandProps(String v) {
+        if (v == null) {
+            return null;
+        }
+        StringBuilder sb = new StringBuilder();
+        int li = -1;
+        for (int i = 0; i < v.length(); i++) {
+            char c = v.charAt(i);
+            switch (c) {
+                case '{':
+                    if (li != -1) {
+                        li = -1;
+                        sb.append('{');
+                    }
+                    else {
+                        li = i;
+                    }
+                    break;
+                case '}':
+                    if (li != -1) {
+                        sb.append(System.getProperty(v.substring(li + 1, i)));
+                        li = -1;
+                    }
+                    else {
+                        sb.append(c);
+                    }
+                    break;
+                default:
+                    if (li == -1) {
+                        sb.append(c);
+                    }
+            }
+        }
+        return sb.toString();
+    }
 }




More information about the Swift-commit mailing list