io.milton.http.caldav.ExpandPropertyReport Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of milton-server-ent Show documentation
Show all versions of milton-server-ent Show documentation
Milton Enterprise: Supports DAV level 2 and above, including Caldav and Carddav. Available on AGPL or
commercial licenses
/*
* Copyright 2012 McEvoy Software Ltd.
*/
package io.milton.http.caldav;
import io.milton.http.ResourceFactory;
import io.milton.http.exceptions.BadRequestException;
import io.milton.http.exceptions.NotAuthorizedException;
import io.milton.http.report.Report;
import io.milton.http.values.HrefList;
import io.milton.http.values.PropFindResponseList;
import io.milton.http.values.ValueAndType;
import io.milton.http.webdav.PropFindPropertyBuilder;
import io.milton.http.webdav.PropFindResponse;
import io.milton.http.webdav.PropFindXmlGenerator;
import io.milton.http.webdav.PropertiesRequest;
import io.milton.http.webdav.PropertiesRequest.Property;
import io.milton.http.webdav.WebDavProtocol;
import io.milton.resource.PropFindableResource;
import io.milton.resource.Resource;
import java.net.URISyntaxException;
import java.util.HashSet;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import javax.xml.namespace.QName;
import org.jdom2.Document;
import org.jdom2.Element;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* http://tools.ietf.org/html/rfc3253#section-3.8
*
* Many property values are defined as a DAV:href, or a set of DAV:href
elements. The DAV:expand-property report provides a mechanism for
retrieving in one request the properties from the resources
identified by those DAV:href elements. This report not only
decreases the number of requests required, but also allows the server
to minimize the number of separate read transactions required on the
underlying versioning store.
The DAV:expand-property report SHOULD be supported by all resources
that support the REPORT method.
Marshalling:
The request body MUST be a DAV:expand-property XML element.
name value: a property element type
namespace value: an XML namespace
The response body for a successful request MUST be a
DAV:multistatus XML element.
multistatus: see RFC 2518, Section 12.9
The properties reported in the DAV:prop elements of the
DAV:multistatus element MUST be those identified by the
DAV:property elements in the DAV:expand-property element. If
there are DAV:property elements nested within a DAV:property
element, then every DAV:href in the value of the corresponding
property is replaced by a DAV:response element whose DAV:prop
elements report the values of the properties identified by the
nested DAV:property elements. The nested DAV:property elements
can in turn contain DAV:property elements, so that multiple levels
of DAV:href expansion can be requested.
Note that a validating parser MUST be aware that the DAV:expand-
property report effectively modifies the DTD of every property by
replacing every occurrence of "href" in the DTD with "href |
response".
*
* REPORT /foo.html HTTP/1.1
Host: www.webdav.org
Content-Type: text/xml; charset="utf-8"
Content-Length: xxxx
>>RESPONSE
HTTP/1.1 207 Multi-Status
Content-Type: text/xml; charset="utf-8"
Content-Length: xxxx
http://www.webdav.org/foo.html
http://repo.webdav.org/his/23
http://repo.webdav.org/his/23/ver/1
Fred
http://www.webdav.org/ws/dev/sally
HTTP/1.1 200 OK
http://repo.webdav.org/his/23/ver/2
Sally
http://repo.webdav.org/act/add-refresh-cmd
HTTP/1.1 200 OK
HTTP/1.1 200 OK
HTTP/1.1 200 OK
In this example, the DAV:creator-displayname and DAV:activity-set
properties of the versions in the DAV:version-set of the
DAV:version-history of http://www.webdav.org/foo.html are reported.
*
* @author bradm
*/
public class ExpandPropertyReport implements Report {
private static final Logger log = LoggerFactory.getLogger(MultiGetReport.class);
private final ResourceFactory resourceFactory;
private final PropFindPropertyBuilder propertyBuilder;
private final PropFindXmlGenerator xmlGenerator;
public ExpandPropertyReport(ResourceFactory resourceFactory, PropFindPropertyBuilder propertyBuilder, PropFindXmlGenerator xmlGenerator) {
this.resourceFactory = resourceFactory;
this.propertyBuilder = propertyBuilder;
this.xmlGenerator = xmlGenerator;
}
@Override
public String process(String host, String path, Resource calendar, Document doc) throws NotAuthorizedException, BadRequestException {
log.debug("process");
PropertiesRequest parseResult = parse(doc.getRootElement());
List propFindResponses;
try {
PropFindableResource pfr = (PropFindableResource) calendar;
propFindResponses = propertyBuilder.buildProperties(pfr, 1, parseResult, path);
for (PropFindResponse r : propFindResponses) {
Set> set = r.getKnownProperties().entrySet();
set = new HashSet<>(set);
for (Entry p : set) {
Object val = p.getValue().getValue();
QName name = p.getKey();
if (val instanceof HrefList) {
HrefList hrefList = (HrefList) val;
Property prop = parseResult.get(name);
PropFindResponseList propFindResponseList = toResponseList(host, hrefList, prop);
replaceHrefs(host, propFindResponseList, prop);
r.getKnownProperties().remove(name);
r.getKnownProperties().put(name, new ValueAndType(propFindResponseList, PropFindResponseList.class));
}
}
}
} catch (URISyntaxException ex) {
throw new RuntimeException("Exception parsing url, indicating the requested URL is not correctly encoded. Please check the client application.", ex);
}
//show("",propFindResponses);
return xmlGenerator.generate(propFindResponses);
}
public PropertiesRequest parse(Element elProp) {
Set set = new HashSet<>();
for (Object o : elProp.getChildren()) {
if (o instanceof Element) {
Element el = (Element) o;
if (el.getName().equals("property")) {
QName name = getQName(el);
Set nested = parseChildren(el);
Property p = new Property(name, nested);
set.add(p);
}
}
}
return new PropertiesRequest(set);
}
private Set parseChildren(Element elProp) {
Set set = new HashSet<>();
for (Object o : elProp.getChildren()) {
if (o instanceof Element) {
Element el = (Element) o;
if (el.getName().equals("property")) {
QName name = getQName(el);
Set nested = parseChildren(el);
Property p = new Property(name, nested);
set.add(p);
}
}
}
return set;
}
private QName getQName(Element el) {
String local = el.getAttributeValue("name");
String ns = el.getAttributeValue("namespace");
if (ns == null) {
ns = WebDavProtocol.DAV_URI;
}
return new QName(ns, local);
}
@Override
public String getName() {
return "expand-property";
}
private PropFindResponseList toResponseList(String host, HrefList hrefList, Property prop) throws URISyntaxException, NotAuthorizedException, BadRequestException {
PropFindResponseList list = new PropFindResponseList();
for (String href : hrefList) {
Resource r = resourceFactory.getResource(host, href);
if (r != null) {
if (r instanceof PropFindableResource) {
PropFindableResource pfr = (PropFindableResource) r;
PropertiesRequest propertyRequest = new PropertiesRequest(prop.getNested());
List propFindResponses = propertyBuilder.buildProperties(pfr, 0, propertyRequest, href);
// should be only one
list.addAll(propFindResponses);
}
}
}
return list;
}
private void replaceHrefs(String host, PropFindResponseList propFindResponseList, Property prop) throws URISyntaxException, NotAuthorizedException, BadRequestException {
for (PropFindResponse r : propFindResponseList) {
Set> set = r.getKnownProperties().entrySet();
set = new HashSet<>(set);
for (Entry p : set) {
Object val = p.getValue().getValue();
QName name = p.getKey();
if (val instanceof HrefList) {
HrefList hrefList = (HrefList) val;
Property nestedProp = prop.getNestedMap().get(name);
// Check for another level of nesting
if (nestedProp != null && nestedProp.getNested() != null && !nestedProp.getNested().isEmpty())
{
PropFindResponseList nestedList = toResponseList(host, hrefList, nestedProp);
replaceHrefs(host, nestedList, nestedProp);
r.getKnownProperties().remove(name);
r.getKnownProperties().put(name, new ValueAndType(nestedList, PropFindResponseList.class));
}
}
}
}
}
private void show(String prefix, List propFindResponses) {
for( PropFindResponse p : propFindResponses ) {
for( Entry e : p.getKnownProperties().entrySet()) {
Object o = e.getValue().getValue();
if( o instanceof PropFindResponseList) {
PropFindResponseList childList = (PropFindResponseList) o;
show(prefix + " ", childList);
} else {
}
}
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy