@Override
public boolean exists(String path) {
+ if (!path.startsWith(ContentUtils.ROOT_SLASH))
+ throw new IllegalArgumentException(path + " is not an absolute path");
ContentProvider contentProvider = contentRepository.getMountManager().findContentProvider(path);
String mountPath = contentProvider.getMountPath();
String relativePath = extractRelativePath(mountPath, path);
//
XLINK_1999("xlink", "http://www.w3.org/1999/xlink", "xlink.xsd", "http://www.w3.org/XML/2008/06/xlink.xsd"),
//
-// WEBDAV("dav", "DAV:", "webdav.xsd", "https://raw.githubusercontent.com/lookfirst/sardine/master/webdav.xsd"),
+ WEBDAV("D", "DAV:", null, "https://raw.githubusercontent.com/lookfirst/sardine/master/webdav.xsd"),
//
XSLT_2_0("xsl", "http://www.w3.org/1999/XSL/Transform", "schema-for-xslt20.xsd",
"https://www.w3.org/2007/schema-for-xslt20.xsd"),
import org.argeo.api.acr.spi.ProvidedSession;
import org.argeo.cms.acr.AbstractContent;
import org.argeo.cms.acr.ContentUtils;
-import org.argeo.util.dav.DavResponse;
+import org.argeo.cms.dav.DavResponse;
public class DavContent extends AbstractContent {
private final DavContentProvider provider;
import org.argeo.api.acr.spi.ContentProvider;
import org.argeo.api.acr.spi.ProvidedContent;
import org.argeo.api.acr.spi.ProvidedSession;
-import org.argeo.util.dav.DavClient;
-import org.argeo.util.dav.DavResponse;
+import org.argeo.cms.dav.DavClient;
+import org.argeo.cms.dav.DavResponse;
public class DavContentProvider implements ContentProvider {
private String mountPath;
import java.util.StringJoiner;
import java.util.concurrent.CompletableFuture;
+import javax.xml.XMLConstants;
import javax.xml.namespace.QName;
import javax.xml.transform.Source;
import javax.xml.transform.TransformerException;
try {
for (String name : udfav.list()) {
QName providerName = NamespaceUtils.parsePrefixedName(provider, name);
+ if (providerName.getNamespaceURI().equals(XMLConstants.XMLNS_ATTRIBUTE_NS_URI))
+ continue; // skip prefix mapping
QName sessionName = new ContentName(providerName, getSession());
result.add(sessionName);
}
for (int i = 0; i < attributes.getLength(); i++) {
Attr attr = (Attr) attributes.item(i);
QName key = toQName(attr);
+ if (key.getNamespaceURI().equals(XMLConstants.XMLNS_ATTRIBUTE_NS_URI))
+ continue;// skip prefix mapping
result.add(key);
}
return result;
.startsWith(HttpHeader.NEGOTIATE)) {
negotiateFailed = true;
} else {
- return HttpResponseStatus.FORBIDDEN.getStatusCode();
+ return HttpResponseStatus.FORBIDDEN.getCode();
}
}
// response.setHeader("Keep-Alive", "timeout=5, max=97");
// response.setContentType("text/html; charset=UTF-8");
- return HttpResponseStatus.UNAUTHORIZED.getStatusCode();
+ return HttpResponseStatus.UNAUTHORIZED.getCode();
}
private static boolean hasAcceptorCredentials() {
--- /dev/null
+package org.argeo.cms.dav;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.Authenticator;
+import java.net.PasswordAuthentication;
+import java.net.URI;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpRequest.BodyPublishers;
+import java.net.http.HttpResponse;
+import java.net.http.HttpResponse.BodyHandler;
+import java.net.http.HttpResponse.BodyHandlers;
+import java.util.Iterator;
+
+import javax.xml.namespace.QName;
+
+import org.argeo.util.http.HttpResponseStatus;
+
+public class DavClient {
+
+ private HttpClient httpClient;
+
+ public DavClient() {
+ httpClient = HttpClient.newBuilder() //
+// .sslContext(insecureContext()) //
+ .version(HttpClient.Version.HTTP_1_1) //
+// .authenticator(new Authenticator() {
+//
+// @Override
+// protected PasswordAuthentication getPasswordAuthentication() {
+// return new PasswordAuthentication("root", "demo".toCharArray());
+// }
+//
+// }) //
+ .build();
+ }
+
+ public void setProperty(String url, QName key, String value) {
+ try {
+ String body = """
+ <?xml version="1.0" encoding="utf-8" ?>
+ <D:propertyupdate xmlns:D="DAV:"
+ """ //
+ + "xmlns:" + key.getPrefix() + "=\"" + key.getNamespaceURI() + "\">" + //
+ """
+ <D:set>
+ <D:prop>
+ """ //
+ + "<" + key.getPrefix() + ":" + key.getLocalPart() + ">" + value + "</" + key.getPrefix() + ":"
+ + key.getLocalPart() + ">" + //
+ """
+ </D:prop>
+ </D:set>
+ </D:propertyupdate>
+ """;
+ System.out.println(body);
+ HttpRequest request = HttpRequest.newBuilder().uri(URI.create(url)) //
+ .header("Depth", "1") //
+ .method(DavMethod.PROPPATCH.name(), BodyPublishers.ofString(body)) //
+ .build();
+ BodyHandler<String> bodyHandler = BodyHandlers.ofString();
+ HttpResponse<String> response = httpClient.send(request, bodyHandler);
+ System.out.println(response.body());
+ } catch (IOException | InterruptedException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+ public Iterator<DavResponse> listChildren(URI uri) {
+ try {
+ String body = """
+ <?xml version="1.0" encoding="utf-8" ?>
+ <D:propfind xmlns:D="DAV:">
+ <D:propname/>
+ </D:propfind>""";
+ HttpRequest request = HttpRequest.newBuilder().uri(uri) //
+ .header(DavHeader.DEPTH.getHeaderName(), DavDepth.DEPTH_1.getValue()) //
+ .method(DavMethod.PROPFIND.name(), BodyPublishers.ofString(body)) //
+ .build();
+
+ HttpResponse<String> responseStr = httpClient.send(request, BodyHandlers.ofString());
+ System.out.println(responseStr.body());
+
+ HttpResponse<InputStream> response = httpClient.send(request, BodyHandlers.ofInputStream());
+ MultiStatusReader msReader = new MultiStatusReader(response.body(), uri.getPath());
+ return msReader;
+ } catch (IOException | InterruptedException e) {
+ throw new IllegalStateException("Cannot list children of " + uri, e);
+ }
+
+ }
+
+ public boolean exists(URI uri) {
+ try {
+ HttpRequest request = HttpRequest.newBuilder().uri(uri) //
+ .header(DavHeader.DEPTH.getHeaderName(), DavDepth.DEPTH_0.getValue()) //
+ .method(DavMethod.HEAD.name(), BodyPublishers.noBody()) //
+ .build();
+ BodyHandler<String> bodyHandler = BodyHandlers.ofString();
+ HttpResponse<String> response = httpClient.send(request, bodyHandler);
+ System.out.println(response.body());
+ int responseStatusCode = response.statusCode();
+ if (responseStatusCode == HttpResponseStatus.NOT_FOUND.getCode())
+ return false;
+ if (responseStatusCode >= 200 && responseStatusCode < 300)
+ return true;
+ throw new IllegalStateException(
+ "Cannot check whether " + uri + " exists: Unknown response status code " + responseStatusCode);
+ } catch (IOException | InterruptedException e) {
+ throw new IllegalStateException("Cannot check whether " + uri + " exists", e);
+ }
+
+ }
+
+ public DavResponse get(URI uri) {
+ try {
+ String body = """
+ <?xml version="1.0" encoding="utf-8" ?>
+ <D:propfind xmlns:D="DAV:">
+ <D:allprop/>
+ </D:propfind>""";
+ HttpRequest request = HttpRequest.newBuilder().uri(uri) //
+ .header(DavHeader.DEPTH.getHeaderName(), DavDepth.DEPTH_0.getValue()) //
+ .method(DavMethod.PROPFIND.name(), BodyPublishers.ofString(body)) //
+ .build();
+
+// HttpResponse<String> responseStr = httpClient.send(request, BodyHandlers.ofString());
+// System.out.println(responseStr.body());
+
+ HttpResponse<InputStream> response = httpClient.send(request, BodyHandlers.ofInputStream());
+ MultiStatusReader msReader = new MultiStatusReader(response.body());
+ if (!msReader.hasNext())
+ throw new IllegalArgumentException(uri + " does not exist");
+ return msReader.next();
+ } catch (IOException | InterruptedException e) {
+ throw new IllegalStateException("Cannot list children of " + uri, e);
+ }
+
+ }
+
+ public static void main(String[] args) {
+ DavClient davClient = new DavClient();
+// Iterator<DavResponse> responses = davClient
+// .listChildren(URI.create("http://localhost/unstable/a2/org.argeo.tp.sdk/"));
+ Iterator<DavResponse> responses = davClient
+ .listChildren(URI.create("http://root:demo@localhost:7070/api/acr/srv/example"));
+ while (responses.hasNext()) {
+ DavResponse response = responses.next();
+ System.out.println(response.getHref() + (response.isCollection() ? " (collection)" : ""));
+ System.out.println(" " + response.getPropertyNames());
+
+ }
+// davClient.setProperty("http://localhost/unstable/a2/org.argeo.tp.sdk/org.opentest4j.1.2.jar",
+// CrName.uuid.qName(), UUID.randomUUID().toString());
+
+ }
+
+}
--- /dev/null
+package org.argeo.cms.dav;
+
+import com.sun.net.httpserver.HttpExchange;
+
+public enum DavDepth {
+ DEPTH_0("0"), DEPTH_1("1"), DEPTH_INFINITY("infinity");
+
+ private final String value;
+
+ private DavDepth(String value) {
+ this.value = value;
+ }
+
+ @Override
+ public String toString() {
+ return getValue();
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public static DavDepth fromHttpExchange(HttpExchange httpExchange) {
+ String value = httpExchange.getRequestHeaders().getFirst(DavHeader.DEPTH.getHeaderName());
+ if (value == null)
+ return null;
+ DavDepth depth = switch (value) {
+ case "0" -> DEPTH_0;
+ case "1" -> DEPTH_1;
+ case "infinity" -> DEPTH_INFINITY;
+ default -> throw new IllegalArgumentException("Unexpected value: " + value);
+ };
+ return depth;
+ }
+}
--- /dev/null
+package org.argeo.cms.dav;
+
+/** Standard HTTP headers. */
+public enum DavHeader {
+ DEPTH("Depth"), //
+ ;
+
+ private final String name;
+
+ private DavHeader(String headerName) {
+ this.name = headerName;
+ }
+
+ public String getHeaderName() {
+ return name;
+ }
+
+ @Override
+ public String toString() {
+ return getHeaderName();
+ }
+
+}
--- /dev/null
+package org.argeo.cms.dav;
+
+public enum DavMethod {
+ // Generic HTTP
+ HEAD, //
+ // WebDav specific
+ PROPFIND, //
+ PROPPATCH, //
+ ;
+}
--- /dev/null
+package org.argeo.cms.dav;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.xml.namespace.QName;
+import javax.xml.stream.FactoryConfigurationError;
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamReader;
+
+public class DavPropfind {
+ private DavDepth depth;
+ private boolean propname = false;
+ private boolean allprop = false;
+ private List<QName> props = new ArrayList<>();
+
+ public DavPropfind(DavDepth depth) {
+ this.depth = depth;
+ }
+
+ public boolean isPropname() {
+ return propname;
+ }
+
+ public void setPropname(boolean propname) {
+ this.propname = propname;
+ }
+
+ public boolean isAllprop() {
+ return allprop;
+ }
+
+ public void setAllprop(boolean allprop) {
+ this.allprop = allprop;
+ }
+
+ public List<QName> getProps() {
+ return props;
+ }
+
+ public DavDepth getDepth() {
+ return depth;
+ }
+
+ public static DavPropfind load(DavDepth depth, InputStream in) throws IOException {
+ try {
+ DavPropfind res = null;
+ XMLInputFactory inputFactory = XMLInputFactory.newFactory();
+ XMLStreamReader reader = inputFactory.createXMLStreamReader(in);
+ while (reader.hasNext()) {
+ reader.next();
+ if (reader.isStartElement()) {
+ QName name = reader.getName();
+// System.out.println(name);
+ DavXmlElement davXmlElement = DavXmlElement.toEnum(name);
+ if (davXmlElement != null) {
+ switch (davXmlElement) {
+ case propfind:
+ res = new DavPropfind(depth);
+ break;
+ case allprop:
+ res.setAllprop(true);
+ break;
+ case propname:
+ res.setPropname(true);
+ case prop:
+ // ignore
+ case include:
+ // ignore
+ break;
+ default:
+ // TODO check that the format is really respected
+ res.getProps().add(reader.getName());
+ }
+ }
+ }
+ }
+
+ // checks
+ if (res.isPropname()) {
+ if (!res.getProps().isEmpty() || res.isAllprop())
+ throw new IllegalArgumentException("Cannot set other values if propname is set");
+ }
+ return res;
+ } catch (FactoryConfigurationError | XMLStreamException e) {
+ throw new RuntimeException("Cannot load propfind", e);
+ }
+ }
+}
--- /dev/null
+package org.argeo.cms.dav;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.xml.namespace.QName;
+
+public class DavResponse {
+ final static String MOD_DAV_NAMESPACE = "http://apache.org/dav/props/";
+
+ private String href;
+ private boolean collection;
+ private Set<QName> propertyNames = new HashSet<>();
+ private Map<QName, String> properties = new HashMap<>();
+ private List<QName> resourceTypes = new ArrayList<>();
+
+ public Map<QName, String> getProperties() {
+ return properties;
+ }
+
+ public void setHref(String href) {
+ this.href = href;
+ }
+
+ public String getHref() {
+ return href;
+ }
+
+ public boolean isCollection() {
+ return collection;
+ }
+
+ public void setCollection(boolean collection) {
+ this.collection = collection;
+ }
+
+ public List<QName> getResourceTypes() {
+ return resourceTypes;
+ }
+
+ public Set<QName> getPropertyNames() {
+ return propertyNames;
+ }
+
+}
--- /dev/null
+package org.argeo.cms.dav;
+
+import javax.xml.namespace.QName;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+
+import org.argeo.api.acr.QNamed;
+
+public enum DavXmlElement implements QNamed {
+ response, //
+ multistatus, //
+ href, //
+ collection, //
+ prop, //
+ resourcetype, //
+
+ // propfind
+ propfind, //
+ allprop, //
+ propname, //
+ include, //
+ propstat, //
+
+ // locking
+ lockscope, //
+ locktype, //
+ supportedlock, //
+ lockentry, //
+ lockdiscovery, //
+ write, //
+ shared, //
+ exclusive, //
+ ;
+
+ final static String WEBDAV_NAMESPACE_URI = "DAV:";
+ final static String WEBDAV_DEFAULT_PREFIX = "D";
+
+// private final QName value;
+//
+// private DavXmlElement() {
+// this.value = new ContentName(getNamespace(), localName(), RuntimeNamespaceContext.getNamespaceContext());
+// }
+//
+// @Override
+// public QName qName() {
+// return value;
+// }
+
+ @Override
+ public String getNamespace() {
+ return WEBDAV_NAMESPACE_URI;
+ }
+
+ @Override
+ public String getDefaultPrefix() {
+ return WEBDAV_DEFAULT_PREFIX;
+ }
+
+ public static DavXmlElement toEnum(QName name) {
+ for (DavXmlElement e : values()) {
+ if (e.qName().equals(name))
+ return e;
+ }
+ return null;
+ }
+
+ public void setSimpleValue(XMLStreamWriter xsWriter, String value) throws XMLStreamException {
+ if (value == null) {
+ emptyElement(xsWriter);
+ return;
+ }
+ startElement(xsWriter);
+ xsWriter.writeCData(value);
+ xsWriter.writeEndElement();
+ }
+
+ public void emptyElement(XMLStreamWriter xsWriter) throws XMLStreamException {
+ xsWriter.writeEmptyElement(WEBDAV_NAMESPACE_URI, name());
+ }
+
+ public void startElement(XMLStreamWriter xsWriter) throws XMLStreamException {
+ xsWriter.writeStartElement(WEBDAV_NAMESPACE_URI, name());
+ }
+}
--- /dev/null
+package org.argeo.cms.dav;
+
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.Iterator;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ForkJoinPool;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import javax.xml.namespace.QName;
+import javax.xml.stream.FactoryConfigurationError;
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLStreamConstants;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamReader;
+
+/**
+ * Asynchronously iterate over the response statuses of the response to a
+ * PROPFIND request.
+ */
+class MultiStatusReader implements Iterator<DavResponse> {
+ private CompletableFuture<Boolean> empty = new CompletableFuture<Boolean>();
+ private AtomicBoolean processed = new AtomicBoolean(false);
+
+ private BlockingQueue<DavResponse> queue = new ArrayBlockingQueue<>(64);
+
+ private final String ignoredHref;
+
+ public MultiStatusReader(InputStream in) {
+ this(in, null);
+ }
+
+ /** Typically ignoring self */
+ public MultiStatusReader(InputStream in, String ignoredHref) {
+ this.ignoredHref = ignoredHref;
+ ForkJoinPool.commonPool().execute(() -> process(in));
+ }
+
+ protected void process(InputStream in) {
+ try {
+ XMLInputFactory inputFactory = XMLInputFactory.newFactory();
+ XMLStreamReader reader = inputFactory.createXMLStreamReader(in, StandardCharsets.UTF_8.name());
+
+ DavResponse currentResponse = null;
+ boolean collectiongProperties = false;
+
+ final QName COLLECTION = DavXmlElement.collection.qName(); // optimisation
+ elements: while (reader.hasNext()) {
+ reader.next();
+ if (reader.isStartElement()) {
+ QName name = reader.getName();
+// System.out.println(name);
+ DavXmlElement davXmlElement = DavXmlElement.toEnum(name);
+ if (davXmlElement != null) {
+ switch (davXmlElement) {
+ case response:
+ currentResponse = new DavResponse();
+ break;
+ case href:
+ assert currentResponse != null;
+ while (reader.hasNext() && !reader.hasText())
+ reader.next();
+ String href = reader.getText();
+ currentResponse.setHref(href);
+ break;
+// case collection:
+// currentResponse.setCollection(true);
+// break;
+ case prop:
+ collectiongProperties = true;
+ break;
+ case resourcetype:
+ while (reader.hasNext()) {
+ int event = reader.nextTag();
+ QName resourceType = reader.getName();
+ if (event == XMLStreamConstants.END_ELEMENT && name.equals(resourceType))
+ break;
+ assert currentResponse != null;
+ if (event == XMLStreamConstants.START_ELEMENT) {
+ if (COLLECTION.equals(resourceType))
+ currentResponse.setCollection(true);
+ else
+ currentResponse.getResourceTypes().add(resourceType);
+ }
+ }
+ break;
+ default:
+ // ignore
+ }
+ } else {
+ if (collectiongProperties) {
+ String value = null;
+ // TODO deal with complex properties
+ readProperty: while (reader.hasNext()) {
+ reader.next();
+ if (reader.getEventType() == XMLStreamConstants.END_ELEMENT)
+ break readProperty;
+ if (reader.getEventType() == XMLStreamConstants.CHARACTERS)
+ value = reader.getText();
+ }
+
+ if (name.getNamespaceURI().equals(DavResponse.MOD_DAV_NAMESPACE))
+ continue elements; // skip mod_dav properties
+
+ assert currentResponse != null;
+ currentResponse.getPropertyNames().add(name);
+ if (value != null)
+ currentResponse.getProperties().put(name, value);
+
+ }
+ }
+ } else if (reader.isEndElement()) {
+ QName name = reader.getName();
+// System.out.println(name);
+ DavXmlElement davXmlElement = DavXmlElement.toEnum(name);
+ if (davXmlElement != null)
+ switch (davXmlElement) {
+ case response:
+ assert currentResponse != null;
+ if (ignoredHref == null || !ignoredHref.equals(currentResponse.getHref())) {
+ if (!empty.isDone())
+ empty.complete(false);
+ publish(currentResponse);
+ }
+ case prop:
+ collectiongProperties = false;
+ break;
+ default:
+ // ignore
+ }
+ }
+ }
+
+ if (!empty.isDone())
+ empty.complete(true);
+ } catch (FactoryConfigurationError | XMLStreamException e) {
+ empty.completeExceptionally(e);
+ throw new IllegalStateException("Cannot process DAV response", e);
+ } finally {
+ processed();
+ }
+ }
+
+ protected synchronized void publish(DavResponse response) {
+ try {
+ queue.put(response);
+ } catch (InterruptedException e) {
+ throw new IllegalStateException("Cannot put response " + response, e);
+ } finally {
+ notifyAll();
+ }
+ }
+
+ protected synchronized void processed() {
+ processed.set(true);
+ notifyAll();
+ }
+
+ @Override
+ public synchronized boolean hasNext() {
+ try {
+ if (empty.get())
+ return false;
+ while (!processed.get() && queue.isEmpty()) {
+ wait();
+ }
+ if (!queue.isEmpty())
+ return true;
+ if (processed.get())
+ return false;
+ throw new IllegalStateException("Cannot determine hasNext");
+ } catch (InterruptedException | ExecutionException e) {
+ throw new IllegalStateException("Cannot determine hasNext", e);
+ } finally {
+ // notifyAll();
+ }
+ }
+
+ @Override
+ public synchronized DavResponse next() {
+ try {
+ if (!hasNext())
+ throw new IllegalStateException("No fursther items are available");
+
+ DavResponse response = queue.take();
+ return response;
+ } catch (InterruptedException e) {
+ throw new IllegalStateException("Cannot get next", e);
+ } finally {
+ // notifyAll();
+ }
+ }
+
+}
--- /dev/null
+package org.argeo.cms.dav;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.CompletionStage;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Consumer;
+
+import javax.xml.XMLConstants;
+import javax.xml.namespace.NamespaceContext;
+import javax.xml.namespace.QName;
+import javax.xml.stream.FactoryConfigurationError;
+import javax.xml.stream.XMLOutputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+
+public class MultiStatusWriter implements Consumer<DavResponse> {
+ private BlockingQueue<DavResponse> queue = new ArrayBlockingQueue<>(64);
+
+// private OutputStream out;
+
+ private Thread processingThread;
+
+ private AtomicBoolean done = new AtomicBoolean(false);
+
+ private AtomicBoolean polling = new AtomicBoolean();
+
+ public void process(NamespaceContext namespaceContext, OutputStream out, CompletionStage<Void> published,
+ boolean propname) throws IOException {
+ published.thenRun(() -> allPublished());
+ processingThread = Thread.currentThread();
+// this.out = out;
+
+ try {
+ XMLOutputFactory xmlOutputFactory = XMLOutputFactory.newFactory();
+ XMLStreamWriter xsWriter = xmlOutputFactory.createXMLStreamWriter(out, StandardCharsets.UTF_8.name());
+ xsWriter.setNamespaceContext(namespaceContext);
+ xsWriter.setDefaultNamespace(DavXmlElement.WEBDAV_NAMESPACE_URI);
+
+ xsWriter.writeStartDocument();
+ DavXmlElement.multistatus.startElement(xsWriter);
+ xsWriter.writeDefaultNamespace(DavXmlElement.WEBDAV_NAMESPACE_URI);
+
+ poll: while (!(done.get() && queue.isEmpty())) {
+ DavResponse davResponse;
+ try {
+ polling.set(true);
+ davResponse = queue.poll(10, TimeUnit.MILLISECONDS);
+ if (davResponse == null)
+ continue poll;
+ System.err.println(davResponse.getHref());
+ } catch (InterruptedException e) {
+ System.err.println(e);
+ continue poll;
+ } finally {
+ polling.set(false);
+ }
+
+ writeDavResponse(xsWriter, davResponse, propname);
+ }
+
+ xsWriter.writeEndElement();// multistatus
+ xsWriter.writeEndDocument();
+ xsWriter.close();
+ out.close();
+ } catch (FactoryConfigurationError | XMLStreamException e) {
+ synchronized (this) {
+ processingThread = null;
+ }
+ }
+ }
+
+ protected void writeDavResponse(XMLStreamWriter xsWriter, DavResponse davResponse, boolean propname)
+ throws XMLStreamException {
+ Set<String> namespaces = new HashSet<>();
+ for (QName key : davResponse.getPropertyNames()) {
+ if (key.getNamespaceURI().equals(DavXmlElement.WEBDAV_NAMESPACE_URI))
+ continue; // skip
+ if (key.getNamespaceURI().equals(XMLConstants.W3C_XML_SCHEMA_NS_URI))
+ continue; // skip
+ namespaces.add(key.getNamespaceURI());
+ }
+ DavXmlElement.response.startElement(xsWriter);
+ // namespaces
+ for (String ns : namespaces)
+ xsWriter.writeNamespace(xsWriter.getNamespaceContext().getPrefix(ns), ns);
+
+ DavXmlElement.href.setSimpleValue(xsWriter, davResponse.getHref());
+
+ {
+ DavXmlElement.propstat.startElement(xsWriter);
+ {
+ DavXmlElement.prop.startElement(xsWriter);
+ if (!davResponse.getResourceTypes().isEmpty() || davResponse.isCollection()) {
+ DavXmlElement.resourcetype.startElement(xsWriter);
+ if (davResponse.isCollection())
+ DavXmlElement.collection.emptyElement(xsWriter);
+ for (QName resourceType : davResponse.getResourceTypes()) {
+ xsWriter.writeEmptyElement(resourceType.getNamespaceURI(), resourceType.getLocalPart());
+ }
+ xsWriter.writeEndElement();// resource type
+ }
+ for (QName key : davResponse.getPropertyNames()) {
+ if (propname) {
+ xsWriter.writeEmptyElement(key.getNamespaceURI(), key.getLocalPart());
+ } else {
+ xsWriter.writeStartElement(key.getNamespaceURI(), key.getLocalPart());
+ xsWriter.writeCData(davResponse.getProperties().get(key));
+ xsWriter.writeEndElement();
+ }
+ }
+ xsWriter.writeEndElement();// prop
+ }
+ xsWriter.writeEndElement();// propstat
+ }
+ xsWriter.writeEndElement();// response
+ }
+
+ @Override
+ public void accept(DavResponse davResponse) {
+ try {
+ queue.put(davResponse);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+
+ protected synchronized void allPublished() {
+ done.set(true);
+ if (processingThread != null && queue.isEmpty() && polling.get()) {
+ // we only interrupt if the queue is already processed
+ // so as not to interrupt I/O
+ processingThread.interrupt();
+ }
+ }
+
+}
import java.io.IOException;
import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Collection;
+import java.util.Map;
import java.util.Optional;
+import java.util.StringJoiner;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ForkJoinPool;
+import java.util.function.Consumer;
+
+import javax.xml.namespace.QName;
import org.argeo.api.acr.Content;
import org.argeo.api.acr.ContentSession;
import org.argeo.api.acr.CrName;
import org.argeo.api.acr.spi.ProvidedRepository;
+import org.argeo.api.cms.CmsConstants;
import org.argeo.cms.acr.ContentUtils;
import org.argeo.cms.auth.RemoteAuthUtils;
+import org.argeo.cms.dav.DavDepth;
+import org.argeo.cms.dav.DavMethod;
+import org.argeo.cms.dav.DavPropfind;
+import org.argeo.cms.dav.DavResponse;
+import org.argeo.cms.dav.DavXmlElement;
+import org.argeo.cms.dav.MultiStatusWriter;
import org.argeo.cms.internal.http.RemoteAuthHttpExchange;
import org.argeo.util.StreamUtils;
-import org.argeo.util.dav.DavServerHandler;
+import org.argeo.util.http.HttpMethod;
import org.argeo.util.http.HttpResponseStatus;
import org.argeo.util.http.HttpServerUtils;
import com.sun.net.httpserver.HttpExchange;
+import com.sun.net.httpserver.HttpHandler;
-public class CmsAcrHttpHandler extends DavServerHandler {
+public class CmsAcrHttpHandler implements HttpHandler {
private ProvidedRepository contentRepository;
@Override
- protected void handleGET(HttpExchange exchange) {
+ public void handle(HttpExchange exchange) throws IOException {
+ String method = exchange.getRequestMethod();
+ if (DavMethod.PROPFIND.name().equals(method)) {
+ handlePROPFIND(exchange);
+ } else if (HttpMethod.GET.name().equals(method)) {
+ handleGET(exchange);
+ } else {
+ throw new IllegalArgumentException("Unsupported method " + method);
+ }
+
+ }
+
+ protected void handlePROPFIND(HttpExchange exchange) throws IOException {
+ String relativePath = HttpServerUtils.relativize(exchange);
+
+ DavDepth depth = DavDepth.fromHttpExchange(exchange);
+ if (depth == null) {
+ // default, as per http://www.webdav.org/specs/rfc4918.html#METHOD_PROPFIND
+ depth = DavDepth.DEPTH_INFINITY;
+ }
+
ContentSession session = RemoteAuthUtils.doAs(() -> contentRepository.get(),
new RemoteAuthHttpExchange(exchange));
+
+ String path = ContentUtils.ROOT_SLASH + relativePath;
+ if (!session.exists(path)) {// not found
+ exchange.sendResponseHeaders(HttpResponseStatus.NOT_FOUND.getCode(), -1);
+ return;
+ }
+ Content content = session.get(path);
+
+ CompletableFuture<Void> published = new CompletableFuture<Void>();
+
+ try (InputStream in = exchange.getRequestBody()) {
+ DavPropfind davPropfind = DavPropfind.load(depth, in);
+ MultiStatusWriter msWriter = new MultiStatusWriter();
+ ForkJoinPool.commonPool().execute(() -> {
+ publishDavResponses(content, davPropfind, msWriter);
+ published.complete(null);
+ });
+ exchange.sendResponseHeaders(HttpResponseStatus.MULTI_STATUS.getCode(), 0l);
+ try (OutputStream out = exchange.getResponseBody()) {
+ msWriter.process(session, out, published.minimalCompletionStage(), davPropfind.isPropname());
+ }
+ }
+ }
+
+ protected void publishDavResponses(Content content, DavPropfind davPropfind, Consumer<DavResponse> consumer) {
+ publishDavResponse(content, davPropfind, consumer, 0);
+ }
+
+ protected void publishDavResponse(Content content, DavPropfind davPropfind, Consumer<DavResponse> consumer,
+ int currentDepth) {
+ DavResponse davResponse = new DavResponse();
+ String href = CmsConstants.PATH_API_ACR + content.getPath();
+ davResponse.setHref(href);
+ if (content.hasContentClass(CrName.collection))
+ davResponse.setCollection(true);
+ if (davPropfind.isAllprop()) {
+ for (Map.Entry<QName, Object> entry : content.entrySet()) {
+ davResponse.getPropertyNames().add(entry.getKey());
+ processMapEntry(davResponse, entry.getKey(), entry.getValue());
+ }
+ davResponse.getResourceTypes().addAll(content.getContentClasses());
+ } else if (davPropfind.isPropname()) {
+ for (QName key : content.keySet()) {
+ davResponse.getPropertyNames().add(key);
+ }
+ } else {
+ for (QName key : davPropfind.getProps()) {
+ if (content.containsKey(key)) {
+ davResponse.getPropertyNames().add(key);
+ Object value = content.get(key);
+ processMapEntry(davResponse, key, value);
+ }
+ if (DavXmlElement.resourcetype.qName().equals(key)) {
+ davResponse.getResourceTypes().addAll(content.getContentClasses());
+ }
+ }
+
+ }
+
+ consumer.accept(davResponse);
+
+ // recurse only on collections
+ if (content.hasContentClass(CrName.collection)) {
+ if (davPropfind.getDepth() == DavDepth.DEPTH_INFINITY
+ || (davPropfind.getDepth() == DavDepth.DEPTH_1 && currentDepth == 0)) {
+ for (Content child : content) {
+ publishDavResponse(child, davPropfind, consumer, currentDepth + 1);
+ }
+ }
+ }
+ }
+
+ protected void processMapEntry(DavResponse davResponse, QName key, Object value) {
+ // ignore content classes
+ if (CrName.cc.qName().equals(key))
+ return;
+ String str;
+ if (value instanceof Collection) {
+ StringJoiner sj = new StringJoiner("\n");
+ for (Object v : (Collection<?>) value) {
+ sj.add(v.toString());
+ }
+ str = sj.toString();
+ } else {
+ str = value.toString();
+ }
+ davResponse.getProperties().put(key, str);
+
+ }
+
+ protected void handleGET(HttpExchange exchange) {
String relativePath = HttpServerUtils.relativize(exchange);
+ ContentSession session = RemoteAuthUtils.doAs(() -> contentRepository.get(),
+ new RemoteAuthHttpExchange(exchange));
Content content = session.get(ContentUtils.ROOT_SLASH + relativePath);
Optional<Long> size = content.get(CrName.size, Long.class);
try (InputStream in = content.open(InputStream.class)) {
- exchange.sendResponseHeaders(HttpResponseStatus.OK.getStatusCode(), size.orElse(0l));
+ exchange.sendResponseHeaders(HttpResponseStatus.OK.getCode(), size.orElse(0l));
StreamUtils.copy(in, exchange.getResponseBody());
} catch (IOException e) {
throw new RuntimeException("Cannot process " + relativePath, e);
+++ /dev/null
-package org.argeo.util.dav;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URI;
-import java.net.http.HttpClient;
-import java.net.http.HttpRequest;
-import java.net.http.HttpRequest.BodyPublishers;
-import java.net.http.HttpResponse;
-import java.net.http.HttpResponse.BodyHandler;
-import java.net.http.HttpResponse.BodyHandlers;
-import java.util.Iterator;
-
-import javax.xml.namespace.QName;
-
-public class DavClient {
-
- private HttpClient httpClient;
-
- public DavClient() {
- httpClient = HttpClient.newBuilder() //
-// .sslContext(insecureContext()) //
- .version(HttpClient.Version.HTTP_1_1) //
- .build();
- }
-
- public void setProperty(String url, QName key, String value) {
- try {
- String body = """
- <?xml version="1.0" encoding="utf-8" ?>
- <D:propertyupdate xmlns:D="DAV:"
- """ //
- + "xmlns:" + key.getPrefix() + "=\"" + key.getNamespaceURI() + "\">" + //
- """
- <D:set>
- <D:prop>
- """ //
- + "<" + key.getPrefix() + ":" + key.getLocalPart() + ">" + value + "</" + key.getPrefix() + ":"
- + key.getLocalPart() + ">" + //
- """
- </D:prop>
- </D:set>
- </D:propertyupdate>
- """;
- System.out.println(body);
- HttpRequest request = HttpRequest.newBuilder().uri(URI.create(url)) //
- .header("Depth", "1") //
- .method(DavMethod.PROPPATCH.name(), BodyPublishers.ofString(body)) //
- .build();
- BodyHandler<String> bodyHandler = BodyHandlers.ofString();
- HttpResponse<String> response = httpClient.send(request, bodyHandler);
- System.out.println(response.body());
- } catch (IOException | InterruptedException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
-
- public Iterator<DavResponse> listChildren(URI uri) {
- try {
- String body = """
- <?xml version="1.0" encoding="utf-8" ?>
- <D:propfind xmlns:D="DAV:">
- <D:propname/>
- </D:propfind>""";
- HttpRequest request = HttpRequest.newBuilder().uri(uri) //
- .header(DavHeader.DEPTH.name(), "1") //
- .method(DavMethod.PROPFIND.name(), BodyPublishers.ofString(body)) //
- .build();
-
-// HttpResponse<String> responseStr = httpClient.send(request, BodyHandlers.ofString());
-// System.out.println(responseStr.body());
-
- HttpResponse<InputStream> response = httpClient.send(request, BodyHandlers.ofInputStream());
- MultiStatusReader msReader = new MultiStatusReader(response.body(), uri.getPath());
- return msReader;
- } catch (IOException | InterruptedException e) {
- throw new IllegalStateException("Cannot list children of " + uri, e);
- }
-
- }
-
- public boolean exists(URI uri) {
- try {
- HttpRequest request = HttpRequest.newBuilder().uri(uri) //
- .header(DavHeader.DEPTH.name(), "0") //
- .method(DavMethod.HEAD.name(), BodyPublishers.noBody()) //
- .build();
- BodyHandler<String> bodyHandler = BodyHandlers.ofString();
- HttpResponse<String> response = httpClient.send(request, bodyHandler);
- System.out.println(response.body());
- int responseStatusCode = response.statusCode();
- if (responseStatusCode == 404)
- return false;
- if (responseStatusCode >= 200 && responseStatusCode < 300)
- return true;
- throw new IllegalStateException(
- "Cannot check whether " + uri + " exists: Unknown response status code " + responseStatusCode);
- } catch (IOException | InterruptedException e) {
- throw new IllegalStateException("Cannot check whether " + uri + " exists", e);
- }
-
- }
-
- public DavResponse get(URI uri) {
- try {
- String body = """
- <?xml version="1.0" encoding="utf-8" ?>
- <D:propfind xmlns:D="DAV:">
- <D:allprop/>
- </D:propfind>""";
- HttpRequest request = HttpRequest.newBuilder().uri(uri) //
- .header(DavHeader.DEPTH.name(), "0") //
- .method(DavMethod.PROPFIND.name(), BodyPublishers.ofString(body)) //
- .build();
-
-// HttpResponse<String> responseStr = httpClient.send(request, BodyHandlers.ofString());
-// System.out.println(responseStr.body());
-
- HttpResponse<InputStream> response = httpClient.send(request, BodyHandlers.ofInputStream());
- MultiStatusReader msReader = new MultiStatusReader(response.body());
- if (!msReader.hasNext())
- throw new IllegalArgumentException(uri + " does not exist");
- return msReader.next();
- } catch (IOException | InterruptedException e) {
- throw new IllegalStateException("Cannot list children of " + uri, e);
- }
-
- }
-
- public static void main(String[] args) {
- DavClient davClient = new DavClient();
- Iterator<DavResponse> responses = davClient
- .listChildren(URI.create("http://localhost/unstable/a2/org.argeo.tp.sdk/"));
- while (responses.hasNext()) {
- DavResponse response = responses.next();
- System.out.println(response.getHref() + (response.isCollection() ? " (collection)" : ""));
- System.out.println(" " + response.getPropertyNames());
-
- }
-// davClient.setProperty("http://localhost/unstable/a2/org.argeo.tp.sdk/org.opentest4j.1.2.jar",
-// CrName.uuid.qName(), UUID.randomUUID().toString());
-
- }
-
-}
+++ /dev/null
-package org.argeo.util.dav;
-
-/** Standard HTTP headers. */
-public enum DavHeader {
- DEPTH("Depth"), //
- ;
-
- private final String name;
-
- private DavHeader(String headerName) {
- this.name = headerName;
- }
-
- public String getName() {
- return name;
- }
-
- @Override
- public String toString() {
- return getName();
- }
-
-}
+++ /dev/null
-package org.argeo.util.dav;
-
-public enum DavMethod {
- // Generic HTTP
- HEAD, //
- // WebDav specific
- PROPFIND, //
- PROPPATCH, //
- ;
-}
+++ /dev/null
-package org.argeo.util.dav;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import javax.xml.namespace.QName;
-
-public class DavResponse {
- final static String MOD_DAV_NAMESPACE = "http://apache.org/dav/props/";
-
- private String href;
- private boolean collection;
- private Set<QName> propertyNames = new HashSet<>();
- private Map<QName, String> properties = new HashMap<>();
- private List<QName> resourceTypes = new ArrayList<>();
-
- public Map<QName, String> getProperties() {
- return properties;
- }
-
- void setHref(String href) {
- this.href = href;
- }
-
- public String getHref() {
- return href;
- }
-
- public boolean isCollection() {
- return collection;
- }
-
- void setCollection(boolean collection) {
- this.collection = collection;
- }
-
- public List<QName> getResourceTypes() {
- return resourceTypes;
- }
-
- public Set<QName> getPropertyNames() {
- return propertyNames;
- }
-
-}
+++ /dev/null
-package org.argeo.util.dav;
-
-import java.io.IOException;
-
-import org.argeo.util.http.HttpMethod;
-
-import com.sun.net.httpserver.HttpExchange;
-import com.sun.net.httpserver.HttpHandler;
-
-public class DavServerHandler implements HttpHandler {
-
- @Override
- public void handle(HttpExchange exchange) throws IOException {
- String method = exchange.getRequestMethod();
- if (DavMethod.PROPFIND.name().equals(method)) {
- handlePROPFIND(exchange);
- } else if (HttpMethod.GET.name().equals(method)) {
- handleGET(exchange);
- } else {
- throw new IllegalArgumentException("Unsupported method " + method);
- }
-
- }
-
- protected void handleGET(HttpExchange exchange) {
- throw new UnsupportedOperationException();
- }
-
- protected DavResponse handlePROPFIND(HttpExchange exchange) {
- throw new UnsupportedOperationException();
- }
-
-}
+++ /dev/null
-package org.argeo.util.dav;
-
-import javax.xml.namespace.QName;
-
-import org.argeo.util.naming.QNamed;
-
-public enum DavXmlElement implements QNamed {
- response, //
- href, //
- collection, //
- prop, //
- resourcetype, //
-
- // locking
- lockscope, //
- locktype, //
- supportedlock, //
- lockentry, //
- lockdiscovery, //
- write, //
- shared, //
- exclusive, //
- ;
-
- final static String WEBDAV_NAMESPACE_URI = "DAV:";
- final static String WEBDAV_DEFAULT_PREFIX = "D";
-
- @Override
- public String getNamespace() {
- return WEBDAV_NAMESPACE_URI;
- }
-
- @Override
- public String getDefaultPrefix() {
- return WEBDAV_DEFAULT_PREFIX;
- }
-
- public static DavXmlElement toEnum(QName name) {
- for (DavXmlElement e : values()) {
- if (e.qName().equals(name))
- return e;
- }
- return null;
- }
-}
+++ /dev/null
-package org.argeo.util.dav;
-
-import java.io.InputStream;
-import java.util.Iterator;
-import java.util.concurrent.ArrayBlockingQueue;
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ForkJoinPool;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import javax.xml.namespace.QName;
-import javax.xml.stream.FactoryConfigurationError;
-import javax.xml.stream.XMLInputFactory;
-import javax.xml.stream.XMLStreamConstants;
-import javax.xml.stream.XMLStreamException;
-import javax.xml.stream.XMLStreamReader;
-
-class MultiStatusReader implements Iterator<DavResponse> {
- private CompletableFuture<Boolean> empty = new CompletableFuture<Boolean>();
- private AtomicBoolean processed = new AtomicBoolean(false);
-
- private BlockingQueue<DavResponse> queue = new ArrayBlockingQueue<>(64);
-
- private final String ignoredHref;
-
- public MultiStatusReader(InputStream in) {
- this(in, null);
- }
-
- /** Typically ignoring self */
- public MultiStatusReader(InputStream in, String ignoredHref) {
- this.ignoredHref = ignoredHref;
- ForkJoinPool.commonPool().execute(() -> process(in));
-
- }
-
- protected void process(InputStream in) {
- try {
- XMLInputFactory inputFactory = XMLInputFactory.newFactory();
- XMLStreamReader reader = inputFactory.createXMLStreamReader(in);
-
- DavResponse currentResponse = null;
- boolean collectiongProperties = false;
-
- final QName COLLECTION = DavXmlElement.collection.qName(); // optimisation
- elements: while (reader.hasNext()) {
- reader.next();
- if (reader.isStartElement()) {
- QName name = reader.getName();
-// System.out.println(name);
- DavXmlElement davXmlElement = DavXmlElement.toEnum(name);
- if (davXmlElement != null) {
- switch (davXmlElement) {
- case response:
- currentResponse = new DavResponse();
- break;
- case href:
- assert currentResponse != null;
- while (reader.hasNext() && !reader.hasText())
- reader.next();
- String href = reader.getText();
- currentResponse.setHref(href);
- break;
-// case collection:
-// currentResponse.setCollection(true);
-// break;
- case prop:
- collectiongProperties = true;
- break;
- case resourcetype:
- while (reader.hasNext()) {
- int event = reader.nextTag();
- QName resourceType = reader.getName();
- if (event == XMLStreamConstants.END_ELEMENT && name.equals(resourceType))
- break;
- assert currentResponse != null;
- if (event == XMLStreamConstants.START_ELEMENT) {
- if (COLLECTION.equals(resourceType))
- currentResponse.setCollection(true);
- else
- currentResponse.getResourceTypes().add(resourceType);
- }
- }
- break;
- default:
- // ignore
- }
- } else {
- if (collectiongProperties) {
- String value = null;
- // TODO deal with complex properties
- readProperty: while (reader.hasNext()) {
- reader.next();
- if (reader.getEventType() == XMLStreamConstants.END_ELEMENT)
- break readProperty;
- if (reader.getEventType() == XMLStreamConstants.CHARACTERS)
- value = reader.getText();
- }
-
- if (name.getNamespaceURI().equals(DavResponse.MOD_DAV_NAMESPACE))
- continue elements; // skip mod_dav properties
-
- assert currentResponse != null;
- currentResponse.getPropertyNames().add(name);
- if (value != null)
- currentResponse.getProperties().put(name, value);
-
- }
- }
- } else if (reader.isEndElement()) {
- QName name = reader.getName();
-// System.out.println(name);
- DavXmlElement davXmlElement = DavXmlElement.toEnum(name);
- if (davXmlElement != null)
- switch (davXmlElement) {
- case response:
- assert currentResponse != null;
- if (ignoredHref == null || !ignoredHref.equals(currentResponse.getHref())) {
- if (!empty.isDone())
- empty.complete(false);
- publish(currentResponse);
- }
- case prop:
- collectiongProperties = false;
- break;
- default:
- // ignore
- }
- }
- }
-
- if (!empty.isDone())
- empty.complete(true);
- } catch (FactoryConfigurationError | XMLStreamException e) {
- throw new IllegalStateException("Cannot process DAV response", e);
- } finally {
- processed();
- }
- }
-
- protected synchronized void publish(DavResponse response) {
- try {
- queue.put(response);
- } catch (InterruptedException e) {
- throw new IllegalStateException("Cannot put response " + response, e);
- } finally {
- notifyAll();
- }
- }
-
- protected synchronized void processed() {
- processed.set(true);
- notifyAll();
- }
-
- @Override
- public synchronized boolean hasNext() {
- try {
- if (empty.get())
- return false;
- while (!processed.get() && queue.isEmpty()) {
- wait();
- }
- if (!queue.isEmpty())
- return true;
- if (processed.get())
- return false;
- throw new IllegalStateException("Cannot determine hasNext");
- } catch (InterruptedException | ExecutionException e) {
- throw new IllegalStateException("Cannot determine hasNext", e);
- } finally {
- notifyAll();
- }
- }
-
- @Override
- public synchronized DavResponse next() {
- try {
- if (!hasNext())
- throw new IllegalStateException("No fursther items are available");
-
- DavResponse response = queue.take();
- return response;
- } catch (InterruptedException e) {
- throw new IllegalStateException("Cannot get next", e);
- } finally {
- notifyAll();
- }
- }
-
-}
public enum HttpResponseStatus {
// Successful responses (200–299)
OK(200), //
+ MULTI_STATUS(207), // WebDav
// Client error responses (400–499)
UNAUTHORIZED(401), //
FORBIDDEN(403), //
NOT_FOUND(404), //
;
- private final int statusCode;
+ private final int code;
HttpResponseStatus(int statusCode) {
- this.statusCode = statusCode;
+ this.code = statusCode;
}
- public int getStatusCode() {
- return statusCode;
+ public int getCode() {
+ return code;
}
}
--- /dev/null
+package org.argeo.util.internal;
+
+import javax.xml.namespace.QName;
+
+public class DisplayQName extends QName {
+ private static final long serialVersionUID = 2376484886212253123L;
+
+ public DisplayQName(String namespaceURI, String localPart, String prefix) {
+ super(namespaceURI, localPart, prefix);
+ }
+
+ public DisplayQName(String localPart) {
+ super(localPart);
+ }
+
+ @Override
+ public String toString() {
+ String prefix = getPrefix();
+ assert prefix != null;
+ return "".equals(prefix) ? getLocalPart() : prefix + ":" + getLocalPart();
+ }
+
+ }
\ No newline at end of file
import java.util.function.Supplier;
+import javax.xml.namespace.QName;
+
+import org.argeo.util.internal.DisplayQName;
+
/**
* Standard LDAP attributes as per:<br>
* - <a href= "https://www.ldap.com/ldap-oid-reference">Standard LDAP</a><br>
* "https://github.com/krb5/krb5/blob/master/src/plugins/kdb/ldap/libkdb_ldap/kerberos.schema">Kerberos
* LDAP (partial)</a>
*/
-public enum LdapAttrs implements SpecifiedName, Supplier<String>, QNamed {
+public enum LdapAttrs implements SpecifiedName, Supplier<String> {
/** */
uid("0.9.2342.19200300.100.1.1", "RFC 4519"),
/** */
// private final static String LDAP_ = "ldap:";
private final String oid, spec;
+ private final QName value;
LdapAttrs(String oid, String spec) {
this.oid = oid;
this.spec = spec;
+ this.value = new DisplayQName(LdapObjs.LDAP_NAMESPACE_URI, name(), LdapObjs.LDAP_DEFAULT_PREFIX);
+ }
+
+ public QName qName() {
+ return value;
}
@Override
return get();
}
- public String get() {
- String prefix = getDefaultPrefix();
- return prefix != null ? prefix + ":" + name() : name();
- }
-
@Override
- public String getDefaultPrefix() {
- return LdapObjs.LDAP_DEFAULT_PREFIX;
- }
-
- @Override
- public String getNamespace() {
- return LdapObjs.LDAP_NAMESPACE_URI;
+ public String get() {
+ return LdapObjs.LDAP_DEFAULT_PREFIX + ":" + name();
}
@Override
package org.argeo.util.naming;
+import java.util.function.Supplier;
+
+import javax.xml.namespace.QName;
+
+import org.argeo.util.internal.DisplayQName;
+
/**
* Standard LDAP object classes as per
* <a href="https://www.ldap.com/ldap-oid-reference">https://www.ldap.com/ldap-
* oid-reference</a>
*/
-public enum LdapObjs implements SpecifiedName, QNamed {
+public enum LdapObjs implements SpecifiedName, Supplier<String> {
account("0.9.2342.19200300.100.4.5", "RFC 4524"),
/** */
document("0.9.2342.19200300.100.4.6", "RFC 4524"),
/** MUST be equal to ContentRepository LDAP prefix. */
final static String LDAP_DEFAULT_PREFIX = "ldap";
- private final static String LDAP_ = LDAP_DEFAULT_PREFIX + ":";
private final String oid, spec;
+ private final QName value;
private LdapObjs(String oid, String spec) {
this.oid = oid;
this.spec = spec;
+ this.value = new DisplayQName(LDAP_NAMESPACE_URI, name(), LDAP_DEFAULT_PREFIX);
+ }
+
+ public QName qName() {
+ return value;
}
public String getOid() {
return spec;
}
+ @Deprecated
public String property() {
- return new StringBuilder(LDAP_).append(name()).toString();
- }
-
- @Override
- public String getDefaultPrefix() {
- return LdapObjs.LDAP_DEFAULT_PREFIX;
+ return get();
}
@Override
- public String getNamespace() {
- return LdapObjs.LDAP_NAMESPACE_URI;
+ public String get() {
+ return LdapObjs.LDAP_DEFAULT_PREFIX + ":" + name();
}
}
+++ /dev/null
-package org.argeo.util.naming;
-
-import javax.xml.namespace.QName;
-
-/** A (possibly) qualified name. To be used in enums. */
-@Deprecated
-public interface QNamed {
- String name();
-
- default QName qName() {
- return new DisplayQName(getNamespace(), name(), getDefaultPrefix());
- }
-
- String getNamespace();
-
- String getDefaultPrefix();
-
- static class DisplayQName extends QName {
- private static final long serialVersionUID = 2376484886212253123L;
-
- public DisplayQName(String namespaceURI, String localPart, String prefix) {
- super(namespaceURI, localPart, prefix);
- }
-
- public DisplayQName(String localPart) {
- super(localPart);
- }
-
- @Override
- public String toString() {
- String prefix = getPrefix();
- assert prefix != null;
- return "".equals(prefix) ? getLocalPart() : prefix + ":" + getLocalPart();
- }
-
- }
-}