]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsAcrHttpHandler.java
Improve ACR attribute typing.
[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.NamespaceContext;
14 import javax.xml.namespace.QName;
15
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;
31
32 import com.sun.net.httpserver.HttpExchange;
33
34 /** A partial WebDav implementation based on ACR. */
35 public class CmsAcrHttpHandler extends DavHttpHandler {
36 private ProvidedRepository contentRepository;
37
38 @Override
39 protected NamespaceContext getNamespaceContext(HttpExchange httpExchange, String path) {
40 // TODO be smarter?
41 return RuntimeNamespaceContext.getNamespaceContext();
42 }
43
44 @Override
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);
57 }
58 }
59
60 @Override
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);
68
69 CompletableFuture<Void> published = new CompletableFuture<Void>();
70 ForkJoinPool.commonPool().execute(() -> {
71 publishDavResponses(content, davPropfind, consumer);
72 published.complete(null);
73 });
74 return published;
75 }
76
77 protected void publishDavResponses(Content content, DavPropfind davPropfind, Consumer<DavResponse> consumer) {
78 publishDavResponse(content, davPropfind, consumer, 0);
79 }
80
81 protected void publishDavResponse(Content content, DavPropfind davPropfind, Consumer<DavResponse> consumer,
82 int currentDepth) {
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());
92 }
93 davResponse.getResourceTypes().addAll(content.getContentClasses());
94 } else if (davPropfind.isPropname()) {
95 for (QName key : content.keySet()) {
96 davResponse.getPropertyNames(HttpStatus.OK).add(key);
97 }
98 } else {
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);
104 } else {
105 davResponse.getPropertyNames(HttpStatus.NOT_FOUND).add(key);
106 }
107 if (DName.resourcetype.qName().equals(key)) {
108 davResponse.getResourceTypes().addAll(content.getContentClasses());
109 }
110 }
111
112 }
113
114 consumer.accept(davResponse);
115
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);
122 }
123 }
124 }
125 }
126
127 protected void processMapEntry(DavResponse davResponse, QName key, Object value) {
128 // ignore content classes
129 if (DName.resourcetype.qName().equals(key))
130 return;
131 String str;
132 if (value instanceof Collection) {
133 StringJoiner sj = new StringJoiner("\n");
134 for (Object v : (Collection<?>) value) {
135 sj.add(v.toString());
136 }
137 str = sj.toString();
138 } else {
139 str = value.toString();
140 }
141 davResponse.getProperties().put(key, str);
142
143 }
144
145 public void setContentRepository(ProvidedRepository contentRepository) {
146 this.contentRepository = contentRepository;
147 }
148
149 }