Thursday, 28 May 2009

Java 'PUT' for RESTful service

I was looking around for a Java example of 'PUT' for  the CherryPy/COOL server, and at least came across a 'GET' at Jose Sandovals blog... but not much on the other methods. Andrea Formica (Atlas Muons) is trying to use it to upload calibrations. Here's the solution he came up with, using Apache's library:

public String coolPutCherryPyHttpClient() throws IOException, FileNotFoundException{
String res = "none";
HttpClient client = new HttpClient();
PutMethod put = new PutMethod(this.writenodeUrl);
    try {
String payloaddata = " 999999. "
"";
client.getParams().setAuthenticationPreemptive(true);
Credentials defaultcreds = new UsernamePasswordCredentials(this.namethis.psswd);
client.getState().setCredentials(new AuthScope(serverUrl, portNumber, AuthScope.ANY_REALM), defaultcreds);

NameValuePair par = new NameValuePair();
par.setName("payload");
par.setValue(payloaddata);
NameValuePair[] params = new NameValuePair[1];
params[0] = par;
put.setQueryString(params);


// Execute the method.
        int statusCode = client.executeMethod(put);

        if (statusCode != HttpStatus.SC_OK) {
          System.err.println("Method failed: " + put.getStatusLine());
        }

        // Read the response body.
        byte[] responseBody = put.getResponseBody();

        // Deal with the response.
        // Use caution: ensure correct character encoding and is not binary data
        res = new String(responseBody);
        System.out.println(new String(responseBody));

      } catch (HttpException e) {
        System.err.println("Fatal protocol violation: " + e.getMessage());
        e.printStackTrace();
      } catch (IOException e) {
        System.err.println("Fatal transport error: " + e.getMessage());
        e.printStackTrace();
      } finally {
        // Release the connection.
        put.releaseConnection();
      }  
return res;
}

Wednesday, 27 May 2009

Saxon XSLT 2.0 format-number problem (update!)


Later: following the post below, I managed to ask on the saxon-help list about this. The reply from Michael Kay (He Should Know!)
is that this is the expected behaviour. Looking in the W3C recommendation, it refers to the JDK 1.1 DecimalFormat class, which indeed states:
Symbol Meaning  
0      a digit  
#      a digit, zero shows as absent  
.      placeholder for decimal separator  
,      placeholder for grouping separator.  
;      separates formats.  
-      default negative prefix.  
%      multiply by 100 and show as percentage  
? multiply by 1000 and show as per mille  
¤ currency sign; replaced by currency symbol; if
         doubled, replaced by international currency symbol.
         If present in a pattern, the monetary decimal separator
         is used instead of the decimal separator.  
X      any other characters can be used in the prefix or suffix  
'      used to quote special characters in a prefix or suffix.  
...so , perverse as it seems, the behaviour is as advertised.

Original post:
One of the (many) advantages of using XSLT 2.0 is its supposed ability to understand scientific notation, e.g. 1E-2 is correctly interpreted as 0.01. However the only implementation I know of for XSLT 2.0 is Saxon, so the XSLT 2.0 standard basically defines this one application. Here's a nasty I discovered while trying to format exponential numbers using Saxon 9.1.0.6: Formatting zero with
format-number(xs:double($myNumber),'#.#######')
causes an empty string to be output.
Heres a sample xml:

<numbers>
<test> id="1">1.3</test>
<test> id="2">8E-4</test>
<test> id="3">8E-8</test>
<test> id="4">0.0</test>
<test> id="5">0</test>
</numbers>


and an xslt:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="2.0">
<xsl:output method="text"/>
<xsl:template match="numbers">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="test">
<xsl:value-of select="concat('Original: ',text())"/>
Format with #.#####
'
<xsl:value-of select="format-number(xs:double(.), '#.#####')"/>'
Format with #.#########
'
<xsl:value-of select="format-number(xs:double(.), '#.#########')"/>'
</xsl:template>
</xsl:stylesheet>


But the output is not what one would expect:

Original: 1.3
Format with #.#####
'1.3'
Format with #.#########
'1.3'

Original: 8E-4
Format with #.#####
'.0008'
Format with #.#########
'.0008'

Original: 8E-8
Format with #.#####
''
Format with #.#########
'.00000008'

Original: 0.0
Format with #.#####
''
Format with #.#########
''

Original: 0
Format with #.#####
''
Format with #.#########
''



Tuesday, 26 May 2009

CherryPy Caching: reprise

Finally I had to turn off caching, even with my fix in place. Users reported a malformed xml being returned for certain queries, and it turned out that anything longer than the maxobjectsize was being cached and returned truncated. I'm sure there's an easy way around this, but not sure I want to spend more time on it just now.

Friday, 15 May 2009

CherryPy Caching


I just tried turning on CherryPy web caching, this is one of the few things which receives only very cursory treatment in Sylvain Hellegouarch's  excellent book, 'CherryPy Essentials'. I managed by incorporating the 
conf = {'/':{'tools.caching.on':True}}
line in my code, however I was dismayed to discover that once on, I couldn't form a request would not get the cached result. Neither 'Cache-Control:max-age=0' nor 'Pragma: no-cache' would do it. Looking into the code (I'm using v. 3.1.1), I found the caching.py tool and indeed saw it pays pretty much no attention to the http request headers except (bizarrely, I think) the 'Vary' header. I've inserted a few lines into caching.py to fix this and submitted a trac bug, so we'll see whether this changes. The code lines are reproduced here:
#some people still use Pragma : no-cache to demand a fresh resource
        pragma_no_cache=False
        pragma_header=request.headers.get('Pragma')
        if pragma_header:
            pragma_values=[header.strip() for header in pragma_header.split(',')]
            for this_value in pragma_values:
                if 'no-cache' in this_value:
                    pragma_no_cache=True
                    break
        #added by shaun, look at cache-control
        max_acceptable_age=MemoryCache.delay
        age_seconds=int(response.time - create_time)
        cache_control_header=request.headers.get('Cache-Control')
        if cache_control_header:
            #split string on commas to get the multiple words
            cache_control_values=[header.strip() for header in cache_control_header.split(',')]
            #look for max-age
            for this_value in cache_control_values:
                if ('max-age' in this_value) and ('=' in this_value):
                    age_pair=[age.strip() for age in this_value.split('=')]
                    if age_pair[1].isdigit():
                      max_acceptable_age=int(age_pair[1])
                    break
                  
        #return if the cache is older than the acceptable age
        if (age_seconds > max_acceptable_age) or pragma_no_cache:
            request.cached = False
            request.cacheable = True
            return False
            
        # Add the required Age header
        response.headers["Age"] = str(age_seconds)