1 package org
.argeo
.cms
.internal
.runtime
;
3 import java
.io
.IOException
;
4 import java
.io
.InputStream
;
5 import java
.util
.Collection
;
7 import java
.util
.Optional
;
8 import java
.util
.StringJoiner
;
9 import java
.util
.concurrent
.CompletableFuture
;
10 import java
.util
.concurrent
.ForkJoinPool
;
11 import java
.util
.function
.Consumer
;
13 import javax
.xml
.namespace
.NamespaceContext
;
14 import javax
.xml
.namespace
.QName
;
16 import org
.argeo
.api
.acr
.Content
;
17 import org
.argeo
.api
.acr
.ContentNotFoundException
;
18 import org
.argeo
.api
.acr
.ContentSession
;
19 import org
.argeo
.api
.acr
.DName
;
20 import org
.argeo
.api
.acr
.RuntimeNamespaceContext
;
21 import org
.argeo
.api
.acr
.spi
.ProvidedRepository
;
22 import org
.argeo
.api
.cms
.CmsConstants
;
23 import org
.argeo
.cms
.auth
.RemoteAuthUtils
;
24 import org
.argeo
.cms
.dav
.DavDepth
;
25 import org
.argeo
.cms
.dav
.DavHttpHandler
;
26 import org
.argeo
.cms
.dav
.DavPropfind
;
27 import org
.argeo
.cms
.dav
.DavResponse
;
28 import org
.argeo
.cms
.http
.HttpStatus
;
29 import org
.argeo
.cms
.internal
.http
.RemoteAuthHttpExchange
;
30 import org
.argeo
.cms
.util
.StreamUtils
;
32 import com
.sun
.net
.httpserver
.HttpExchange
;
34 /** A partial WebDav implementation based on ACR. */
35 public class CmsAcrHttpHandler
extends DavHttpHandler
{
36 private ProvidedRepository contentRepository
;
39 protected NamespaceContext
getNamespaceContext(HttpExchange httpExchange
, String path
) {
41 return RuntimeNamespaceContext
.getNamespaceContext();
45 protected void handleGET(HttpExchange exchange
, String path
) throws IOException
{
46 ContentSession session
= RemoteAuthUtils
.doAs(() -> contentRepository
.get(),
47 new RemoteAuthHttpExchange(exchange
));
48 if (!session
.exists(path
)) // not found
49 throw new ContentNotFoundException(session
, path
);
50 Content content
= session
.get(path
);
51 Optional
<Long
> size
= content
.get(DName
.getcontentlength
, Long
.class);
52 try (InputStream in
= content
.open(InputStream
.class)) {
53 exchange
.sendResponseHeaders(HttpStatus
.OK
.getCode(), size
.orElse(0l));
54 StreamUtils
.copy(in
, exchange
.getResponseBody());
55 } catch (IOException e
) {
56 throw new RuntimeException("Cannot process " + path
, e
);
61 protected CompletableFuture
<Void
> handlePROPFIND(HttpExchange exchange
, String path
, DavPropfind davPropfind
,
62 Consumer
<DavResponse
> consumer
) throws IOException
{
63 ContentSession session
= RemoteAuthUtils
.doAs(() -> contentRepository
.get(),
64 new RemoteAuthHttpExchange(exchange
));
65 if (!session
.exists(path
)) // not found
66 throw new ContentNotFoundException(session
, path
);
67 Content content
= session
.get(path
);
69 CompletableFuture
<Void
> published
= new CompletableFuture
<Void
>();
70 ForkJoinPool
.commonPool().execute(() -> {
71 publishDavResponses(content
, davPropfind
, consumer
);
72 published
.complete(null);
77 protected void publishDavResponses(Content content
, DavPropfind davPropfind
, Consumer
<DavResponse
> consumer
) {
78 publishDavResponse(content
, davPropfind
, consumer
, 0);
81 protected void publishDavResponse(Content content
, DavPropfind davPropfind
, Consumer
<DavResponse
> consumer
,
83 DavResponse davResponse
= new DavResponse();
84 String href
= CmsConstants
.PATH_API_ACR
+ content
.getPath();
85 davResponse
.setHref(href
);
86 if (content
.hasContentClass(DName
.collection
))
87 davResponse
.setCollection(true);
88 if (davPropfind
.isAllprop()) {
89 for (Map
.Entry
<QName
, Object
> entry
: content
.entrySet()) {
90 davResponse
.getPropertyNames(HttpStatus
.OK
).add(entry
.getKey());
91 processMapEntry(davResponse
, entry
.getKey(), entry
.getValue());
93 davResponse
.getResourceTypes().addAll(content
.getContentClasses());
94 } else if (davPropfind
.isPropname()) {
95 for (QName key
: content
.keySet()) {
96 davResponse
.getPropertyNames(HttpStatus
.OK
).add(key
);
99 for (QName key
: davPropfind
.getProps()) {
100 if (content
.containsKey(key
)) {
101 davResponse
.getPropertyNames(HttpStatus
.OK
).add(key
);
102 Object value
= content
.get(key
);
103 processMapEntry(davResponse
, key
, value
);
105 davResponse
.getPropertyNames(HttpStatus
.NOT_FOUND
).add(key
);
107 if (DName
.resourcetype
.qName().equals(key
)) {
108 davResponse
.getResourceTypes().addAll(content
.getContentClasses());
114 consumer
.accept(davResponse
);
116 // recurse only on collections
117 if (content
.hasContentClass(DName
.collection
)) {
118 if (davPropfind
.getDepth() == DavDepth
.DEPTH_INFINITY
119 || (davPropfind
.getDepth() == DavDepth
.DEPTH_1
&& currentDepth
== 0)) {
120 for (Content child
: content
) {
121 publishDavResponse(child
, davPropfind
, consumer
, currentDepth
+ 1);
127 protected void processMapEntry(DavResponse davResponse
, QName key
, Object value
) {
128 // ignore content classes
129 if (DName
.resourcetype
.qName().equals(key
))
132 if (value
instanceof Collection
) {
133 StringJoiner sj
= new StringJoiner("\n");
134 for (Object v
: (Collection
<?
>) value
) {
135 sj
.add(v
.toString());
139 str
= value
.toString();
141 davResponse
.getProperties().put(key
, str
);
145 public void setContentRepository(ProvidedRepository contentRepository
) {
146 this.contentRepository
= contentRepository
;