CCML1.0


CCML is an XML-based language for agent-driven content negotiation. CCML1.0 comprises a set of namespaced XML elements that can be embedded in any XML document. Such an enclosing document may be resolved against an environmental description, yielding a new document which describes how to deploy an entity in that environment in a way which is precisely compatible with that environment.

CCML1.0 elements are meant to be embedded in other XML documents. They use the namespace http://www.activenet.lancs.ac.uk/ns/ccml, and this documentation uses the prefix cc as if xmlns:cc="http://www.activenet.lancs.ac.uk/ns/ccml" has been defined in the enclosing document. The domain has only historical significance.

An XML document containing CCML is processed against an environment description (a set of named properties). The result is another XML document with the CCML stripped out, or failure of the original document to be compatible with the environment.

Each element in the enclosing document is deemed to either succeed or fail in matching (i.e., being compatible with) the environment. CCML elements succeed or fail as described below. Non-CCML elements and other non-element node types always succeed.

Environmental comparison

The basic CCML element is <cc:rel>, and is used to express comparisons against the environment description. A <cc:rel prop="propname" arg="arg"> compares the value of an environment property called propname against a value arg. The attribute type specifies how the two values are to be interpreted, while op specifies how they are to be compared.

See the range of property expressions available.

Logical combinations

A <cc:logic> element combines the results of its direct CCML children. (Other content will be stripped.) With the default op="or", it succeeds if any of those children succeed. With op="and", it succeeds only if all of those children succeed. nor and nand invert those results respectively.

The following example fragment succeeds only if the property arch includes the token i386 and plugin-types includes Proxylet:

<cc:logic op="and">
  <cc:rel prop="arch" arg="i386">
  <cc:rel prop="plugin-types" arg="Proxylet">
</cc:logic>

See how logical operations appear in earlier versions.

Context

Each CCML element is processed in a certain context determined by its containing elements. A root context applies to the root element. If a CCML element in the root context fails, the whole process has failed. Unless otherwise specified, each element is processed in the same context as its parent's. The element type <cc:option> provides a new context which masks the outer context, so a local failure within it does not cause wider or global failure. The <cc:choice> element type re-exposes its own context to failures of its contained elements, but only if all fail.

Optional content

When a <cc:option> element is processed, all CCML descendants not nested in other <cc:option>s are checked to see if they match. If any fail, the entire <cc:option> is removed, including non-CCML content; otherwise, the nested CCML elements are stripped, leaving only the non-CCML elements behind. Note that a <cc:option> that is not a direct child of a <cc:choice> is always considered to succeed in matching the environment, even if its content does not.

The following element contains two conditions and some application-defined content:

<cc:option>
  <cc:rel prop="arch" arg="i386">
  <cc:rel prop="plugin-types" arg="Proxylet>
  <myapp:some-elem>
    Some content goes here.
  </myapp:some-elem>
</cc:option>

If both conditions are met (i.e., there is an implicit AND here), the fragment will resolve to:


  
  
  <myapp:some-elem>
    Some content goes here.
  </myapp:some-elem>

Alternatives

A <cc:choice> must consist of zero or more <cc:option>s. The first of these whose content matches the environment will be retained, and all other content will be stripped. If no child's content matches, the <cc:choice> fails.

The following element specifies three mutually exclusive options:

<cc:choice>
  <cc:option>
    <cc:rel prop="system" arg="linux">
    <myapp:load-resource href="linux.jar" />
  </cc:option>

  <cc:option>
    <cc:rel prop="system" arg="macos">
    <myapp:load-resource href="mac.jar" />
  </cc:option>

  <cc:option>
    <cc:rel prop="system" arg="windows">
    <myapp:load-resource href="win.jar" />
  </cc:option>
</cc:choice>

The content of the first option that matches will remain after processing, e.g.:

    <myapp:load-resource href="mac.jar" />

If none match, the <cc:choice> has failed. This could result in the whole document failing (and producing an exception), or it could be masked by an enclosing <cc:option>. To prevent failure, add an empty <cc:choice> at the end.

Processing CCML in Java

To process CCML in Java, create a Properties describing your environment, create a PropertiesDOMFilter from it, and pass a DOM Element to it representing the document containing embedded CCML. The document will be modified in-place, according to the environment description. A SelectionException is thrown if some critical element failed to match the environment.

The following example program resolves files named as arguments against Java's own system properties:

import java.util.Properties;
import java.io.File;
import org.xml.sax.InputSource;
import org.w3c.dom.Document;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import uk.ac.lancs.scc.ccml.SelectionException;
import uk.ac.lancs.scc.ccml.DOMFilter;
import uk.ac.lancs.scc.ccml.PropertiesDOMFilter;

public class TestCCML {
    public static void main(String args) throws Exception {
        Properties properties = System.getProperties();
        properties.store(System.out, "Configuration properties");
    
        DOMFilter filter = new PropertiesDOMFilter(properties);
        DocumentBuilderFactory documentBuilderFactory =
            DocumentBuilderFactory.newInstance();
        documentBuilderFactory.setIgnoringComments(true);
        documentBuilderFactory.setNamespaceAware(true);
        documentBuilderFactory.setIgnoringElementContentWhitespace(true);
        documentBuilderFactory.setValidating(false);
    
        for (String filename : args) {
            try {
                // Read in the document.
                File file = new File(filename);
                InputSource source = new InputSource(new FileInputStream(file));
                DocumentBuilder builder =
                    documentBuilderFactory.newDocumentBuilder();
                Document document = builder.parse(source);
    
                // Filter CCML from the document.
                filter.filter(document.getDocumentElement());
    
                // Write out the filtered document.
                System.out.println();
                System.out.println(filename + ":");
                TransformerFactory transformerFactory =
                    TransformerFactory.newInstance();
                Transformer transformer = transformerFactory.newTransformer();
                DOMSource domSource = new DOMSource(document);
                StreamResult result = new StreamResult(System.out);
                transformer.transform(domSource, result);
                System.out.println();
            } catch (SelectionException ex) {
                System.out.println(filename + ": failed\n" + ex);
            } catch (Exception ex) {
                System.out.println(ex);
            }
        }
    }
}

Comparison with CCML0.4

  • Lower case is used throughout.

  • There is no DTD, schema, or public identifier.

  • A URI namespace is specified.

  • There are no reference expressions.

  • CCML is not a separate document, but embedded in another. This obviates action elements, and only expression elements are required.

  • Nesting of <cc:choice> elements is fully mandated.