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