]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsAcrHttpHandler.java
5a42a3e383ec2d9435af424f7276f394e3f18623
[lgpl/argeo-commons.git] / org.argeo.cms / src / org / argeo / cms / internal / runtime / CmsAcrHttpHandler.java
1 package org.argeo.cms.internal.runtime;
2
3 import java.io.IOException;
4 import java.io.InputStream;
5 import java.util.Collection;
6 import java.util.Map;
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;
12
13 import javax.xml.namespace.QName;
14
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;
29
30 import com.sun.net.httpserver.HttpExchange;
31
32 /** A partial WebDav implementation based on ACR. */
33 public class CmsAcrHttpHandler extends DavHttpHandler {
34 private ProvidedRepository contentRepository;
35
36 @Override
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);
49 }
50 }
51
52 @Override
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);
60
61 CompletableFuture<Void> published = new CompletableFuture<Void>();
62 ForkJoinPool.commonPool().execute(() -> {
63 publishDavResponses(content, davPropfind, consumer);
64 published.complete(null);
65 });
66 return published;
67 }
68
69 protected void publishDavResponses(Content content, DavPropfind davPropfind, Consumer<DavResponse> consumer) {
70 publishDavResponse(content, davPropfind, consumer, 0);
71 }
72
73 protected void publishDavResponse(Content content, DavPropfind davPropfind, Consumer<DavResponse> consumer,
74 int currentDepth) {
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());
84 }
85 davResponse.getResourceTypes().addAll(content.getContentClasses());
86 } else if (davPropfind.isPropname()) {
87 for (QName key : content.keySet()) {
88 davResponse.getPropertyNames().add(key);
89 }
90 } else {
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);
96 }
97 if (DName.resourcetype.qName().equals(key)) {
98 davResponse.getResourceTypes().addAll(content.getContentClasses());
99 }
100 }
101
102 }
103
104 consumer.accept(davResponse);
105
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);
112 }
113 }
114 }
115 }
116
117 protected void processMapEntry(DavResponse davResponse, QName key, Object value) {
118 // ignore content classes
119 if (DName.resourcetype.qName().equals(key))
120 return;
121 String str;
122 if (value instanceof Collection) {
123 StringJoiner sj = new StringJoiner("\n");
124 for (Object v : (Collection<?>) value) {
125 sj.add(v.toString());
126 }
127 str = sj.toString();
128 } else {
129 str = value.toString();
130 }
131 davResponse.getProperties().put(key, str);
132
133 }
134
135 public void setContentRepository(ProvidedRepository contentRepository) {
136 this.contentRepository = contentRepository;
137 }
138
139 }