X-Git-Url: https://git.argeo.org/?a=blobdiff_plain;f=org.argeo.cms%2Fsrc%2Forg%2Fargeo%2Fcms%2Finternal%2Fruntime%2FCmsAcrHttpHandler.java;h=c80933a559753203cac8e50ebc95b98afd79fd42;hb=54df376a9c2dd458a82eaa09bfbb718fe699dd0d;hp=d9fa9e751d64394f37f5651fc0349afdc4580b33;hpb=1d7058b30bd990cda7d4efc1c029501f05a07113;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 d9fa9e751..c80933a55 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 @@ -1,7 +1,149 @@ package org.argeo.cms.internal.runtime; -import org.argeo.util.dav.DavServerHandler; +import java.io.IOException; +import java.io.InputStream; +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; -public class CmsAcrHttpHandler extends DavServerHandler { +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.DName; +import org.argeo.api.acr.RuntimeNamespaceContext; +import org.argeo.api.acr.spi.ProvidedRepository; +import org.argeo.api.cms.CmsConstants; +import org.argeo.cms.auth.RemoteAuthUtils; +import org.argeo.cms.dav.DavDepth; +import org.argeo.cms.dav.DavHttpHandler; +import org.argeo.cms.dav.DavPropfind; +import org.argeo.cms.dav.DavResponse; +import org.argeo.cms.http.HttpStatus; +import org.argeo.cms.internal.http.RemoteAuthHttpExchange; +import org.argeo.cms.util.StreamUtils; + +import com.sun.net.httpserver.HttpExchange; + +/** A partial WebDav implementation based on ACR. */ +public class CmsAcrHttpHandler extends DavHttpHandler { + private ProvidedRepository contentRepository; + + @Override + protected NamespaceContext getNamespaceContext(HttpExchange httpExchange, String path) { + // TODO be smarter? + return RuntimeNamespaceContext.getNamespaceContext(); + } + + @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 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 handlePROPFIND(HttpExchange exchange, String path, DavPropfind davPropfind, + Consumer consumer) 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); + + CompletableFuture published = new CompletableFuture(); + ForkJoinPool.commonPool().execute(() -> { + publishDavResponses(content, davPropfind, consumer); + published.complete(null); + }); + return published; + } + + 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(DName.collection)) + davResponse.setCollection(true); + if (davPropfind.isAllprop()) { + for (Map.Entry entry : content.entrySet()) { + 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(HttpStatus.OK).add(key); + } + } else { + for (QName key : davPropfind.getProps()) { + if (content.containsKey(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 (DName.resourcetype.qName().equals(key)) { + davResponse.getResourceTypes().addAll(content.getContentClasses()); + } + } + + } + + consumer.accept(davResponse); + + // recurse only on collections + if (content.hasContentClass(DName.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 (DName.resourcetype.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); + + } + + public void setContentRepository(ProvidedRepository contentRepository) { + this.contentRepository = contentRepository; + } }