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.concurrent.ForkJoinPool;
import java.util.function.Consumer;
+import javax.xml.namespace.NamespaceContext;
import javax.xml.namespace.QName;
import org.argeo.api.acr.Content;
+import org.argeo.api.acr.ContentNotFoundException;
import org.argeo.api.acr.ContentSession;
-import org.argeo.api.acr.CrName;
+import org.argeo.api.acr.DName;
+import org.argeo.api.acr.RuntimeNamespaceContext;
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.DavHttpHandler;
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.http.HttpStatus;
import org.argeo.cms.internal.http.RemoteAuthHttpExchange;
-import org.argeo.util.StreamUtils;
-import org.argeo.util.http.HttpMethod;
-import org.argeo.util.http.HttpResponseStatus;
-import org.argeo.util.http.HttpServerUtils;
+import org.argeo.cms.util.StreamUtils;
import com.sun.net.httpserver.HttpExchange;
-import com.sun.net.httpserver.HttpHandler;
-public class CmsAcrHttpHandler implements HttpHandler {
+/** A partial WebDav implementation based on ACR. */
+public class CmsAcrHttpHandler extends DavHttpHandler {
private ProvidedRepository contentRepository;
@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 NamespaceContext getNamespaceContext(HttpExchange httpExchange, String path) {
+ // TODO be smarter?
+ return RuntimeNamespaceContext.getNamespaceContext();
}
- 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;
+ @Override
+ protected void handleGET(HttpExchange exchange, String path) throws IOException {
+ ContentSession session = RemoteAuthUtils.doAs(() -> contentRepository.get(),
+ new RemoteAuthHttpExchange(exchange));
+ if (!session.exists(path)) // not found
+ throw new ContentNotFoundException(session, path);
+ Content content = session.get(path);
+ Optional<Long> size = content.get(DName.getcontentlength, Long.class);
+ try (InputStream in = content.open(InputStream.class)) {
+ exchange.sendResponseHeaders(HttpStatus.OK.getCode(), size.orElse(0l));
+ StreamUtils.copy(in, exchange.getResponseBody());
+ } catch (IOException e) {
+ throw new RuntimeException("Cannot process " + path, e);
}
+ }
+ @Override
+ protected CompletableFuture<Void> handlePROPFIND(HttpExchange exchange, String path, DavPropfind davPropfind,
+ Consumer<DavResponse> consumer) throws IOException {
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;
- }
+ if (!session.exists(path)) // not found
+ throw new ContentNotFoundException(session, path);
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());
- }
- }
+ ForkJoinPool.commonPool().execute(() -> {
+ publishDavResponses(content, davPropfind, consumer);
+ published.complete(null);
+ });
+ return published;
}
protected void publishDavResponses(Content content, DavPropfind davPropfind, Consumer<DavResponse> consumer) {
DavResponse davResponse = new DavResponse();
String href = CmsConstants.PATH_API_ACR + content.getPath();
davResponse.setHref(href);
- if (content.hasContentClass(CrName.collection))
+ if (content.hasContentClass(DName.collection))
davResponse.setCollection(true);
if (davPropfind.isAllprop()) {
for (Map.Entry<QName, Object> entry : content.entrySet()) {
- davResponse.getPropertyNames().add(entry.getKey());
+ davResponse.getPropertyNames(HttpStatus.OK).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);
+ davResponse.getPropertyNames(HttpStatus.OK).add(key);
}
} else {
for (QName key : davPropfind.getProps()) {
if (content.containsKey(key)) {
- davResponse.getPropertyNames().add(key);
+ davResponse.getPropertyNames(HttpStatus.OK).add(key);
Object value = content.get(key);
processMapEntry(davResponse, key, value);
+ } else {
+ davResponse.getPropertyNames(HttpStatus.NOT_FOUND).add(key);
}
- if (DavXmlElement.resourcetype.qName().equals(key)) {
+ if (DName.resourcetype.qName().equals(key)) {
davResponse.getResourceTypes().addAll(content.getContentClasses());
}
}
consumer.accept(davResponse);
// recurse only on collections
- if (content.hasContentClass(CrName.collection)) {
+ if (content.hasContentClass(DName.collection)) {
if (davPropfind.getDepth() == DavDepth.DEPTH_INFINITY
|| (davPropfind.getDepth() == DavDepth.DEPTH_1 && currentDepth == 0)) {
for (Content child : content) {
protected void processMapEntry(DavResponse davResponse, QName key, Object value) {
// ignore content classes
- if (CrName.cc.qName().equals(key))
+ if (DName.resourcetype.qName().equals(key))
return;
String str;
if (value instanceof Collection) {
}
- 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.getCode(), size.orElse(0l));
- StreamUtils.copy(in, exchange.getResponseBody());
- } catch (IOException e) {
- throw new RuntimeException("Cannot process " + relativePath, e);
- }
- }
-
public void setContentRepository(ProvidedRepository contentRepository) {
this.contentRepository = contentRepository;
}