This project has retired. For details please refer to its
Attic page.
AtomEntryParser xref
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.chemistry.opencmis.server.impl.atompub;
20
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.math.BigInteger;
24 import java.util.Iterator;
25 import java.util.List;
26 import java.util.Locale;
27 import java.util.Map;
28
29 import javax.xml.namespace.QName;
30 import javax.xml.stream.XMLStreamConstants;
31 import javax.xml.stream.XMLStreamException;
32 import javax.xml.stream.XMLStreamReader;
33 import javax.xml.stream.XMLStreamWriter;
34
35 import org.apache.chemistry.opencmis.commons.PropertyIds;
36 import org.apache.chemistry.opencmis.commons.data.Acl;
37 import org.apache.chemistry.opencmis.commons.data.ContentStream;
38 import org.apache.chemistry.opencmis.commons.data.ObjectData;
39 import org.apache.chemistry.opencmis.commons.data.Properties;
40 import org.apache.chemistry.opencmis.commons.data.PropertyData;
41 import org.apache.chemistry.opencmis.commons.data.PropertyId;
42 import org.apache.chemistry.opencmis.commons.data.PropertyString;
43 import org.apache.chemistry.opencmis.commons.definitions.TypeDefinition;
44 import org.apache.chemistry.opencmis.commons.exceptions.CmisInvalidArgumentException;
45 import org.apache.chemistry.opencmis.commons.exceptions.CmisNotSupportedException;
46 import org.apache.chemistry.opencmis.commons.impl.Base64;
47 import org.apache.chemistry.opencmis.commons.impl.IOUtils;
48 import org.apache.chemistry.opencmis.commons.impl.XMLConstants;
49 import org.apache.chemistry.opencmis.commons.impl.XMLConstraints;
50 import org.apache.chemistry.opencmis.commons.impl.XMLConverter;
51 import org.apache.chemistry.opencmis.commons.impl.XMLUtils;
52 import org.apache.chemistry.opencmis.commons.impl.dataobjects.BulkUpdateImpl;
53 import org.apache.chemistry.opencmis.commons.impl.dataobjects.ContentStreamImpl;
54 import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertiesImpl;
55 import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyStringImpl;
56 import org.apache.chemistry.opencmis.commons.server.TempStoreOutputStream;
57 import org.apache.chemistry.opencmis.server.shared.CappedInputStream;
58 import org.apache.chemistry.opencmis.server.shared.TempStoreOutputStreamFactory;
59 import org.slf4j.Logger;
60 import org.slf4j.LoggerFactory;
61
62
63
64
65 public final class AtomEntryParser {
66
67 private static final Logger LOG = LoggerFactory.getLogger(AtomEntryParser.class);
68
69 private static final long MAX_STREAM_LENGTH = 10 * 1024 * 1024;
70
71 private static final String TAG_ENTRY = "entry";
72 private static final String TAG_TITLE = "title";
73 private static final String TAG_OBJECT = "object";
74 private static final String TAG_CONTENT = "content";
75 private static final String TAG_BASE64 = "base64";
76 private static final String TAG_MEDIATYPE = "mediatype";
77 private static final String TAG_FILENAME = "filename";
78 private static final String TAG_TYPE = "type";
79 private static final String TAG_BULK_UPDATE = "bulkUpdate";
80
81 private static final String ATTR_SRC = "src";
82 private static final String ATTR_TYPE = "type";
83
84 private boolean ignoreAtomContentSrc;
85
86 private CappedInputStream cappedStream;
87
88 private final TempStoreOutputStreamFactory streamFactory;
89
90 private ObjectData object;
91 private ContentStreamImpl atomContentStream;
92 private ContentStreamImpl cmisContentStream;
93 private TypeDefinition typeDef;
94 private BulkUpdateImpl bulkUpdate;
95
96
97
98
99 public AtomEntryParser(TempStoreOutputStreamFactory streamFactory) {
100 this.streamFactory = streamFactory;
101 }
102
103
104
105
106 public AtomEntryParser(InputStream stream, TempStoreOutputStreamFactory streamFactory) throws XMLStreamException,
107 IOException {
108 this(streamFactory);
109 parse(stream);
110 }
111
112
113
114
115
116 public void setIgnoreAtomContentSrc(boolean ignoreAtomContentSrc) {
117 this.ignoreAtomContentSrc = ignoreAtomContentSrc;
118 }
119
120
121
122
123 public ObjectData getObject() {
124 return object;
125 }
126
127
128
129
130 public Properties getProperties() {
131 return (object == null ? null : object.getProperties());
132 }
133
134
135
136
137 public String getId() {
138 Properties properties = getProperties();
139 if (properties == null) {
140 return null;
141 }
142
143 Map<String, PropertyData<?>> propertiesMap = properties.getProperties();
144 if (propertiesMap == null) {
145 return null;
146 }
147
148 PropertyData<?> property = propertiesMap.get(PropertyIds.OBJECT_ID);
149 if (property instanceof PropertyId) {
150 return ((PropertyId) property).getFirstValue();
151 }
152
153 return null;
154 }
155
156
157
158
159 public Acl getAcl() {
160 return (object == null ? null : object.getAcl());
161 }
162
163
164
165
166 public List<String> getPolicyIds() {
167 if ((object == null) || (object.getPolicyIds() == null)) {
168 return null;
169 }
170
171 return object.getPolicyIds().getPolicyIds();
172 }
173
174
175
176
177 public ContentStream getContentStream() {
178 return (cmisContentStream == null ? atomContentStream : cmisContentStream);
179 }
180
181
182
183
184 public TypeDefinition getTypeDefinition() {
185 return typeDef;
186 }
187
188
189
190
191 public BulkUpdateImpl getBulkUpdate() {
192 return bulkUpdate;
193 }
194
195
196
197
198 public void parse(InputStream stream) throws XMLStreamException, IOException {
199 release();
200
201 if (stream == null) {
202 return;
203 }
204
205 cappedStream = new CappedInputStream(stream, MAX_STREAM_LENGTH);
206 XMLStreamReader parser = XMLUtils.createParser(cappedStream);
207
208 try {
209 while (true) {
210 int event = parser.getEventType();
211 if (event == XMLStreamConstants.START_ELEMENT) {
212 QName name = parser.getName();
213
214 if (XMLConstants.NAMESPACE_ATOM.equals(name.getNamespaceURI())
215 && (TAG_ENTRY.equals(name.getLocalPart()))) {
216 parseEntry(parser);
217 break;
218 } else {
219 throw new CmisInvalidArgumentException("XML is not an Atom entry!");
220 }
221 }
222
223 if (!XMLUtils.next(parser)) {
224 break;
225 }
226 }
227 } catch (XMLStreamException xse) {
228 release();
229 throw xse;
230 } catch (IOException ioe) {
231 release();
232 throw ioe;
233 } catch (RuntimeException re) {
234 release();
235 throw re;
236 } finally {
237 try {
238 parser.close();
239 } catch (XMLStreamException xse) {
240 if (LOG.isWarnEnabled()) {
241 LOG.warn("Parser couldn't be closed: {}", xse.toString(), xse);
242 }
243 }
244 }
245 }
246
247
248
249
250 public void release() {
251 object = null;
252 typeDef = null;
253 bulkUpdate = null;
254 closeAtomContentStream();
255 closeCmisContentStream();
256 }
257
258
259
260
261 private void closeAtomContentStream() {
262 IOUtils.closeQuietly(atomContentStream);
263 atomContentStream = null;
264 }
265
266
267
268
269 private void closeCmisContentStream() {
270 IOUtils.closeQuietly(cmisContentStream);
271 cmisContentStream = null;
272 }
273
274
275
276
277 private void parseEntry(XMLStreamReader parser) throws XMLStreamException, IOException {
278 String atomTitle = null;
279
280 XMLUtils.next(parser);
281
282
283 while (true) {
284 int event = parser.getEventType();
285 if (event == XMLStreamConstants.START_ELEMENT) {
286 QName name = parser.getName();
287
288 if (XMLConstants.NAMESPACE_RESTATOM.equals(name.getNamespaceURI())) {
289 if (TAG_OBJECT.equals(name.getLocalPart())) {
290 parseObject(parser);
291 } else if (TAG_TYPE.equals(name.getLocalPart())) {
292 parseTypeDefinition(parser);
293 } else if (TAG_BULK_UPDATE.equals(name.getLocalPart())) {
294 parseBulkUpdate(parser);
295 } else if (TAG_CONTENT.equals(name.getLocalPart())) {
296 parseCmisContent(parser);
297 } else {
298 XMLUtils.skip(parser);
299 }
300 } else if (XMLConstants.NAMESPACE_ATOM.equals(name.getNamespaceURI())) {
301 if (TAG_CONTENT.equals(name.getLocalPart())) {
302 parseAtomContent(parser);
303 } else if (TAG_TITLE.equals(name.getLocalPart())) {
304 atomTitle = XMLUtils.readText(parser, XMLConstraints.MAX_STRING_LENGTH);
305 } else {
306 XMLUtils.skip(parser);
307 }
308 } else {
309 XMLUtils.skip(parser);
310 }
311 } else if (event == XMLStreamConstants.END_ELEMENT) {
312 break;
313 } else {
314 if (!XMLUtils.next(parser)) {
315 break;
316 }
317 }
318 }
319
320
321 if ((object != null) && (object.getProperties() != null) && (atomTitle != null) && (atomTitle.length() > 0)) {
322 PropertyString nameProperty = new PropertyStringImpl(PropertyIds.NAME, atomTitle);
323 ((PropertiesImpl) object.getProperties()).replaceProperty(nameProperty);
324 }
325 }
326
327
328
329
330 private void parseObject(XMLStreamReader parser) throws XMLStreamException {
331 object = XMLConverter.convertObject(parser);
332 }
333
334
335
336
337 private void parseTypeDefinition(XMLStreamReader parser) throws XMLStreamException {
338 typeDef = XMLConverter.convertTypeDefinition(parser);
339 }
340
341
342
343
344 private void parseBulkUpdate(XMLStreamReader parser) throws XMLStreamException {
345 bulkUpdate = XMLConverter.convertBulkUpdate(parser);
346 }
347
348
349
350
351
352
353
354 private void parseAtomContent(XMLStreamReader parser) throws XMLStreamException, IOException {
355 if (atomContentStream != null) {
356 closeAtomContentStream();
357 throw new CmisInvalidArgumentException("More than one content provided!");
358 }
359
360 if (cmisContentStream != null) {
361
362 XMLUtils.skip(parser);
363 return;
364 }
365
366 atomContentStream = new ContentStreamImpl();
367
368
369 String type = "text";
370 String mimeType = "text/plain";
371 for (int i = 0; i < parser.getAttributeCount(); i++) {
372 QName attrName = parser.getAttributeName(i);
373 if (ATTR_TYPE.equals(attrName.getLocalPart())) {
374 if (parser.getAttributeValue(i) != null) {
375 type = parser.getAttributeValue(i).trim().toLowerCase(Locale.ENGLISH);
376 }
377 } else if (ATTR_SRC.equals(attrName.getLocalPart())) {
378 if (ignoreAtomContentSrc) {
379 atomContentStream = null;
380 XMLUtils.skip(parser);
381 return;
382 }
383 throw new CmisNotSupportedException("External content not supported!");
384 }
385 }
386
387 TempStoreOutputStream tsos = null;
388 if (type.equals("text")) {
389 mimeType = "text/plain";
390 tsos = readContentBytes(parser, mimeType);
391 } else if (type.equals("html")) {
392 mimeType = "text/html";
393 tsos = readContentBytes(parser, mimeType);
394 } else if (type.equals("xhtml")) {
395 mimeType = "application/xhtml+xml";
396 tsos = copy(parser, mimeType);
397 } else if (type.endsWith("/xml") || type.endsWith("+xml")) {
398 mimeType = type;
399 tsos = copy(parser, mimeType);
400 } else if (type.startsWith("text/")) {
401 mimeType = type;
402 tsos = readContentBytes(parser, mimeType);
403 } else {
404 mimeType = type;
405 tsos = readBase64(parser, mimeType, null);
406 }
407
408 atomContentStream.setMimeType(mimeType);
409
410 if (tsos != null) {
411 try {
412 atomContentStream.setStream(tsos.getInputStream());
413 atomContentStream.setLength(BigInteger.valueOf(tsos.getLength()));
414 } catch (IOException e) {
415 tsos.destroy(e);
416 throw e;
417 }
418 }
419 }
420
421
422
423
424 private void parseCmisContent(XMLStreamReader parser) throws XMLStreamException, IOException {
425 closeAtomContentStream();
426 if (cmisContentStream != null) {
427 closeCmisContentStream();
428 throw new CmisInvalidArgumentException("More than one content provided!");
429 }
430
431 cmisContentStream = new ContentStreamImpl();
432
433 XMLUtils.next(parser);
434
435
436 while (true) {
437 int event = parser.getEventType();
438 if (event == XMLStreamConstants.START_ELEMENT) {
439 QName name = parser.getName();
440
441 if (XMLConstants.NAMESPACE_RESTATOM.equals(name.getNamespaceURI())) {
442 if (TAG_MEDIATYPE.equals(name.getLocalPart())) {
443 cmisContentStream.setMimeType(XMLUtils.readText(parser, XMLConstraints.MAX_STRING_LENGTH));
444 } else if (TAG_BASE64.equals(name.getLocalPart())) {
445 TempStoreOutputStream tsos = readBase64(parser, cmisContentStream.getMimeType(),
446 cmisContentStream.getFileName());
447 try {
448 cmisContentStream.setStream(tsos.getInputStream());
449 cmisContentStream.setLength(BigInteger.valueOf(tsos.getLength()));
450 } catch (IOException e) {
451 tsos.destroy(e);
452 throw e;
453 }
454 } else {
455 XMLUtils.skip(parser);
456 }
457 } else if (XMLConstants.NAMESPACE_APACHE_CHEMISTRY.equals(name.getNamespaceURI())) {
458 if (TAG_FILENAME.equals(name.getLocalPart())) {
459 cmisContentStream.setFileName(XMLUtils.readText(parser, XMLConstraints.MAX_STRING_LENGTH));
460 } else {
461 XMLUtils.skip(parser);
462 }
463 } else {
464 XMLUtils.skip(parser);
465 }
466 } else if (event == XMLStreamConstants.END_ELEMENT) {
467 break;
468 } else {
469 if (!XMLUtils.next(parser)) {
470 break;
471 }
472 }
473 }
474
475 XMLUtils.next(parser);
476 }
477
478
479
480
481 private TempStoreOutputStream readContentBytes(XMLStreamReader parser, String mimeType) throws XMLStreamException,
482 IOException {
483 TempStoreOutputStream bufferStream = streamFactory.newOutputStream();
484 bufferStream.setMimeType(mimeType);
485
486 XMLUtils.next(parser);
487
488 try {
489 while (true) {
490 int event = parser.getEventType();
491 if (event == XMLStreamConstants.END_ELEMENT) {
492 break;
493 } else if (event == XMLStreamConstants.CHARACTERS) {
494 String s = parser.getText();
495 if (s != null) {
496 byte[] bytes = IOUtils.toUTF8Bytes(s);
497 bufferStream.write(bytes);
498 cappedStream.deductBytes(bytes.length);
499 }
500 } else if (event == XMLStreamConstants.START_ELEMENT) {
501 bufferStream.destroy(null);
502 throw new CmisInvalidArgumentException("Unexpected tag: " + parser.getName());
503 }
504
505 if (!XMLUtils.next(parser)) {
506 break;
507 }
508 }
509 } catch (XMLStreamException xse) {
510
511 bufferStream.destroy(xse);
512 throw xse;
513 } catch (IOException ioe) {
514
515 bufferStream.destroy(ioe);
516 throw ioe;
517 }
518
519 XMLUtils.next(parser);
520
521 return bufferStream;
522 }
523
524
525
526
527 private TempStoreOutputStream readBase64(XMLStreamReader parser, String mimeType, String filename)
528 throws XMLStreamException, IOException {
529 TempStoreOutputStream bufferStream = streamFactory.newOutputStream();
530 bufferStream.setMimeType(mimeType);
531 bufferStream.setFileName(filename);
532 Base64.OutputStream b64stream = new Base64.OutputStream(bufferStream, Base64.DECODE);
533
534 XMLUtils.next(parser);
535
536 try {
537 while (true) {
538 int event = parser.getEventType();
539 if (event == XMLStreamConstants.END_ELEMENT) {
540 break;
541 } else if (event == XMLStreamConstants.CHARACTERS) {
542 int len = parser.getTextLength();
543 if (len > 0) {
544 char[] chars = parser.getTextCharacters();
545 int offset = parser.getTextStart();
546 for (int i = 0; i < len; i++) {
547
548 b64stream.write(chars[offset + i]);
549 }
550 cappedStream.deductBytes(len);
551 }
552 } else if (event == XMLStreamConstants.START_ELEMENT) {
553 b64stream.close();
554 bufferStream.destroy(null);
555 throw new CmisInvalidArgumentException("Unexpected tag: " + parser.getName());
556 }
557
558 if (!XMLUtils.next(parser)) {
559 break;
560 }
561 }
562
563 b64stream.close();
564 } catch (XMLStreamException xse) {
565
566 bufferStream.destroy(xse);
567 throw xse;
568 } catch (IOException ioe) {
569
570 bufferStream.destroy(ioe);
571 throw ioe;
572 }
573
574 XMLUtils.next(parser);
575
576 return bufferStream;
577 }
578
579
580
581
582 private TempStoreOutputStream copy(XMLStreamReader parser, String mimeType) throws XMLStreamException, IOException {
583
584 TempStoreOutputStream bufferStream = streamFactory.newOutputStream();
585 bufferStream.setMimeType(mimeType);
586
587 try {
588 XMLStreamWriter writer = XMLUtils.createWriter(bufferStream);
589
590 writer.writeStartDocument();
591
592
593 int level = 1;
594 while (XMLUtils.next(parser)) {
595 int event = parser.getEventType();
596 if (event == XMLStreamConstants.START_ELEMENT) {
597 copyStartElement(parser, writer);
598 level++;
599 } else if (event == XMLStreamConstants.CHARACTERS) {
600 writer.writeCharacters(parser.getText());
601 } else if (event == XMLStreamConstants.COMMENT) {
602 writer.writeComment(parser.getText());
603 } else if (event == XMLStreamConstants.CDATA) {
604 writer.writeCData(parser.getText());
605 } else if (event == XMLStreamConstants.END_ELEMENT) {
606 level--;
607 if (level == 0) {
608 break;
609 }
610 writer.writeEndElement();
611 } else {
612 break;
613 }
614 }
615
616 writer.writeEndDocument();
617 writer.flush();
618
619 bufferStream.close();
620 } catch (XMLStreamException xse) {
621
622 bufferStream.destroy(xse);
623 throw xse;
624 } catch (IOException ioe) {
625
626 bufferStream.destroy(ioe);
627 throw ioe;
628 }
629
630 XMLUtils.next(parser);
631
632 return bufferStream;
633 }
634
635
636
637
638 private static void copyStartElement(XMLStreamReader parser, XMLStreamWriter writer) throws XMLStreamException {
639 String namespaceUri = parser.getNamespaceURI();
640 String prefix = parser.getPrefix();
641 String localName = parser.getLocalName();
642
643
644 if (namespaceUri != null) {
645 if ((prefix == null) || (prefix.length() == 0)) {
646 writer.writeStartElement(localName);
647 } else {
648 writer.writeStartElement(prefix, localName, namespaceUri);
649 }
650 } else {
651 writer.writeStartElement(localName);
652 }
653
654
655 for (int i = 0; i < parser.getNamespaceCount(); i++) {
656 addNamespace(writer, parser.getNamespacePrefix(i), parser.getNamespaceURI(i));
657 }
658 addNamespaceIfMissing(writer, prefix, namespaceUri);
659
660
661 for (int i = 0; i < parser.getAttributeCount(); i++) {
662 String attrNamespaceUri = parser.getAttributeNamespace(i);
663 String attrPrefix = parser.getAttributePrefix(i);
664 String attrName = parser.getAttributeLocalName(i);
665 String attrValue = parser.getAttributeValue(i);
666
667 if ((attrNamespaceUri == null) || (attrNamespaceUri.trim().length() == 0)) {
668 writer.writeAttribute(attrName, attrValue);
669 } else if ((attrPrefix == null) || (attrPrefix.trim().length() == 0)) {
670 writer.writeAttribute(attrNamespaceUri, attrName, attrValue);
671 } else {
672 addNamespaceIfMissing(writer, attrPrefix, attrNamespaceUri);
673 writer.writeAttribute(attrPrefix, attrNamespaceUri, attrName, attrValue);
674 }
675 }
676 }
677
678
679
680
681 @SuppressWarnings("unchecked")
682 private static void addNamespaceIfMissing(XMLStreamWriter writer, String prefix, String namespaceUri)
683 throws XMLStreamException {
684 if ((namespaceUri == null) || (namespaceUri.trim().length() == 0)) {
685 return;
686 }
687
688 if (prefix == null) {
689 prefix = "";
690 }
691
692 Iterator<String> iter = writer.getNamespaceContext().getPrefixes(namespaceUri);
693 if (iter == null) {
694 return;
695 }
696
697 while (iter.hasNext()) {
698 String p = iter.next();
699 if ((p != null) && (p.equals(prefix))) {
700 return;
701 }
702 }
703
704 addNamespace(writer, prefix, namespaceUri);
705 }
706
707
708
709
710 private static void addNamespace(XMLStreamWriter writer, String prefix, String namespaceUri)
711 throws XMLStreamException {
712 if ((prefix == null) || (prefix.trim().length() == 0)) {
713 writer.setDefaultNamespace(namespaceUri);
714 writer.writeDefaultNamespace(namespaceUri);
715 } else {
716 writer.setPrefix(prefix, namespaceUri);
717 writer.writeNamespace(prefix, namespaceUri);
718 }
719 }
720 }