/*
* Copyright 2011 Open Source Applications Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.osaf.caldav4j.methods;
import java.io.IOException;
import java.io.InputStream;
import net.fortuna.ical4j.data.CalendarBuilder;
import net.fortuna.ical4j.data.ParserException;
import net.fortuna.ical4j.model.Calendar;
import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpConnection;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.HttpState;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.webdav.lib.methods.DepthSupport;
import org.apache.webdav.lib.methods.HttpRequestBodyMethodBase;
import org.apache.webdav.lib.util.XMLDebugOutputer;
import org.osaf.caldav4j.CalDAVConstants;
import org.osaf.caldav4j.exceptions.CalDAV4JException;
import org.osaf.caldav4j.exceptions.CalDAV4JProtocolException;
import org.osaf.caldav4j.exceptions.DOMValidationException;
import org.osaf.caldav4j.model.request.CalDAVReportRequest;
import org.osaf.caldav4j.util.UrlUtils;
import org.osaf.caldav4j.util.XMLUtils;
import org.w3c.dom.Document;
/**
* This method implements the REPORT method described in
* caldav RFC4791. As it's requestBody is an XML document
* it takes as a parameter a class describing the document, then
* generates the body before passing it to executeMethod.
*
* so:
* 1- set this.reportRequest
* 2- the class creates the body from the XML
*
* This method differs from {@code CalDAVReportMethod} in that its response is expected to be a calendar, rather than
* XML.
*
* @author robipolli@gmail.com
* @author <a href="mailto:markhobson@gmail.com">Mark Hobson</a>
* @see CalDAVReportMethod
*/
public class CalendarCalDAVReportMethod extends HttpRequestBodyMethodBase implements DepthSupport, CalDAVConstants {
// TODO: rationalise with CalDAVReportMethod
private static final Log log = LogFactory
.getLog(CalendarCalDAVReportMethod.class);
// debug level
private int debug = 0;
/**
* XML Debug Outputer
*/
private XMLDebugOutputer xo = new XMLDebugOutputer();
private CalendarBuilder calendarBuilder = null;
public CalendarBuilder getCalendarBuilder() {
return calendarBuilder;
}
public void setCalendarBuilder(CalendarBuilder calendarBuilder) {
this.calendarBuilder = calendarBuilder;
}
/** this is the XML document that will be generated by @link{generateRequestBody()} */
private CalDAVReportRequest reportRequest;
private int depth = DEPTH_1;
protected CalendarCalDAVReportMethod() {
}
protected CalendarCalDAVReportMethod(String path, CalDAVReportRequest reportRequest) {
this.reportRequest = reportRequest;
setPath(path);
}
/**
* Depth setter.
*
* @param depth New depth value
*/
public void setDepth(int depth) {
this.depth = depth;
}
/**
* Depth getter.
*
* @return int depth value
*/
public int getDepth() {
return depth;
}
@Override
public String getName() {
return CalDAVConstants.METHOD_REPORT;
}
public CalDAVReportRequest getReportRequest() {
return reportRequest;
}
public void setReportRequest(CalDAVReportRequest reportRequest) {
this.reportRequest = reportRequest;
}
/**
* Generate additional headers needed by the request.
*
* @param state State token
* @param conn The connection being used to make the request.
*/
@Override
public void addRequestHeaders(HttpState state, HttpConnection conn)
throws IOException, HttpException {
super.addRequestHeaders(state, conn);
switch (depth) {
case DEPTH_0:
super.setRequestHeader("Depth", "0");
break;
case DEPTH_1:
super.setRequestHeader("Depth", "1");
break;
case DEPTH_INFINITY:
super.setRequestHeader("Depth", CalDAVConstants.INFINITY_STRING);
break;
}
if (getRequestHeader(HEADER_CONTENT_TYPE) == null) {
addRequestHeader(HEADER_CONTENT_TYPE,CONTENT_TYPE_TEXT_XML);
}
}
// TODO: centralise; copied from XMLResponseMethodBase
/**
* Return the length (in bytes) of my request body, suitable for use in a
* <tt>Content-Length</tt> header.
*
* <p>
* Return <tt>-1</tt> when the content-length is unknown.
* </p>
*
* <p>
* This implementation returns <tt>0</tt>, indicating that the request has
* no body.
* </p>
*
* @return <tt>0</tt>, indicating that the request has no body.
*/
@Override
protected int getRequestContentLength() {
if (!isRequestContentAlreadySet()) {
String contents = generateRequestBody();
// be nice - allow overriding functions to return null or empty
// strings for no content.
if (contents == null)
contents = "";
setRequestBody(contents);
if (debug > 0) {
System.out.println("\n>>>>>>> to server ---------------------------------------------------");
System.out.println(getName() + " " +
getPath() + (getQueryString() != null ? "?" + getQueryString() : "") + " " + "HTTP/1.1");
Header[] headers = getRequestHeaders();
for (int i = 0; i < headers.length; i++) {
Header header = headers[i];
System.out.print(header.toString());
}
System.out.println("Content-Length: "+super.getRequestContentLength());
if (this instanceof DepthSupport) {
System.out.println("Depth: "+((DepthSupport)this).getDepth());
}
System.out.println();
xo.print(contents);
System.out.println("------------------------------------------------------------------------");
}
}
return super.getRequestContentLength();
}
/**
* Generates a request body from the calendar query.
*/
protected String generateRequestBody() {
Document doc = null;
try {
doc = reportRequest.createNewDocument(XMLUtils
.getDOMImplementation());
} catch (DOMValidationException domve) {
log.error("Error trying to create DOM from CalDAVReportRequest: ", domve);
throw new RuntimeException(domve);
}
return XMLUtils.toPrettyXML(doc);
}
// TODO: centralise; copied from GetMethod
public Calendar getResponseBodyAsCalendar() throws
ParserException, CalDAV4JException {
Calendar ret = null;
InputStream stream = null;
try {
Header header = getResponseHeader(CalDAVConstants.HEADER_CONTENT_TYPE);
String contentType = header.getValue();
if (contentType.startsWith(CalDAVConstants.CONTENT_TYPE_CALENDAR)) {
stream = getResponseBodyAsStream();
ret = calendarBuilder.build(stream);
return ret;
}
log.error("Expected content-type text/calendar. Was: " + contentType);
throw new CalDAV4JProtocolException("Expected content-type text/calendar. Was: " + contentType );
} catch (IOException e) {
if (stream != null ) { //the server sends the response
if (log.isWarnEnabled()) {
log.warn("Server response is " + UrlUtils.parseISToString(stream));
}
}
throw new CalDAV4JException("Error retrieving and parsing server response at " + getPath(), e);
}
}
// remove double slashes
@Override
public void setPath(String path) {
super.setPath(UrlUtils.removeDoubleSlashes(path));
}
}