X-Git-Url: https://git.argeo.org/?a=blobdiff_plain;f=org.argeo.cms%2Fsrc%2Forg%2Fargeo%2Fcms%2Finternal%2Fruntime%2FCmsAcrHttpHandler.java;h=92da1b0c298ed3524087cf308ff0703ddc3af173;hb=b8f50d6d8e7b9c9215d156ba33f9dedfcee913a7;hp=7799a8c4eea29f9f1547721ecbd1c7d431cd847c;hpb=b897c27d370ad4772b5ae4b2c22cc505ef5d1a3f;p=lgpl%2Fargeo-commons.git diff --git a/org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsAcrHttpHandler.java b/org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsAcrHttpHandler.java index 7799a8c4e..92da1b0c2 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsAcrHttpHandler.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsAcrHttpHandler.java @@ -2,34 +2,164 @@ package org.argeo.cms.internal.runtime; 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 published = new CompletableFuture(); + + 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 consumer) { + publishDavResponse(content, davPropfind, consumer, 0); + } + + protected void publishDavResponse(Content content, DavPropfind davPropfind, Consumer 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 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 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);