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 java.io.IOException;
22  import java.io.OutputStream;
23  import java.math.BigInteger;
24  import java.util.ArrayList;
25  import java.util.HashMap;
26  import java.util.List;
27  import java.util.Map;
28  
29  import javax.xml.stream.XMLStreamException;
30  
31  import org.apache.chemistry.opencmis.client.bindings.spi.BindingSession;
32  import org.apache.chemistry.opencmis.client.bindings.spi.atompub.objects.AtomElement;
33  import org.apache.chemistry.opencmis.client.bindings.spi.atompub.objects.AtomEntry;
34  import org.apache.chemistry.opencmis.client.bindings.spi.atompub.objects.AtomFeed;
35  import org.apache.chemistry.opencmis.client.bindings.spi.atompub.objects.AtomLink;
36  import org.apache.chemistry.opencmis.client.bindings.spi.http.Output;
37  import org.apache.chemistry.opencmis.client.bindings.spi.http.Response;
38  import org.apache.chemistry.opencmis.commons.data.ExtensionsData;
39  import org.apache.chemistry.opencmis.commons.data.RepositoryInfo;
40  import org.apache.chemistry.opencmis.commons.definitions.TypeDefinition;
41  import org.apache.chemistry.opencmis.commons.definitions.TypeDefinitionContainer;
42  import org.apache.chemistry.opencmis.commons.definitions.TypeDefinitionList;
43  import org.apache.chemistry.opencmis.commons.exceptions.CmisConnectionException;
44  import org.apache.chemistry.opencmis.commons.exceptions.CmisInvalidArgumentException;
45  import org.apache.chemistry.opencmis.commons.exceptions.CmisObjectNotFoundException;
46  import org.apache.chemistry.opencmis.commons.impl.Constants;
47  import org.apache.chemistry.opencmis.commons.impl.UrlBuilder;
48  import org.apache.chemistry.opencmis.commons.impl.dataobjects.TypeDefinitionContainerImpl;
49  import org.apache.chemistry.opencmis.commons.impl.dataobjects.TypeDefinitionListImpl;
50  import org.apache.chemistry.opencmis.commons.spi.RepositoryService;
51  
52  /**
53   * Repository Service AtomPub client.
54   */
55  public class RepositoryServiceImpl extends AbstractAtomPubService implements RepositoryService {
56  
57      /**
58       * Constructor.
59       */
60      public RepositoryServiceImpl(BindingSession session) {
61          setSession(session);
62      }
63  
64      @Override
65      public List<RepositoryInfo> getRepositoryInfos(ExtensionsData extension) {
66          return getRepositoriesInternal(null);
67      }
68  
69      @Override
70      public RepositoryInfo getRepositoryInfo(String repositoryId, ExtensionsData extension) {
71          List<RepositoryInfo> repositoryInfos = getRepositoriesInternal(repositoryId);
72  
73          if (repositoryInfos.isEmpty()) {
74              throw new CmisObjectNotFoundException("Repository '" + repositoryId + "' not found!");
75          }
76  
77          // find the repository
78          for (RepositoryInfo info : repositoryInfos) {
79              if (info.getId() == null) {
80                  continue;
81              }
82  
83              if (info.getId().equals(repositoryId)) {
84                  return info;
85              }
86          }
87  
88          throw new CmisObjectNotFoundException("Repository '" + repositoryId + "' not found!");
89      }
90  
91      @Override
92      public TypeDefinition getTypeDefinition(String repositoryId, String typeId, ExtensionsData extension) {
93          return getTypeDefinitionInternal(repositoryId, typeId);
94      }
95  
96      @Override
97      public TypeDefinitionList getTypeChildren(String repositoryId, String typeId, Boolean includePropertyDefinitions,
98              BigInteger maxItems, BigInteger skipCount, ExtensionsData extension) {
99          TypeDefinitionListImpl result = new TypeDefinitionListImpl();
100 
101         // find the link
102         String link = null;
103         if (typeId == null) {
104             link = loadCollection(repositoryId, Constants.COLLECTION_TYPES);
105         } else {
106             link = loadTypeLink(repositoryId, typeId, Constants.REL_DOWN, Constants.MEDIATYPE_CHILDREN);
107         }
108 
109         if (link == null) {
110             throw new CmisObjectNotFoundException("Unknown repository or type!");
111         }
112 
113         UrlBuilder url = new UrlBuilder(link);
114         url.addParameter(Constants.PARAM_PROPERTY_DEFINITIONS, includePropertyDefinitions);
115         url.addParameter(Constants.PARAM_MAX_ITEMS, maxItems);
116         url.addParameter(Constants.PARAM_SKIP_COUNT, skipCount);
117 
118         // read and parse
119         Response resp = read(url);
120         AtomFeed feed = parse(resp.getStream(), AtomFeed.class);
121 
122         // handle top level
123         for (AtomElement element : feed.getElements()) {
124             if (element.getObject() instanceof AtomLink) {
125                 if (isNextLink(element)) {
126                     result.setHasMoreItems(Boolean.TRUE);
127                 }
128             } else if (isInt(NAME_NUM_ITEMS, element)) {
129                 result.setNumItems((BigInteger) element.getObject());
130             }
131         }
132 
133         result.setList(new ArrayList<TypeDefinition>(feed.getEntries().size()));
134 
135         // get the children
136         if (!feed.getEntries().isEmpty()) {
137             for (AtomEntry entry : feed.getEntries()) {
138                 TypeDefinition child = null;
139 
140                 lockTypeLinks();
141                 try {
142                     // walk through the entry
143                     for (AtomElement element : entry.getElements()) {
144                         if (element.getObject() instanceof AtomLink) {
145                             addTypeLink(repositoryId, entry.getId(), (AtomLink) element.getObject());
146                         } else if (element.getObject() instanceof TypeDefinition) {
147                             child = (TypeDefinition) element.getObject();
148                         }
149                     }
150                 } finally {
151                     unlockTypeLinks();
152                 }
153 
154                 if (child != null) {
155                     result.getList().add(child);
156                 }
157             }
158         }
159 
160         return result;
161     }
162 
163     @Override
164     public List<TypeDefinitionContainer> getTypeDescendants(String repositoryId, String typeId, BigInteger depth,
165             Boolean includePropertyDefinitions, ExtensionsData extension) {
166         List<TypeDefinitionContainer> result = new ArrayList<TypeDefinitionContainer>();
167 
168         // find the link
169         String link = null;
170         if (typeId == null) {
171             link = loadRepositoryLink(repositoryId, Constants.REP_REL_TYPEDESC);
172         } else {
173             link = loadTypeLink(repositoryId, typeId, Constants.REL_DOWN, Constants.MEDIATYPE_DESCENDANTS);
174         }
175 
176         if (link == null) {
177             throw new CmisObjectNotFoundException("Unknown repository or type!");
178         }
179 
180         UrlBuilder url = new UrlBuilder(link);
181         url.addParameter(Constants.PARAM_DEPTH, depth);
182         url.addParameter(Constants.PARAM_PROPERTY_DEFINITIONS, includePropertyDefinitions);
183 
184         // read and parse
185         Response resp = read(url);
186         AtomFeed feed = parse(resp.getStream(), AtomFeed.class);
187 
188         // process tree
189         addTypeDescendantsLevel(repositoryId, feed, result);
190 
191         return result;
192     }
193 
194     /**
195      * Adds type descendants level recursively.
196      */
197     private void addTypeDescendantsLevel(String repositoryId, AtomFeed feed, List<TypeDefinitionContainer> containerList) {
198         if (feed == null || feed.getEntries().isEmpty()) {
199             return;
200         }
201 
202         // walk through the feed
203         for (AtomEntry entry : feed.getEntries()) {
204             TypeDefinitionContainerImpl childContainer = null;
205             List<TypeDefinitionContainer> childContainerList = new ArrayList<TypeDefinitionContainer>();
206 
207             // walk through the entry
208             lockTypeLinks();
209             try {
210                 for (AtomElement element : entry.getElements()) {
211                     if (element.getObject() instanceof AtomLink) {
212                         addTypeLink(repositoryId, entry.getId(), (AtomLink) element.getObject());
213                     } else if (element.getObject() instanceof TypeDefinition) {
214                         childContainer = new TypeDefinitionContainerImpl((TypeDefinition) element.getObject());
215                     } else if (element.getObject() instanceof AtomFeed) {
216                         addTypeDescendantsLevel(repositoryId, (AtomFeed) element.getObject(), childContainerList);
217                     }
218                 }
219             } finally {
220                 unlockTypeLinks();
221             }
222 
223             if (childContainer != null) {
224                 childContainer.setChildren(childContainerList);
225                 containerList.add(childContainer);
226             }
227         }
228     }
229 
230     @Override
231     public TypeDefinition createType(String repositoryId, TypeDefinition type, ExtensionsData extension) {
232         if (type == null) {
233             throw new CmisInvalidArgumentException("Type definition must be set!");
234         }
235 
236         String parentId = type.getParentTypeId();
237         if (parentId == null) {
238             throw new CmisInvalidArgumentException("Type definition has no parent type ID!");
239         }
240 
241         // find the link
242         String link = loadTypeLink(repositoryId, parentId, Constants.REL_DOWN, Constants.MEDIATYPE_CHILDREN);
243 
244         if (link == null) {
245             throw new CmisObjectNotFoundException("Unknown repository or parent type!");
246         }
247 
248         // set up writer
249         final AtomEntryWriter entryWriter = new AtomEntryWriter(type, getCmisVersion(repositoryId));
250 
251         // post the new type definition
252         Response resp = post(new UrlBuilder(link), Constants.MEDIATYPE_ENTRY, new Output() {
253             @Override
254             public void write(OutputStream out) throws XMLStreamException, IOException {
255                 entryWriter.write(out);
256             }
257         });
258 
259         // parse the response
260         AtomEntry entry = parse(resp.getStream(), AtomEntry.class);
261 
262         // we expect a CMIS entry
263         if (entry.getId() == null) {
264             throw new CmisConnectionException("Received Atom entry is not a CMIS entry!");
265         }
266 
267         lockTypeLinks();
268         TypeDefinition result = null;
269         try {
270             // clean up cache
271             removeTypeLinks(repositoryId, entry.getId());
272 
273             // walk through the entry
274             for (AtomElement element : entry.getElements()) {
275                 if (element.getObject() instanceof AtomLink) {
276                     addTypeLink(repositoryId, entry.getId(), (AtomLink) element.getObject());
277                 } else if (element.getObject() instanceof TypeDefinition) {
278                     result = (TypeDefinition) element.getObject();
279                 }
280             }
281         } finally {
282             unlockTypeLinks();
283         }
284 
285         return result;
286     }
287 
288     @Override
289     public TypeDefinition updateType(String repositoryId, TypeDefinition type, ExtensionsData extension) {
290         if (type == null) {
291             throw new CmisInvalidArgumentException("Type definition must be set!");
292         }
293 
294         String typeId = type.getId();
295         if (typeId == null) {
296             throw new CmisInvalidArgumentException("Type definition has no type ID!");
297         }
298 
299         // find the link
300         Map<String, Object> parameters = new HashMap<String, Object>();
301         parameters.put(Constants.PARAM_ID, typeId);
302 
303         String link = loadTemplateLink(repositoryId, Constants.TEMPLATE_TYPE_BY_ID, parameters);
304         if (link == null) {
305             throw new CmisObjectNotFoundException("Unknown repository or type!");
306         }
307 
308         // set up writer
309         final AtomEntryWriter entryWriter = new AtomEntryWriter(type, getCmisVersion(repositoryId));
310 
311         // post the new type definition
312         Response resp = put(new UrlBuilder(link), Constants.MEDIATYPE_ENTRY, new Output() {
313             @Override
314             public void write(OutputStream out) throws XMLStreamException, IOException {
315                 entryWriter.write(out);
316             }
317         });
318 
319         // parse the response
320         AtomEntry entry = parse(resp.getStream(), AtomEntry.class);
321 
322         // we expect a CMIS entry
323         if (entry.getId() == null) {
324             throw new CmisConnectionException("Received Atom entry is not a CMIS entry!");
325         }
326 
327         lockTypeLinks();
328         TypeDefinition result = null;
329         try {
330             // clean up cache
331             removeTypeLinks(repositoryId, entry.getId());
332 
333             // walk through the entry
334             for (AtomElement element : entry.getElements()) {
335                 if (element.getObject() instanceof AtomLink) {
336                     addTypeLink(repositoryId, entry.getId(), (AtomLink) element.getObject());
337                 } else if (element.getObject() instanceof TypeDefinition) {
338                     result = (TypeDefinition) element.getObject();
339                 }
340             }
341         } finally {
342             unlockTypeLinks();
343         }
344 
345         return result;
346     }
347 
348     @Override
349     public void deleteType(String repositoryId, String typeId, ExtensionsData extension) {
350         Map<String, Object> parameters = new HashMap<String, Object>();
351         parameters.put(Constants.PARAM_ID, typeId);
352 
353         String link = loadTemplateLink(repositoryId, Constants.TEMPLATE_TYPE_BY_ID, parameters);
354         if (link == null) {
355             throw new CmisObjectNotFoundException("Unknown repository!");
356         }
357 
358         delete(new UrlBuilder(link));
359     }
360 }