When creating a new entry with Service.insert() method, the current implementation seems to expect a non-empty response stream. If the stream is empty, it throws a SAX ParseException of "Premature end of file".
Technically, this happens because the Entry.parseAtom() method is called and the BaseEntry superclass implementation does not test for the presence of actual content on this stream before trying to parse it. Another way to look at it is that the HttpGDataRequest implementation always assumes an 'insert' will result in a non-empty response and thus returns the connection's input stream even if it is empty. The ATOM publishing spec http://www.ietf.org/internet-drafts/draft-ietf-atompub-protocol-09.txt has the following in section 5.2 (Creating a resource) : 2. If the Member resource was created successfully, the server responds with a status code of 201 and a Location: header that contains the URI of the newly created resource. and later, in section 8.1 When the server generates a response with a status code of 201 ("Created"), it SHOULD also return a response body, which, if provided, MUST be an Atom Entry Document representing the newly- created resource. Clients MUST NOT assume that an Atom Entry returned is a full representation of the member resource. The observed behavior of the GData Service, GDataRequest and BaseEntry implemetations a) throw a ParseException if no response body is sent back, b) ignore the Location header and c) do not provide the developer easy access to the Location header. Thus, the GData Service, GDataRequest and BaseEntry implementation classes 'out of the box' are not quite in spirit with the above spec and cannot be used with all ATOM feeds out there without some work-arounds applied. To work-around this, you need to create subclasses of GoogleGDataRequest and GoogleGDataRequest.Factory. In your subclass of GoogleGDataRequest, overload the constructor to save the authToken and userAgent parameters in instance fields (in addition to passing them on to the super(...) constructor, of course). Then, overload the getResponse method like so: 1) get the input stream from super.getResponseStream() 2) test the stream to see if it has data 2a) if it has data, reset it and return it. 2b) if it doesn't, use the protected httpConn field to get access to the "Location" response header: String loc = httpConn.getHeaderField("Location"); 2b0) if the Location header is not there, then you are screwed because the service is not compliant with the atom spec. You could put in a dummy stream here based on a bytearrayinputstream to avoid the GData bug, if you want. 'Better to throw an exception noting the out-of-compliance feed. 2b1) if the Location header is there, use the specified URL to open a stream to the posted entry. Be sure to use the authToken and userAgent data you saved up above to set the headers for the new connection. Return this stream. In your subclass of GooglGDataRequest.Factory, simply override the getRequest() method so it creates your request subclass. Finally, just remember to use the GoogleService.setRequestFactory() method to set your factory before using the service. It would be really nice if the steps 2->2b1 above were added to the base HttpGDataRequest.getResponseStream() method. This would bring the GData API client libraries into compliance with the Atom spec. Here is a snippet of a replacement GDataRequest implementation as described above: public static class MyGDataRequest extends GoogleGDataRequest { public static class Factory extends GoogleGDataRequest.Factory { public GDataRequest getRequest(RequestType type, URL requestUrl, ContentType contentType) throws IOException, ServiceException { return new MyGDataRequest(type, requestUrl, contentType, authToken, userAgent); } } private AuthToken authToken = null; private String userAgent = null; protected MyGDataRequest(RequestType type, URL requestUrl, ContentType contentType, AuthToken authToken, String userAgent) throws IOException { super(type, requestUrl, contentType, authToken, userAgent); this.authToken = authToken; this.userAgent = userAgent; } public InputStream getResponseStream() throws IOException { InputStream input = super.getResponseStream(); if(input!=null){ //test for non-empty stream InputStream bis = new BufferedInputStream(input); bis.mark(16); int test = bis.read(); if(test<0){ input=null;//empty stream }else{ bis.reset(); input = bis; } } if(input==null){ String location = httpConn.getHeaderField("Location"); if(location!=null){ URL loc = new URL(location); HttpURLConnection locConn = (HttpURLConnection)loc.openConnection(); String authProperty = httpConn.getRequestProperty("Authorization"); if(authToken!=null){ locConn.setRequestProperty("Authorization", authToken.getAuthorizationHeader(loc.toExternalForm())); } if (userAgent != null) { locConn.setRequestProperty("User-Agent", userAgent); } input = locConn.getInputStream(); } } return input; } } --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Google Calendar Data API" group. To post to this group, send email to [email protected] To unsubscribe from this group, send email to [EMAIL PROTECTED] For more options, visit this group at http://groups.google.com/group/google-calendar-help-dataapi -~----------~----~----~----~------~----~------~--~---
