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
.QName
;
15 import org
.argeo
.api
.acr
.Content
;
16 import org
.argeo
.api
.acr
.ContentNotFoundException
;
17 import org
.argeo
.api
.acr
.ContentSession
;
18 import org
.argeo
.api
.acr
.DName
;
19 import org
.argeo
.api
.acr
.spi
.ProvidedRepository
;
20 import org
.argeo
.api
.cms
.CmsConstants
;
21 import org
.argeo
.cms
.auth
.RemoteAuthUtils
;
22 import org
.argeo
.cms
.dav
.DavDepth
;
23 import org
.argeo
.cms
.dav
.DavHttpHandler
;
24 import org
.argeo
.cms
.dav
.DavPropfind
;
25 import org
.argeo
.cms
.dav
.DavResponse
;
26 import org
.argeo
.cms
.internal
.http
.RemoteAuthHttpExchange
;
27 import org
.argeo
.util
.StreamUtils
;
28 import org
.argeo
.util
.http
.HttpResponseStatus
;
30 import com
.sun
.net
.httpserver
.HttpExchange
;
32 /** A partial WebDav implementation based on ACR. */
33 public class CmsAcrHttpHandler
extends DavHttpHandler
{
34 private ProvidedRepository contentRepository
;
37 protected void handleGET(HttpExchange exchange
, String path
) throws IOException
{
38 ContentSession session
= RemoteAuthUtils
.doAs(() -> contentRepository
.get(),
39 new RemoteAuthHttpExchange(exchange
));
40 if (!session
.exists(path
)) // not found
41 throw new ContentNotFoundException(session
, path
);
42 Content content
= session
.get(path
);
43 Optional
<Long
> size
= content
.get(DName
.getcontentlength
, Long
.class);
44 try (InputStream in
= content
.open(InputStream
.class)) {
45 exchange
.sendResponseHeaders(HttpResponseStatus
.OK
.getCode(), size
.orElse(0l));
46 StreamUtils
.copy(in
, exchange
.getResponseBody());
47 } catch (IOException e
) {
48 throw new RuntimeException("Cannot process " + path
, e
);
53 protected CompletableFuture
<Void
> handlePROPFIND(HttpExchange exchange
, String path
, DavPropfind davPropfind
,
54 Consumer
<DavResponse
> consumer
) throws IOException
{
55 ContentSession session
= RemoteAuthUtils
.doAs(() -> contentRepository
.get(),
56 new RemoteAuthHttpExchange(exchange
));
57 if (!session
.exists(path
)) // not found
58 throw new ContentNotFoundException(session
, path
);
59 Content content
= session
.get(path
);
61 CompletableFuture
<Void
> published
= new CompletableFuture
<Void
>();
62 ForkJoinPool
.commonPool().execute(() -> {
63 publishDavResponses(content
, davPropfind
, consumer
);
64 published
.complete(null);
69 protected void publishDavResponses(Content content
, DavPropfind davPropfind
, Consumer
<DavResponse
> consumer
) {
70 publishDavResponse(content
, davPropfind
, consumer
, 0);
73 protected void publishDavResponse(Content content
, DavPropfind davPropfind
, Consumer
<DavResponse
> consumer
,
75 DavResponse davResponse
= new DavResponse();
76 String href
= CmsConstants
.PATH_API_ACR
+ content
.getPath();
77 davResponse
.setHref(href
);
78 if (content
.hasContentClass(DName
.collection
))
79 davResponse
.setCollection(true);
80 if (davPropfind
.isAllprop()) {
81 for (Map
.Entry
<QName
, Object
> entry
: content
.entrySet()) {
82 davResponse
.getPropertyNames().add(entry
.getKey());
83 processMapEntry(davResponse
, entry
.getKey(), entry
.getValue());
85 davResponse
.getResourceTypes().addAll(content
.getContentClasses());
86 } else if (davPropfind
.isPropname()) {
87 for (QName key
: content
.keySet()) {
88 davResponse
.getPropertyNames().add(key
);
91 for (QName key
: davPropfind
.getProps()) {
92 if (content
.containsKey(key
)) {
93 davResponse
.getPropertyNames().add(key
);
94 Object value
= content
.get(key
);
95 processMapEntry(davResponse
, key
, value
);
97 if (DName
.resourcetype
.qName().equals(key
)) {
98 davResponse
.getResourceTypes().addAll(content
.getContentClasses());
104 consumer
.accept(davResponse
);
106 // recurse only on collections
107 if (content
.hasContentClass(DName
.collection
)) {
108 if (davPropfind
.getDepth() == DavDepth
.DEPTH_INFINITY
109 || (davPropfind
.getDepth() == DavDepth
.DEPTH_1
&& currentDepth
== 0)) {
110 for (Content child
: content
) {
111 publishDavResponse(child
, davPropfind
, consumer
, currentDepth
+ 1);
117 protected void processMapEntry(DavResponse davResponse
, QName key
, Object value
) {
118 // ignore content classes
119 if (DName
.resourcetype
.qName().equals(key
))
122 if (value
instanceof Collection
) {
123 StringJoiner sj
= new StringJoiner("\n");
124 for (Object v
: (Collection
<?
>) value
) {
125 sj
.add(v
.toString());
129 str
= value
.toString();
131 davResponse
.getProperties().put(key
, str
);
135 public void setContentRepository(ProvidedRepository contentRepository
) {
136 this.contentRepository
= contentRepository
;