View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   * http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.chemistry.opencmis.client.bindings.spi.atompub;
20  
21  import static org.apache.chemistry.opencmis.client.bindings.spi.atompub.CmisAtomPubConstants.TAG_ATOM_ID;
22  import static org.apache.chemistry.opencmis.client.bindings.spi.atompub.CmisAtomPubConstants.TAG_ATOM_TITLE;
23  import static org.apache.chemistry.opencmis.client.bindings.spi.atompub.CmisAtomPubConstants.TAG_ATOM_UPDATED;
24  import static org.apache.chemistry.opencmis.client.bindings.spi.atompub.CmisAtomPubConstants.TAG_CONTENT;
25  import static org.apache.chemistry.opencmis.client.bindings.spi.atompub.CmisAtomPubConstants.TAG_CONTENT_BASE64;
26  import static org.apache.chemistry.opencmis.client.bindings.spi.atompub.CmisAtomPubConstants.TAG_CONTENT_FILENAME;
27  import static org.apache.chemistry.opencmis.client.bindings.spi.atompub.CmisAtomPubConstants.TAG_CONTENT_MEDIATYPE;
28  
29  import java.io.BufferedInputStream;
30  import java.io.ByteArrayInputStream;
31  import java.io.IOException;
32  import java.io.InputStream;
33  import java.io.OutputStream;
34  import java.util.GregorianCalendar;
35  import java.util.TimeZone;
36  
37  import javax.xml.stream.XMLStreamException;
38  import javax.xml.stream.XMLStreamWriter;
39  
40  import org.apache.chemistry.opencmis.commons.PropertyIds;
41  import org.apache.chemistry.opencmis.commons.data.ContentStream;
42  import org.apache.chemistry.opencmis.commons.data.ObjectData;
43  import org.apache.chemistry.opencmis.commons.data.PropertyData;
44  import org.apache.chemistry.opencmis.commons.data.PropertyString;
45  import org.apache.chemistry.opencmis.commons.definitions.TypeDefinition;
46  import org.apache.chemistry.opencmis.commons.enums.CmisVersion;
47  import org.apache.chemistry.opencmis.commons.exceptions.CmisInvalidArgumentException;
48  import org.apache.chemistry.opencmis.commons.impl.Base64;
49  import org.apache.chemistry.opencmis.commons.impl.DateTimeHelper;
50  import org.apache.chemistry.opencmis.commons.impl.XMLConstants;
51  import org.apache.chemistry.opencmis.commons.impl.XMLConverter;
52  import org.apache.chemistry.opencmis.commons.impl.XMLUtils;
53  import org.apache.chemistry.opencmis.commons.impl.dataobjects.BulkUpdateImpl;
54  
55  /**
56   * Writes a CMIS Atom entry to an output stream.
57   */
58  public class AtomEntryWriter {
59  
60      private static final int BUFFER_SIZE = 64 * 1024;
61  
62      private final CmisVersion cmisVersion;
63      private final ObjectData object;
64      private final ContentStream contentStream;
65      private final InputStream stream;
66      private final TypeDefinition typeDef;
67      private final BulkUpdateImpl bulkUpdate;
68  
69      /**
70       * Constructor for objects.
71       */
72      public AtomEntryWriter(ObjectData object, CmisVersion cmisVersion) {
73          this(object, cmisVersion, null);
74      }
75  
76      /**
77       * Constructor for objects.
78       */
79      public AtomEntryWriter(ObjectData object, CmisVersion cmisVersion, ContentStream contentStream) {
80          if ((object == null) || (object.getProperties() == null)) {
81              throw new CmisInvalidArgumentException("Object and properties must not be null!");
82          }
83  
84          if ((contentStream != null) && (contentStream.getMimeType() == null)) {
85              throw new CmisInvalidArgumentException("Media type must be set if a stream is present!");
86          }
87  
88          this.object = object;
89          this.cmisVersion = cmisVersion;
90          this.contentStream = contentStream;
91          if (contentStream != null && contentStream.getStream() != null) {
92              InputStream in = contentStream.getStream();
93  
94              // avoid double buffering
95              if (!(in instanceof BufferedInputStream) && !(in instanceof ByteArrayInputStream)) {
96                  stream = new BufferedInputStream(in, BUFFER_SIZE);
97              } else {
98                  stream = in;
99              }
100         } else {
101             stream = null;
102         }
103         this.typeDef = null;
104         this.bulkUpdate = null;
105     }
106 
107     /**
108      * Constructor for types.
109      */
110     public AtomEntryWriter(TypeDefinition type, CmisVersion cmisVersion) {
111         if (type == null) {
112             throw new CmisInvalidArgumentException("Type must not be null!");
113         }
114 
115         this.typeDef = type;
116         this.cmisVersion = cmisVersion;
117         this.object = null;
118         this.contentStream = null;
119         this.stream = null;
120         this.bulkUpdate = null;
121     }
122 
123     /**
124      * Constructor for bulk updates.
125      */
126     public AtomEntryWriter(BulkUpdateImpl bulkUpdate) {
127         if (bulkUpdate == null) {
128             throw new CmisInvalidArgumentException("Bulk update data must not be null!");
129         }
130 
131         this.bulkUpdate = bulkUpdate;
132         this.typeDef = null;
133         this.cmisVersion = CmisVersion.CMIS_1_1;
134         this.object = null;
135         this.contentStream = null;
136         this.stream = null;
137     }
138 
139     /**
140      * Writes the entry to an output stream.
141      */
142     public void write(OutputStream out) throws XMLStreamException, IOException {
143         XMLStreamWriter writer = XMLUtils.createWriter(out);
144 
145         XMLUtils.startXmlDocument(writer);
146 
147         writer.writeStartElement(XMLConstants.PREFIX_ATOM, "entry", XMLConstants.NAMESPACE_ATOM);
148 
149         writer.writeNamespace(XMLConstants.PREFIX_ATOM, XMLConstants.NAMESPACE_ATOM);
150         writer.writeNamespace(XMLConstants.PREFIX_CMIS, XMLConstants.NAMESPACE_CMIS);
151         writer.writeNamespace(XMLConstants.PREFIX_RESTATOM, XMLConstants.NAMESPACE_RESTATOM);
152         if (contentStream != null && contentStream.getFileName() != null) {
153             writer.writeNamespace(XMLConstants.PREFIX_APACHE_CHEMISTY, XMLConstants.NAMESPACE_APACHE_CHEMISTRY);
154         }
155 
156         // atom:id
157         XMLUtils.write(writer, XMLConstants.PREFIX_ATOM, XMLConstants.NAMESPACE_ATOM, TAG_ATOM_ID,
158                 "urn:uuid:00000000-0000-0000-0000-00000000000");
159 
160         // atom:title
161         XMLUtils.write(writer, XMLConstants.PREFIX_ATOM, XMLConstants.NAMESPACE_ATOM, TAG_ATOM_TITLE, getTitle());
162 
163         // atom:updated
164         XMLUtils.write(writer, XMLConstants.PREFIX_ATOM, XMLConstants.NAMESPACE_ATOM, TAG_ATOM_UPDATED,
165                 new GregorianCalendar(DateTimeHelper.GMT));
166 
167         // content
168         if (stream != null) {
169             writer.writeStartElement(XMLConstants.PREFIX_RESTATOM, TAG_CONTENT, XMLConstants.NAMESPACE_RESTATOM);
170 
171             XMLUtils.write(writer, XMLConstants.PREFIX_RESTATOM, XMLConstants.NAMESPACE_RESTATOM,
172                     TAG_CONTENT_MEDIATYPE, contentStream.getMimeType());
173 
174             if (contentStream.getFileName() != null) {
175                 XMLUtils.write(writer, XMLConstants.PREFIX_APACHE_CHEMISTY, XMLConstants.NAMESPACE_APACHE_CHEMISTRY,
176                         TAG_CONTENT_FILENAME, contentStream.getFileName());
177             }
178 
179             writer.writeStartElement(XMLConstants.PREFIX_RESTATOM, TAG_CONTENT_BASE64, XMLConstants.NAMESPACE_RESTATOM);
180             writeContent(writer);
181             writer.writeEndElement();
182 
183             writer.writeEndElement();
184         }
185 
186         // object
187         if (object != null) {
188             XMLConverter.writeObject(writer, cmisVersion, XMLConstants.NAMESPACE_RESTATOM, object);
189         }
190 
191         // type
192         if (typeDef != null) {
193             XMLConverter.writeTypeDefinition(writer, cmisVersion, XMLConstants.NAMESPACE_RESTATOM, typeDef);
194         }
195 
196         // bulk update
197         if (bulkUpdate != null) {
198             XMLConverter.writeBulkUpdate(writer, XMLConstants.NAMESPACE_RESTATOM, bulkUpdate);
199         }
200 
201         // end entry
202         writer.writeEndElement();
203 
204         // end document
205         XMLUtils.endXmlDocument(writer);
206     }
207 
208     // ---- internal ----
209 
210     private String getTitle() {
211         String result = "";
212 
213         if (object != null) {
214             PropertyData<?> nameProperty = object.getProperties().getProperties().get(PropertyIds.NAME);
215             if (nameProperty instanceof PropertyString) {
216                 result = ((PropertyString) nameProperty).getFirstValue();
217             }
218         }
219 
220         if (typeDef != null) {
221             if (typeDef.getDisplayName() != null) {
222                 result = typeDef.getDisplayName();
223             }
224         }
225 
226         if (bulkUpdate != null) {
227             result = "Bulk Update Properties";
228         }
229 
230         return result;
231     }
232 
233     private void writeContent(XMLStreamWriter writer) throws XMLStreamException, IOException {
234         @SuppressWarnings("resource")
235         Base64.InputStream b64stream = new Base64.InputStream(stream, Base64.ENCODE);
236 
237         char[] buffer = new char[BUFFER_SIZE];
238         int pos = 0;
239         int b;
240         while ((b = b64stream.read()) > -1) {
241             buffer[pos++] = (char) (b & 0xFF);
242             if (pos == buffer.length) {
243                 writer.writeCharacters(buffer, 0, buffer.length);
244                 pos = 0;
245             }
246         }
247         if (pos > 0) {
248             writer.writeCharacters(buffer, 0, pos);
249         }
250     }
251 }