Java tutorial
/* * Copyright 2006-2007 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.cosmo.dav.impl; import java.io.IOException; import java.net.URL; import java.util.Date; import javax.activation.MimeType; import javax.activation.MimeTypeParseException; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServletRequest; import org.apache.abdera.util.EntityTag; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.jackrabbit.webdav.WebdavRequestImpl; import org.apache.jackrabbit.webdav.property.DavPropertyName; import org.apache.jackrabbit.webdav.property.DavPropertyNameSet; import org.apache.jackrabbit.webdav.property.DavPropertySet; import org.apache.jackrabbit.webdav.version.report.ReportInfo; import org.apache.jackrabbit.webdav.xml.DomUtil; import org.apache.jackrabbit.webdav.xml.ElementIterator; import org.osaf.cosmo.dav.BadRequestException; import org.osaf.cosmo.dav.DavException; import org.osaf.cosmo.dav.DavRequest; import org.osaf.cosmo.dav.DavResourceLocator; import org.osaf.cosmo.dav.DavResourceLocatorFactory; import org.osaf.cosmo.dav.ExtendedDavConstants; import org.osaf.cosmo.dav.UnsupportedMediaTypeException; import org.osaf.cosmo.dav.acl.AclConstants; import org.osaf.cosmo.dav.acl.DavPrivilege; import org.osaf.cosmo.dav.acl.DavPrivilegeSet; import org.osaf.cosmo.dav.caldav.CaldavConstants; import org.osaf.cosmo.dav.property.StandardDavProperty; import org.osaf.cosmo.dav.ticket.TicketConstants; import org.osaf.cosmo.model.EntityFactory; import org.osaf.cosmo.model.Ticket; import org.osaf.cosmo.util.BufferedServletInputStream; import org.osaf.cosmo.xml.DomWriter; import org.w3c.dom.Document; import org.w3c.dom.Element; /** */ public class StandardDavRequest extends WebdavRequestImpl implements DavRequest, ExtendedDavConstants, AclConstants, CaldavConstants, TicketConstants { private static final Log log = LogFactory.getLog(StandardDavRequest.class); private static final MimeType APPLICATION_XML = registerMimeType("application/xml"); private static final MimeType TEXT_XML = registerMimeType("text/xml"); private static final MimeType registerMimeType(String s) { try { return new MimeType(s); } catch (Exception e) { throw new RuntimeException("Can't register MIME type " + s, e); } } private int propfindType = PROPFIND_ALL_PROP; private DavPropertyNameSet propfindProps; private DavPropertySet proppatchSet; private DavPropertyNameSet proppatchRemove; private DavPropertySet mkcalendarSet; private Ticket ticket; private ReportInfo reportInfo; private boolean bufferRequestContent = false; private long bufferedContentLength = -1; private DavResourceLocatorFactory locatorFactory; private DavResourceLocator locator; private DavResourceLocator destinationLocator; private EntityFactory entityFactory; public StandardDavRequest(HttpServletRequest request, DavResourceLocatorFactory factory, EntityFactory entityFactory) { this(request, factory, entityFactory, false); } public StandardDavRequest(HttpServletRequest request, DavResourceLocatorFactory factory, EntityFactory entityFactory, boolean bufferRequestContent) { super(request, null); this.locatorFactory = factory; this.bufferRequestContent = bufferRequestContent; this.entityFactory = entityFactory; } // DavRequest methods public EntityTag[] getIfMatch() { return EntityTag.parseTags(getHeader("If-Match")); } public Date getIfModifiedSince() { long value = getDateHeader("If-Modified-Since"); return value != -1 ? new Date(value) : null; } public EntityTag[] getIfNoneMatch() { return EntityTag.parseTags(getHeader("If-None-Match")); } public Date getIfUnmodifiedSince() { long value = getDateHeader("If-Unmodified-Since"); return value != -1 ? new Date(value) : null; } public int getPropFindType() throws DavException { if (propfindProps == null) parsePropFindRequest(); return propfindType; } public DavPropertyNameSet getPropFindProperties() throws DavException { if (propfindProps == null) parsePropFindRequest(); return propfindProps; } public DavPropertySet getProppatchSetProperties() throws DavException { if (proppatchSet == null) parsePropPatchRequest(); return proppatchSet; } public DavPropertyNameSet getProppatchRemoveProperties() throws DavException { if (proppatchRemove == null) parsePropPatchRequest(); return proppatchRemove; } public DavResourceLocator getResourceLocator() { if (locator == null) { URL context = null; try { String basePath = getContextPath() + getServletPath(); context = new URL(getScheme(), getServerName(), getServerPort(), basePath); locator = locatorFactory.createResourceLocatorByUri(context, getRequestURI()); } catch (Exception e) { throw new RuntimeException(e); } } return locator; } public DavResourceLocator getDestinationResourceLocator() throws DavException { if (destinationLocator != null) return destinationLocator; String destination = getHeader(HEADER_DESTINATION); if (destination == null) return null; URL context = ((DavResourceLocator) getResourceLocator()).getContext(); destinationLocator = locatorFactory.createResourceLocatorByUri(context, destination); return destinationLocator; } // CaldavRequest methods public DavPropertySet getMkCalendarSetProperties() throws DavException { if (mkcalendarSet == null) mkcalendarSet = parseMkCalendarRequest(); return mkcalendarSet; } // TicketDavRequest methods public Ticket getTicketInfo() throws DavException { if (ticket == null) ticket = parseTicketRequest(); return ticket; } public String getTicketKey() throws DavException { String key = getParameter(PARAM_TICKET); if (key == null) key = getHeader(HEADER_TICKET); if (key != null) return key; throw new BadRequestException("Request does not contain a ticket key"); } public ReportInfo getReportInfo() throws DavException { if (reportInfo == null) reportInfo = parseReportRequest(); return reportInfo; } // private methods private Document getSafeRequestDocument() throws DavException { return getSafeRequestDocument(true); } private Document getSafeRequestDocument(boolean requireDocument) throws DavException { try { if (StringUtils.isBlank(getContentType())) { if (requireDocument) throw new BadRequestException("No Content-Type specified"); return null; } MimeType mimeType = new MimeType(getContentType()); if (!(mimeType.match(APPLICATION_XML) || mimeType.match(TEXT_XML))) throw new UnsupportedMediaTypeException( "Expected Content-Type " + APPLICATION_XML + " or " + TEXT_XML); return getRequestDocument(); } catch (MimeTypeParseException e) { throw new UnsupportedMediaTypeException(e.getMessage()); } catch (IllegalArgumentException e) { Throwable cause = e.getCause(); String msg = cause != null ? cause.getMessage() : null; if (msg == null) msg = "Unknown error parsing request document"; throw new BadRequestException(msg); } } private void dumpPropFindRequest(Element root) { if (!log.isTraceEnabled()) return; StringBuffer sb = new StringBuffer( "\n------------------------ Dump of propFind request -------------------\n"); try { if (root == null) sb.append("ALL props requested\n"); else sb.append(DomWriter.write(root)).append("\n"); } catch (Exception e) { // skip reporting } sb.append("------------------------ End dump of propFind request -------------------"); log.trace(sb); } private void parsePropFindRequest() throws DavException { Document requestDocument = getSafeRequestDocument(); if (requestDocument == null) { // treat as allprop propfindType = PROPFIND_ALL_PROP; propfindProps = new DavPropertyNameSet(); dumpPropFindRequest(null); return; } Element root = requestDocument.getDocumentElement(); dumpPropFindRequest(root); if (!DomUtil.matches(root, XML_PROPFIND, NAMESPACE)) throw new BadRequestException("Expected " + QN_PROPFIND + " root element"); Element prop = DomUtil.getChildElement(root, XML_PROP, NAMESPACE); if (prop != null) { propfindType = PROPFIND_BY_PROPERTY; propfindProps = new DavPropertyNameSet(prop); return; } if (DomUtil.getChildElement(root, XML_PROPNAME, NAMESPACE) != null) { propfindType = PROPFIND_PROPERTY_NAMES; propfindProps = new DavPropertyNameSet(); return; } if (DomUtil.getChildElement(root, XML_ALLPROP, NAMESPACE) != null) { propfindType = PROPFIND_ALL_PROP; propfindProps = new DavPropertyNameSet(); Element include = DomUtil.getChildElement(root, "include", NAMESPACE); if (include != null) { ElementIterator included = DomUtil.getChildren(include); while (included.hasNext()) { DavPropertyName name = DavPropertyName.createFromXml(included.nextElement()); propfindProps.add(name); } } return; } throw new BadRequestException("Expected one of " + XML_PROP + ", " + XML_PROPNAME + ", or " + XML_ALLPROP + " as child of " + QN_PROPFIND); } private void parsePropPatchRequest() throws DavException { Document requestDocument = getSafeRequestDocument(); if (requestDocument == null) throw new BadRequestException("PROPPATCH requires entity body"); Element root = requestDocument.getDocumentElement(); if (!DomUtil.matches(root, XML_PROPERTYUPDATE, NAMESPACE)) throw new BadRequestException("Expected " + QN_PROPERTYUPDATE + " root element"); ElementIterator sets = DomUtil.getChildren(root, XML_SET, NAMESPACE); ElementIterator removes = DomUtil.getChildren(root, XML_REMOVE, NAMESPACE); if (!(sets.hasNext() || removes.hasNext())) throw new BadRequestException("Expected at least one of " + QN_REMOVE + " and " + QN_SET + " as a child of " + QN_PROPERTYUPDATE); Element prop = null; ElementIterator i = null; proppatchSet = new DavPropertySet(); while (sets.hasNext()) { Element set = sets.nextElement(); prop = DomUtil.getChildElement(set, XML_PROP, NAMESPACE); if (prop == null) throw new BadRequestException("Expected " + QN_PROP + " child of " + QN_SET); i = DomUtil.getChildren(prop); while (i.hasNext()) { StandardDavProperty p = StandardDavProperty.createFromXml(i.nextElement()); proppatchSet.add(p); } } proppatchRemove = new DavPropertyNameSet(); while (removes.hasNext()) { Element remove = removes.nextElement(); prop = DomUtil.getChildElement(remove, XML_PROP, NAMESPACE); if (prop == null) throw new BadRequestException("Expected " + QN_PROP + " child of " + QN_REMOVE); i = DomUtil.getChildren(prop); while (i.hasNext()) proppatchRemove.add(DavPropertyName.createFromXml(i.nextElement())); } } private DavPropertySet parseMkCalendarRequest() throws DavException { DavPropertySet propertySet = new DavPropertySet(); Document requestDocument = getSafeRequestDocument(false); if (requestDocument == null) return propertySet; Element root = requestDocument.getDocumentElement(); if (!DomUtil.matches(root, ELEMENT_CALDAV_MKCALENDAR, NAMESPACE_CALDAV)) throw new BadRequestException("Expected " + QN_MKCALENDAR + " root element"); Element set = DomUtil.getChildElement(root, XML_SET, NAMESPACE); if (set == null) throw new BadRequestException("Expected " + QN_SET + " child of " + QN_MKCALENDAR); Element prop = DomUtil.getChildElement(set, XML_PROP, NAMESPACE); if (prop == null) throw new BadRequestException("Expected " + QN_PROP + " child of " + QN_SET); ElementIterator i = DomUtil.getChildren(prop); while (i.hasNext()) propertySet.add(StandardDavProperty.createFromXml(i.nextElement())); return propertySet; } private Ticket parseTicketRequest() throws DavException { Document requestDocument = getSafeRequestDocument(); if (requestDocument == null) throw new BadRequestException("MKTICKET requires entity body"); Element root = requestDocument.getDocumentElement(); if (!DomUtil.matches(root, ELEMENT_TICKET_TICKETINFO, NAMESPACE_TICKET)) throw new BadRequestException("Expected " + QN_TICKET_TICKETINFO + " root element"); if (DomUtil.hasChildElement(root, ELEMENT_TICKET_ID, NAMESPACE_TICKET)) throw new BadRequestException(QN_TICKET_TICKETINFO + " may not contain child " + QN_TICKET_ID); if (DomUtil.hasChildElement(root, XML_OWNER, NAMESPACE)) throw new BadRequestException(QN_TICKET_TICKETINFO + " may not contain child " + QN_OWNER); String timeout = DomUtil.getChildTextTrim(root, ELEMENT_TICKET_TIMEOUT, NAMESPACE_TICKET); if (timeout != null && !timeout.equals(TIMEOUT_INFINITE)) { try { int seconds = Integer.parseInt(timeout.substring(7)); log.trace("Timeout seconds: " + seconds); } catch (NumberFormatException e) { throw new BadRequestException("Malformed " + QN_TICKET_TIMEOUT + " value " + timeout); } } else timeout = TIMEOUT_INFINITE; // visit limits are not supported Element pe = DomUtil.getChildElement(root, XML_PRIVILEGE, NAMESPACE); if (pe == null) throw new BadRequestException("Expected " + QN_PRIVILEGE + " child of " + QN_TICKET_TICKETINFO); DavPrivilegeSet privileges = DavPrivilegeSet.createFromXml(pe); if (!privileges.containsAny(DavPrivilege.READ, DavPrivilege.WRITE, DavPrivilege.READ_FREE_BUSY)) throw new BadRequestException("Empty or invalid " + QN_PRIVILEGE); Ticket ticket = entityFactory.creatTicket(); ticket.setTimeout(timeout); privileges.setTicketPrivileges(ticket); return ticket; } private ReportInfo parseReportRequest() throws DavException { Document requestDocument = getSafeRequestDocument(); if (requestDocument == null) //reports with no bodies are supported for collections return null; try { return new ReportInfo(requestDocument.getDocumentElement(), getDepth(DEPTH_0)); } catch (org.apache.jackrabbit.webdav.DavException e) { throw new DavException(e); } catch (IllegalArgumentException e) { throw new BadRequestException(e.getMessage()); } } @Override public ServletInputStream getInputStream() throws IOException { if (!bufferRequestContent) return super.getInputStream(); BufferedServletInputStream is = new BufferedServletInputStream(super.getInputStream()); bufferedContentLength = is.getLength(); long contentLength = getContentLength(); if (contentLength != -1 && contentLength != bufferedContentLength) throw new IOException("Read only " + bufferedContentLength + " of " + contentLength + " bytes"); return is; } public boolean isRequestContentBuffered() { return bufferRequestContent; } public long getBufferedContentLength() { return bufferedContentLength; } }