apache > cocoon
 

DELI

Introduction

In order for a web server to provide optimized content to different clients it requires a description of the capabilities of the client. Two new compatible standards have been created for describing delivery context based on the Resource Description Framework (RDF): Composite Capabilities / Preferences Profile (CC/PP) created by the W3C and User Agent Profile (UAProf) created by the WAP Forum. These standards allow the efficient transmission of delivery context information to the server even via low bandwidth wireless networks. Instead of sending an entire profile with every request, a client only sends a reference to a profile, stored on a third device known as a profile repository, along with a list of differences specific to this particular client. The process of reassembling the final profile from the profile references and differences is known as profile resolution.

HP Labs has produced an open-source library called DELI that allows Java servlets to resolve HTTP requests containing CC/PP or UAProf information and query the resolved profile. This document describes how DELI may be used within Apache Cocoon. For more information on the DELI library please refer to the DELI web-site. DELI currently uses Jena, an RDF Framework developed at HP Labs. For more details of Jena see Brian McBride's paper and the HP Labs Semantic Web activity homepage.

CC/PP

CC/PP is described in CC/PP Structure and Vocabularies, CC/PP Requirements and Architecture and CC/PP Terminology and Abbreviations. A CC/PP profile is broadly constructed as a two level hierarchy: a profile has a number of components and each component has a number of attributes. A protocol for transmitting CC/PP profiles has been proposed but is based on an experimental variant of HTTP known as HTTP-ex so is not compatible with existing servers. Therefore DELI uses the W-HTTP protocol proposed by UAProf. This has identical functionality to the CC/PP protocol based on HTTP-ex, but is compatible with HTTP/1.1.

UAProf

The UAProf specification is based on the CC/PP specification. Like CC/PP, a UAProf profile is a two level hierarchy composed of components and attributes. Unlike CC/PP, the UAProf specification also proposes a vocabulary - a specific set of components and attributes - to describe the next generation of WAP phones. The specification also describes two protocols for transmitting the profile >from the client to the server. Currently DELI only supports the W-HTTP protocol.

Profiles using the UAProf vocabulary consist of six components: HardwarePlatform, SoftwarePlatform, NetworkCharacteristics, BrowserUA, WapCharacteristics and PushCharacteristics. These components contain attributes. In DELI each attribute has a distinct name and has an associated collection type, attribute type and resolution rule. In UAProf there are three collection types:

  • Simple contains a single value e.g. ColorCapable in HardwarePlatform.
  • Bag contains multiple unordered values e.g. BluetoothProfile in the HardwarePlatform component.
  • Seq contains multiple ordered values e.g. Ccpp-AcceptLanguage in the SoftwarePlatform component.

In addition attributes can have one of four attribute types:

  • String e.g. BrowserName in BrowserUA.
  • Boolean e.g. ColorCapable in HardwarePlatform.
  • Number is a positive integer e.g. BitsPerPixel in HardwarePlatform.
  • Dimension is a pair of positive integers e.g. ScreenSize in HardwarePlatform.

Finally attributes are associated with a resolution rule:

  • Locked indicates the final value of an attribute is the first occurrence of the attribute outside the default description block.
  • Override indicates the final value of an attribute is the last occurrence of the attribute outside the default description block.
  • Append indicates the final value of the attribute is the list of all occurrences of the attribute outside the default description block.

The UAProf vocabulary is described using the file uaprofspec.xml. This describes the attribute name, component, collectionType, attributeType and resolution rule of each component. The vocabulary description file has the following format:

<?xml version="1.0"?>
<vocabspec>
	<attribute>
		<name>CcppAccept</name>
		<component>SoftwarePlatform</component>
		<collectionType>Bag</collectionType>
		<attributeType>Literal</attributeType>
		<resolution>Append</resolution>
	</attribute>
</vocabspec>

DELI can also read vocabularies described using RDF schemas. The WAP Forum have published two such schemas to describe the two versions of UAProf currently in use. However RDF Schema does not provide an easy way of describing attributeType or resolution rule. Therefore the UAProf schemas store this information in the comments field for each attribute. Therefore DELI also parses the comments fields to create the vocabulary. This is not an ideal solution so hopefully a more robust way of representing this information will be used in later versions of UAProf.

W-HTTP Protocol

An example W-HTTP request using this protocol is shown below:

GET /ccpp/html/ HTTP/1.1
Host: localhost
x-wap-profile:"http://127.0.0.1:8080/ccpp/profiles/test09defaults.rdf", 
  "1-Rb0sq/nuUFQU75vAjKyiHw=="
x-wap-profile-diff:1;<?xml version="1.0"?>
 <rdf:RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
  xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" 
  xmlns:prf="http://www.wapforum.org/profiles/UAPROF/ccppschema-20010430#">
  <rdf:Description rdf:ID="MyDeviceProfile">
     <prf:component>
       <rdf:Description rdf:ID="HardwarePlatform">
          <rdf:type 
  rdf:resource="http://www.wapforum.org/profiles/UAPROF/ccppschema-
    20010426#HardwarePlatform"/>
  		<prf:BitsPerPixel>16</prf:BitsPerPixel>
       </rdf:Description>
     </prf:component>
  </rdf:Description>
 </rdf:RDF>

The first two lines describe the resource that is being requested by the client, http://localhost/ccpp/html, and the method being used to make the request, GET, and the protocol being used HTTP/1.1. The remaining lines of the request describe the device delivery context. This is specified using a profile reference and a profile-diff. The profile is referenced via the x-wap-profile line and has the URI

http://127.0.0.1:8080/ccpp/profiles/test09defaults.rdf. 

After the profile reference, there is a value

1-Rb0sq/nuUFQU75vAjKyiHw== 

known as a profile-diff digest. The first part of the profile-diff-digest, 1-, is the profile-diff sequence number. This is used to indicate the order of the profile-diffs and to indicate which profile-diff the profile-diff digest refers to. The remainder of the profile-diff digest is generated by applying the MD5 message digest algorithm and Base64 algorithm to the corresponding profile-diff. The MD5 algorithm takes as input a message of arbitary length and produces as output a 128-bit "fingerprint" or "message-digest" of the input. The Base64 algorithm takes as input arbitary binary data and produces as output printable encoding data.

After the profile-diff digest, the next line contains the x-wap-profile-diff. This request header field also has a profile-diff sequence number which indicates that this profile-diff corresponds to the previous profile-diff-digest. The profile-diff itself consists of the XML fragment which spans the remainder of the request. Multi-line request header fields are permitted by the HTTP/1.1 specification as long as each subsequent line starts with either a tab character or a whitespace.

When the server receives a HTTP request with UAProf request headers, it has to perform profile resolution i.e. retrieve the referenced profile(s) and any further profiles referenced via default blocks. It then has to merge these profiles and the profile-diffs while applying the UAProf resolution rules.

Configuring DELI

In order to use DELI, you need to enable the DELI component. In addition, you may want to configure DELI using deliConfig.xml, the DELI configuration file or legacyDevices.xml, the DELI legacy device support file.

Cocoon.xconf

In order to use DELI you need to configure Deli and specify the configuration file. You can either do this in deli.xconf in which case you will need to rebuild Cocoon or change the deployed cocoon.xconf in the web-server WEB-INF directory:

  <deli class="org.apache.cocoon.components.deli.DeliImpl">
    <parameter name="deli-config-file" value="deli/config/deliConfig.xml"/>
  </deli>

Sitemap.xmap

In order to make profile information available to your stylesheet then you need to add <map:parameter name="use-deli" value="true"/> to the match that specifies your stylesheet in sitemap.xmap. Here is the match used for the deli test stylesheet:

   <map:match pattern="deli.html">
     <map:generate src="docs/samples/hello-page.xml"/>
     <map:transform src="stylesheets/deli_test.xsl" type="deli">
         <map:parameter name="use-deli" value="true"/>
     </map:transform>
     <map:serialize type="html"/>
   </map:match>

Main Configuration File

DELI also has its own configuration files that are found in the resources\deli\config directory. The most important one, deliConfig.xml is used to configure the main DELI options:

<?xml version="1.0"?>
<deli>
 <localProfilesFile>WEB-INF/deli/config/localProfiles.xml</localProfilesFile>
 <localProfilesPath>WEB-INF/deli/legacyProfiles</localProfilesPath>
 <useLocalProfilesIfNoCCPP>true</useLocalProfilesIfNoCCPP>
 <useLocalProfilesInAdditionToCCPP>false</useLocalProfilesInAdditionToCCPP>
 <debug>false</debug>
 <printDefaults>true</printDefaults>
 <printProfileBeforeMerge>false</printProfileBeforeMerge>
 <processUndefinedAttributes>true</processUndefinedAttributes>
 <useCapabilityClasses>false</useCapabilityClasses> 
 <capabilityClassFile>WEB-INF/deli/config/capClass.xml</capabilityClassFile> 
 <namespaceConfigFile>WEB-INF/deli/config/namespaceConfig.xml</namespaceConfigFile>
 <!-- note that the default uri is also hardcoded -->
 <rdfsUri>http://www.w3.org/1999/PR-rdf-schema-19990303#</rdfsUri>
 <rdfsUri>http://www.w3.org/TR/PR-rdf-schema#</rdfsUri>
</deli>

This file can contain a number of configuration directives described in in the following sections:

Caching options

The caching options control how the server caches referenced profiles. DELI can either cache profiles indefinitely or update stale profiles after a set interval. It is also possible to configure the maximum size of the profile cache.

Element Name

Default Value

Description

maxCachedProfileLifetime

24 hours

The maximum lifetime of a cached profile in hours.

maxCacheSize

100

The maximum number of profiles in the profile cache.

refreshStaleProfiles

false

Do we refresh cached profiles after the maximum lifetime has expired?

Debugging options

The debugging options are used to control the information that DELI prints to the Servlet engine console.

Element Name

Default Value

Description

debug

true

Is the automatic debug log information turned on?

printDefaults

true

Print both default and override values of attributes for debugging purposes?

printProfileBeforeMerge

false

Print the profile before merging for debugging purposes?

Legacy device options

As already mentioned DELI can support legacy devices by recognising the user-agent string supplied by a client and mapping it on to a profile. In order to use this facility it is necessary to supply an XML file that contains information about legacy device user-agent strings and the corresponding profile URLs. The format for the legacy device file is described in a subsequent section.

Element Name

Default Value

Description

useLocalProfilesIfNoCCPP

true

Use the legacy device database if devices send no CC/PP information?

localProfilesFile

legacyDevice.xml

The file containing the legacy device database.

Protocol options

It is possible to switch on whitespace normalisation in profile-diffs prior to calculating the profile-diff-digest so that additional whitespaces added by the proxy are ignored. To use this option clients must also support whitespace normalisation.

Element Name

Default Value

Description

normaliseWhitespaceInProfileDiff

true

Is whitespace normalisation of the profile-diff prior to calculating the profile-diff-digest turned on?

Vocabulary options

DELI has a number of vocabulary options. Firstly it is possible to configure the vocabulary using an XML file, or if you are using UAProf using a UAProf RDF Schema. You can find examples of both approaches in this distribution. Secondly it is possible to specify the URI to be used for the RDF namespace. Thirdly it is possible to set the string used to represent components and defaults in the vocabulary. This is important because the two standards currently use different cases for the first letter of default elements (CC/PP uses "default" whereas UAProf uses "Default").

Element Name

Default Value

Description

vocabularyFile

uaprofspec.xml

The file containing the vocabulary specification.

schemaVocabularyFile

ccppschema-20000405.rdfs

The file containing the vocabulary specification as an UAProf RDF Schema. Use the attribute namespace to configure which namespace the schema corresponds to.

rdfUri

http://www.w3.org/1999/02/22-rdf-syntax-ns#

The namespace used for RDF constructs.

componentProperty

component

The name for components.

defaultProperty

Default

The name for defaults

Configuring Legacy Devices

It is easy to configure DELI to recognise legacy devices via user-agent strings. A user-agent string is a string sent by the client to the server as part of the HTTP request. It allows different browsers to be identified. The legacy device configuration file maps user-agent strings on to profile either on the local filestore or on a profile repository. By default this is done in the legacyDevice.xml file which has the following format:

<?xml version="1.0" encoding="UTF-8"?>
<devices>
<!-- Alcatel -->
  <device>
    <ua value="Alcatel-BF4/2.0" profile="Alcatel_OT512.rdf"/>
  </device>
  <device>
    <ua value="Alcatel-BE4/1.0" profile="Alcatel_OT301.rdf"/>
  </device>
</devices>

Where vale is a device unique string found in the user-agent string of the device and profile is either a local file or a URL for the appropriate legacy profile.

Writing CC/PP and UAProf aware stylesheets

Once you have got DELI running on Cocoon, the next step in creating a CC/PP aware site is to create some stylesheets that use profile information. DELI makes CC/PP or UAProf attributes available to XSLT stylesheets as parameters. In the process of doing this, DELI 'flattens' the profiles by omitting the component information. Hence to retrieve the CcppAccept attribute you use the XPath deli-capabilities/browser/CcppAccept whereas to retrieve the ScreenSize attribute you use the XPath deli-capabilities/browser/ScreenSize. In addition where attributes contain multiple values e.g. Bags or Sequences those values are separated using <li> elements. Hence to retrieve the individual elements you use the XPath deli-capabilities/browser/CcppAccept/li. The following code demonstrates how to retrieve both a simple and a complex attribute. For more complex examples see the deli_test.xsl stylesheet with Cocoon.

<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
 <xsl:param name="deli-capabilities"/>
 <xsl:template match="/">
  <xsl:if test="contains($deli-capabilities/browser/CcppAccept/li,'wml')">
   <xsl:call-template name="wmldevice"/>
  </xsl:if>
  <xsl:if test="not(contains($deli-capabilities/browser/CcppAccept/li,'wml'))">
   <xsl:call-template name="htmldevice"/>
  </xsl:if>
 </xsl:template>

 <xsl:template name="wmldevice">
  <wml>
   <card id="init" newcontext="true">
    <p>
     <xsl:call-template name="capabilities"/>
    </p>
   </card>
  </wml>
 </xsl:template>

 <xsl:template name="htmldevice">
  <html>
   <head>
    <title>Test Page for DELI in Cocoon</title>
   </head>
   <body>
    <xsl:call-template name="capabilities"/>
   </body>
  </html>
 </xsl:template>

 <xsl:template name="capabilities">
  <xsl:if test="$deli-capabilities/browser/ImageCapable">
   ImageCapable: <xsl:value-of select="$deli-capabilities/browser/ImageCapable"/>
   <br/>
  </xsl:if>
  <xsl:if test="$deli-capabilities/browser/CcppAccept">
   CcppAccept:
   <xsl:for-each select="$deli-capabilities/browser/CcppAccept/li">
    <xsl:value-of select="."/>, 
   </xsl:for-each>
   <br/>
  </xsl:if>
 </xsl:template>

</xsl:stylesheet>

Incorporating DELI into other Cocoon components

If you want to use DELI in other Cocoon components, DELI needs to go through an initialization phase in order to read in configuration files. This occurs in initialization() in DeliImpl.java in the Cocoon DELI component in this line:

Workspace.getInstance().configure(this.servletContext, this.deliConfig);

i.e. it constructs a DELI workspace object. In addition, DeliImpl.java has a number of methods to get profile information, but the one which is of most use is getProfile(). For examples of how to call DELI, see DeliTransformer.java - here are the relevant pieces.

First import DELI:

import org.apache.cocoon.components.deli.Deli;

Then in compose(), initialize DELI:

if (this.manager.hasComponent(Deli.ROLE)) {
 if (this.getLogger().isDebugEnabled()) {
     getLogger().debug("Looking up " + Deli.ROLE);
}
this.deli = (Deli) this.manager.lookup(Deli.ROLE);
} else {
  if (this.getLogger().isDebugEnabled()) {
    getLogger().debug("Deli is not available");
}
}

Then in getLogicSheetParameters, call DELI to get a profile and convert it into a DOM tree to give to XSLT as a parameter:

if (this.deli != null && this._useDeli) {
  try {
   Request request = ObjectModelHelper.getRequest(objectModel);
   if (map == null) {
    map = new HashMap();
  }

  org.w3c.dom.Document deliCapabilities = this.deli.getUACapabilities(request);
  map.put("deli-capabilities", deliCapabilities);
  } catch (Exception e) {
    getLogger().error("Error setting DELI info", e);
  }
}

However, you don't want to pipe DELI to XSLT so here is some more example code showing how to use DELI in Cocoon if you want to use the DELI API directly:

Import the deli component and the DELI API:

import org.apache.cocoon.components.deli.Deli;

import com.hp.hpl.deli.Profile;
import com.hp.hpl.deli.ProfileAttribute;

Add code to initialize the compose method as before

  public void compose(ComponentManager manager) throws ComponentException
  {
    try
    {
      deli = (Deli)manager.lookup(Deli.ROLE);
    } 
    catch(ComponentException ce)
    {
      logger.log(Level.ERROR, "Cannot get ref to Deli", ce);
      ce.printStackTrace();
    }
  }

Some example code that shows how to extract WmlDeckSize from a DELI profile

try
{
 Profile theProfile = deli.getProfile(request);
 if(theProfile != null)
 {
   ProfileAttribute attrib = theProfile.getAttribute("WmlDeckSize");
   if(attrib != null)
   {
      Vector temp = (Vector)attrib.get();
      if(temp != null)
      {
          deckSizeString = temp.get(0).toString();
          System.out.println("Determined WmlDeckSize from DELI: " + deckSizeString);
      }
   }
 }
 if(deckSizeString == null)
 {
    deckSizeString = "1100";
    System.out.println("Using fallback WmlDeckSize of " + deckSizeString);
 }
}
catch(Exception ex)
{
    System.out.println(ex.toString());
    ex.printStackTrace();
}
}

More information ?

For more information on the DELI library please refer to the DELI web-site.