]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.cms/src/org/argeo/cms/dav/MultiStatusReader.java
FS utils throws IOException
[lgpl/argeo-commons.git] / org.argeo.cms / src / org / argeo / cms / dav / MultiStatusReader.java
1 package org.argeo.cms.dav;
2
3 import java.io.InputStream;
4 import java.nio.charset.StandardCharsets;
5 import java.util.HashSet;
6 import java.util.Iterator;
7 import java.util.Set;
8 import java.util.concurrent.ArrayBlockingQueue;
9 import java.util.concurrent.BlockingQueue;
10 import java.util.concurrent.CompletableFuture;
11 import java.util.concurrent.ExecutionException;
12 import java.util.concurrent.ForkJoinPool;
13 import java.util.concurrent.atomic.AtomicBoolean;
14
15 import javax.xml.namespace.QName;
16 import javax.xml.stream.FactoryConfigurationError;
17 import javax.xml.stream.XMLInputFactory;
18 import javax.xml.stream.XMLStreamConstants;
19 import javax.xml.stream.XMLStreamException;
20 import javax.xml.stream.XMLStreamReader;
21
22 import org.argeo.cms.http.HttpStatus;
23
24 /**
25 * Asynchronously iterate over the response statuses of the response to a
26 * PROPFIND request.
27 */
28 class MultiStatusReader implements Iterator<DavResponse> {
29 private CompletableFuture<Boolean> empty = new CompletableFuture<Boolean>();
30 private AtomicBoolean processed = new AtomicBoolean(false);
31
32 private BlockingQueue<DavResponse> queue = new ArrayBlockingQueue<>(64);
33
34 private final String ignoredHref;
35
36 public MultiStatusReader(InputStream in) {
37 this(in, null);
38 }
39
40 /** Typically ignoring self */
41 public MultiStatusReader(InputStream in, String ignoredHref) {
42 this.ignoredHref = ignoredHref;
43 ForkJoinPool.commonPool().execute(() -> process(in));
44 }
45
46 protected void process(InputStream in) {
47 try {
48 XMLInputFactory inputFactory = XMLInputFactory.newFactory();
49 XMLStreamReader reader = inputFactory.createXMLStreamReader(in, StandardCharsets.UTF_8.name());
50
51 DavResponse currentResponse = null;
52 boolean collectiongProperties = false;
53 Set<QName> currentPropertyNames = null;
54 HttpStatus currentStatus = null;
55
56 final QName COLLECTION = DavXmlElement.collection.qName(); // optimisation
57 elements: while (reader.hasNext()) {
58 reader.next();
59 if (reader.isStartElement()) {
60 QName name = reader.getName();
61 // System.out.println(name);
62 DavXmlElement davXmlElement = DavXmlElement.toEnum(name);
63 if (davXmlElement != null) {
64 switch (davXmlElement) {
65 case response:
66 currentResponse = new DavResponse();
67 break;
68 case href:
69 assert currentResponse != null;
70 while (reader.hasNext() && !reader.hasText())
71 reader.next();
72 String href = reader.getText();
73 currentResponse.setHref(href);
74 break;
75 // case collection:
76 // currentResponse.setCollection(true);
77 // break;
78 case status:
79 reader.next();
80 String statusLine = reader.getText();
81 currentStatus = HttpStatus.parseStatusLine(statusLine);
82 break;
83 case prop:
84 collectiongProperties = true;
85 currentPropertyNames = new HashSet<>();
86 break;
87 case resourcetype:
88 while (reader.hasNext()) {
89 int event = reader.nextTag();
90 QName resourceType = reader.getName();
91 if (event == XMLStreamConstants.END_ELEMENT && name.equals(resourceType))
92 break;
93 assert currentResponse != null;
94 if (event == XMLStreamConstants.START_ELEMENT) {
95 if (COLLECTION.equals(resourceType))
96 currentResponse.setCollection(true);
97 else
98 currentResponse.getResourceTypes().add(resourceType);
99 }
100 }
101 break;
102 default:
103 // ignore
104 }
105 } else {
106 if (collectiongProperties) {
107 String value = null;
108 // TODO deal with complex properties
109 readProperty: while (reader.hasNext()) {
110 reader.next();
111 if (reader.getEventType() == XMLStreamConstants.END_ELEMENT)
112 break readProperty;
113 if (reader.getEventType() == XMLStreamConstants.CHARACTERS)
114 value = reader.getText();
115 }
116
117 if (name.getNamespaceURI().equals(DavResponse.MOD_DAV_NAMESPACE))
118 continue elements; // skip mod_dav properties
119
120 assert currentResponse != null;
121 currentPropertyNames.add(name);
122 if (value != null)
123 currentResponse.getProperties().put(name, value);
124
125 }
126 }
127 } else if (reader.isEndElement()) {
128 QName name = reader.getName();
129 // System.out.println(name);
130 DavXmlElement davXmlElement = DavXmlElement.toEnum(name);
131 if (davXmlElement != null)
132 switch (davXmlElement) {
133 case propstat:
134 currentResponse.getPropertyNames(currentStatus).addAll(currentPropertyNames);
135 currentPropertyNames = null;
136 break;
137 case response:
138 assert currentResponse != null;
139 if (ignoredHref == null || !ignoredHref.equals(currentResponse.getHref())) {
140 if (!empty.isDone())
141 empty.complete(false);
142 publish(currentResponse);
143 }
144 case prop:
145 collectiongProperties = false;
146 break;
147 default:
148 // ignore
149 }
150 }
151 }
152
153 if (!empty.isDone())
154 empty.complete(true);
155 } catch (FactoryConfigurationError | XMLStreamException e) {
156 empty.completeExceptionally(e);
157 throw new IllegalStateException("Cannot process DAV response", e);
158 } finally {
159 processed();
160 }
161 }
162
163 protected synchronized void publish(DavResponse response) {
164 try {
165 queue.put(response);
166 } catch (InterruptedException e) {
167 throw new IllegalStateException("Cannot put response " + response, e);
168 } finally {
169 notifyAll();
170 }
171 }
172
173 protected synchronized void processed() {
174 processed.set(true);
175 notifyAll();
176 }
177
178 @Override
179 public synchronized boolean hasNext() {
180 try {
181 if (empty.get())
182 return false;
183 while (!processed.get() && queue.isEmpty()) {
184 wait();
185 }
186 if (!queue.isEmpty())
187 return true;
188 if (processed.get())
189 return false;
190 throw new IllegalStateException("Cannot determine hasNext");
191 } catch (InterruptedException | ExecutionException e) {
192 throw new IllegalStateException("Cannot determine hasNext", e);
193 } finally {
194 // notifyAll();
195 }
196 }
197
198 @Override
199 public synchronized DavResponse next() {
200 try {
201 if (!hasNext())
202 throw new IllegalStateException("No fursther items are available");
203
204 DavResponse response = queue.take();
205 return response;
206 } catch (InterruptedException e) {
207 throw new IllegalStateException("Cannot get next", e);
208 } finally {
209 // notifyAll();
210 }
211 }
212
213 }