]> git.aero2k.de Git - tmp/jakarta-migration.git/blob
caa828e7f0556f60603bdcc6f23bae2fecb7ae42
[tmp/jakarta-migration.git] /
1 /**
2  *  This document is a part of the source code and related artifacts
3  *  for CollectionSpace, an open source collections management system
4  *  for museums and related institutions:
5
6  *  http://www.collectionspace.org
7  *  http://wiki.collectionspace.org
8
9  *  Copyright 2009 University of California at Berkeley
10
11  *  Licensed under the Educational Community License (ECL), Version 2.0.
12  *  You may not use this file except in compliance with this License.
13
14  *  You may obtain a copy of the ECL 2.0 License at
15
16  *  https://source.collectionspace.org/collection-space/LICENSE.txt
17
18  *  Unless required by applicable law or agreed to in writing, software
19  *  distributed under the License is distributed on an "AS IS" BASIS,
20  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21  *  See the License for the specific language governing permissions and
22  *  limitations under the License.
23  */
24 package org.collectionspace.services.vocabulary;
25
26 import org.collectionspace.services.client.IClientQueryParams;
27 import org.collectionspace.services.client.PayloadInputPart;
28 import org.collectionspace.services.client.PoxPayload;
29 import org.collectionspace.services.client.PoxPayloadIn;
30 import org.collectionspace.services.client.PoxPayloadOut;
31 import org.collectionspace.services.client.VocabularyClient;
32 import org.collectionspace.services.client.workflow.WorkflowClient;
33 import org.collectionspace.services.common.CSWebApplicationException;
34 import org.collectionspace.services.common.ResourceMap;
35 import org.collectionspace.services.common.ServiceMessages;
36 import org.collectionspace.services.common.UriInfoWrapper;
37 import org.collectionspace.services.common.api.Tools;
38 import org.collectionspace.services.common.context.ServiceBindingUtils;
39 import org.collectionspace.services.common.context.ServiceContext;
40 import org.collectionspace.services.common.document.DocumentException;
41 import org.collectionspace.services.common.document.DocumentHandler;
42 import org.collectionspace.services.common.document.JaxbUtils;
43 import org.collectionspace.services.common.repository.RepositoryClient;
44 import org.collectionspace.services.common.vocabulary.AuthorityResource;
45 import org.collectionspace.services.common.vocabulary.AuthorityServiceUtils;
46 import org.collectionspace.services.common.vocabulary.RefNameServiceUtils;
47 import org.collectionspace.services.common.vocabulary.RefNameServiceUtils.Specifier;
48 import org.collectionspace.services.common.vocabulary.RefNameServiceUtils.SpecifierForm;
49 import org.collectionspace.services.jaxb.AbstractCommonList;
50 import org.collectionspace.services.jaxb.AbstractCommonList.ListItem;
51 import org.collectionspace.services.nuxeo.client.java.CoreSessionInterface;
52 import org.collectionspace.services.nuxeo.client.java.NuxeoRepositoryClientImpl;
53 import org.collectionspace.services.vocabulary.nuxeo.VocabularyItemDocumentModelHandler;
54 import org.collectionspace.services.workflow.WorkflowCommon;
55 import org.nuxeo.ecm.core.api.DocumentModel;
56 import org.nuxeo.ecm.core.api.DocumentModelList;
57 import org.slf4j.Logger;
58 import org.slf4j.LoggerFactory;
59 import org.w3c.dom.Element;
60
61 import javax.ws.rs.GET;
62 import javax.ws.rs.POST;
63 import javax.ws.rs.PUT;
64 import javax.ws.rs.Path;
65 import javax.ws.rs.PathParam;
66 import javax.ws.rs.core.Context;
67 import javax.ws.rs.core.MultivaluedMap;
68 import javax.ws.rs.core.Request;
69 import javax.ws.rs.core.Response;
70 import javax.ws.rs.core.UriBuilder;
71 import javax.ws.rs.core.UriInfo;
72
73 @Path("/" + VocabularyClient.SERVICE_PATH_COMPONENT)
74 public class VocabularyResource extends 
75         AuthorityResource<VocabulariesCommon, VocabularyItemDocumentModelHandler> {
76
77         private enum Method {
78         POST, PUT;
79     }
80         
81     private final static String vocabularyServiceName = VocabularyClient.SERVICE_PATH_COMPONENT;
82
83         private final static String VOCABULARIES_COMMON = "vocabularies_common";
84     
85     private final static String vocabularyItemServiceName = "vocabularyitems";
86         private final static String VOCABULARYITEMS_COMMON = "vocabularyitems_common";
87     
88     final Logger logger = LoggerFactory.getLogger(VocabularyResource.class);
89
90         public VocabularyResource() {
91                 super(VocabulariesCommon.class, VocabularyResource.class,
92                                 VOCABULARIES_COMMON, VOCABULARYITEMS_COMMON);
93         }
94
95         @POST
96     @Override
97     public Response createAuthority(
98                 @Context ResourceMap resourceMap,
99                 @Context UriInfo uriInfo,
100                 String xmlPayload) {
101         //
102         // Requests to create new authorities come in on new threads. Unfortunately, we need to synchronize those threads on this block because, as of 8/27/2015, we can't seem to get Nuxeo
103         // transaction code to deal with a database level UNIQUE constraint violations on the 'shortidentifier' column of the vocabularies_common table.
104         // Therefore, to prevent having multiple authorities with the same shortid, we need to synchronize
105         // the code that creates new authorities.  The authority document model handler will first check for authorities with the same short id before
106         // trying to create a new authority.
107         //
108         synchronized(AuthorityResource.class) {
109                 try {
110                     PoxPayloadIn input = new PoxPayloadIn(xmlPayload);
111                     ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(input);
112                                 RepositoryClient<PoxPayloadIn, PoxPayloadOut> repoClient = this.getRepositoryClient(ctx);
113                                 
114                                 CoreSessionInterface repoSession = repoClient.getRepositorySession(ctx);
115                                 try {
116                             DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createDocumentHandler(ctx);                          
117                             String csid = repoClient.create(ctx, handler);
118                             handleItemsPayload(Method.POST, repoSession, csid, resourceMap, uriInfo, input);
119                             UriBuilder path = UriBuilder.fromResource(resourceClass);
120                             path.path("" + csid);
121                             Response response = Response.created(path.build()).build();
122                             return response;
123                     } catch (Throwable t) {
124                         repoSession.setTransactionRollbackOnly();
125                         throw t;
126                     } finally {
127                         repoClient.releaseRepositorySession(ctx, repoSession);
128                     }
129                 } catch (Exception e) {
130                     throw bigReThrow(e, ServiceMessages.CREATE_FAILED);
131                 }
132         }
133     }
134         
135     @PUT
136     @Path("{csid}")
137     @Override
138     public byte[] updateAuthority(
139                 @Context ResourceMap resourceMap,
140                 @Context UriInfo uriInfo,
141             @PathParam("csid") String specifier,
142             String xmlPayload) {
143         PoxPayloadOut result = null;
144         try {
145             PoxPayloadIn theUpdate = new PoxPayloadIn(xmlPayload);
146             Specifier spec = Specifier.getSpecifier(specifier, "updateAuthority", "UPDATE");
147             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(theUpdate);
148                         RepositoryClient<PoxPayloadIn, PoxPayloadOut> repoClient = this.getRepositoryClient(ctx);
149
150                         CoreSessionInterface repoSession = repoClient.getRepositorySession(ctx);
151                         try {
152                     DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createDocumentHandler(ctx);
153                     String csid;
154                     if (spec.form == SpecifierForm.CSID) {
155                         csid = spec.value;
156                     } else {
157                         String whereClause = RefNameServiceUtils.buildWhereForAuthByName(authorityCommonSchemaName, spec.value);
158                         csid = getRepositoryClient(ctx).findDocCSID(null, ctx, whereClause);
159                     }
160                     getRepositoryClient(ctx).update(ctx, csid, handler);
161                     handleItemsPayload(Method.PUT, repoSession, csid, resourceMap, uriInfo, theUpdate);
162                     result = ctx.getOutput();
163             } catch (Throwable t) {
164                 repoSession.setTransactionRollbackOnly();
165                 throw t;
166             } finally {
167                 repoClient.releaseRepositorySession(ctx, repoSession);
168             }
169         } catch (Exception e) {
170             throw bigReThrow(e, ServiceMessages.UPDATE_FAILED);
171         }
172         return result.getBytes();
173     }
174     
175     private boolean handleItemsPayload(
176                 Method method,
177                 CoreSessionInterface repoSession,
178                 String parentIdentifier,
179                 ResourceMap resourceMap,
180                 UriInfo uriInfo,
181                 PoxPayloadIn input) throws Exception {
182         boolean result = true;
183         
184         PayloadInputPart abstractCommonListPart  = input.getPart(PoxPayload.ABSTRACT_COMMON_LIST_ROOT_ELEMENT_LABEL);
185         if (abstractCommonListPart != null) {
186                 AbstractCommonList itemsList = (AbstractCommonList) abstractCommonListPart.getBody();
187                 for (ListItem item : itemsList.getListItem()) {
188                         String errMsg = null;
189                         boolean success = true;
190                         Response response = null;
191                         PoxPayloadOut payloadOut = null;
192                         PoxPayloadIn itemXmlPayload = getItemXmlPayload(item);
193                         switch (method) {
194                                 case POST:
195                                         response = this.createAuthorityItem(repoSession, resourceMap, uriInfo, parentIdentifier, itemXmlPayload);
196                                         if (response.getStatus() != Response.Status.CREATED.getStatusCode()) {
197                                                 success = false;
198                                                 errMsg = String.format("Could not create the term list payload of vocabuary '%s'.", parentIdentifier);
199                                         }
200                                         break;
201                                 case PUT:
202                                         String itemSpecifier = getSpecifier(item);
203                                         if (itemSpecifier != null) {
204                                                 payloadOut = updateAuthorityItem(repoSession, resourceMap, uriInfo, parentIdentifier, itemSpecifier, itemXmlPayload);
205                                                 if (payloadOut == null) {
206                                                         success = false;
207                                                         errMsg = String.format("Could not update the term list payload of vocabuary '%s'.", parentIdentifier);
208                                                 }
209                                         } else {
210                                                 success = false;
211                                                 errMsg = String.format("Could not update the term list payload of vocabuary '%s' because one of the item is missing a CSID or short identifier value.",
212                                                                 parentIdentifier);
213                                         }
214                                         break;                                  
215                         }
216                         //
217                         // Throw an exception as soon as we have problems with any item
218                         //
219                         if (success == false) {
220                                         throw new DocumentException(errMsg);
221                         }
222                 }
223         }
224         
225         return result;
226         }    
227     
228     /**
229      * We'll return null if we can create a specifier from the list item.
230      * 
231      * @param item
232      * @return
233      */
234     private String getSpecifier(ListItem item) {
235                 String result = null;
236
237                 String csid = null;
238                 for (Element ele : item.getAny()) {
239                         String fieldName = ele.getTagName();
240                         String fieldValue = ele.getTextContent();
241                         if (fieldName.equalsIgnoreCase("csid")) {
242                                 result = csid = fieldValue;
243                                 break;
244                         }
245                 }
246                 
247                 if (csid == null) {
248                         String shortId = null;
249                         for (Element ele : item.getAny()) {
250                                 String fieldName = ele.getTagName();
251                                 String fieldValue = ele.getTextContent();
252                                 if (fieldName.equalsIgnoreCase("shortIdentifier")) {
253                                         shortId = fieldValue;
254                                         break;
255                                 }
256                         }
257                         
258                         if (shortId != null) {
259                                 result = Specifier.createShortIdURNValue(shortId);
260                         }
261                 }
262
263                 return result;
264         }
265
266         /**
267      * This is very brittle.  If the class VocabularyitemsCommon changed with new fields we'd have to
268      * update this method.
269      * 
270      * @param item
271      * @return
272      * @throws DocumentException 
273      */
274         private PoxPayloadIn getItemXmlPayload(ListItem item) throws DocumentException {
275                 PoxPayloadIn result = null;
276
277                 VocabularyitemsCommon vocabularyItem = new VocabularyitemsCommon();
278                 for (Element ele : item.getAny()) {
279                         String fieldName = ele.getTagName();
280                         String fieldValue = ele.getTextContent();
281                         switch (fieldName) {
282                                 case "displayName":
283                                         vocabularyItem.setDisplayName(fieldValue);
284                                         break;
285                                         
286                                 case "shortIdentifier":
287                                         vocabularyItem.setShortIdentifier(fieldValue);
288                                         break;
289                                         
290                                 case "order":
291                                         vocabularyItem.setOrder(fieldValue);
292                                         break;
293                                         
294                                 case "source":
295                                         vocabularyItem.setSource(fieldValue);
296                                         break;
297                                         
298                                 case "sourcePage":
299                                         vocabularyItem.setSourcePage(fieldValue);
300                                         break;
301                                         
302                                 case "description":
303                                         vocabularyItem.setDescription(fieldValue);
304                                         break;
305                                         
306                                 case "csid":
307                                         vocabularyItem.setCsid(fieldValue);
308                                         break;
309
310                                 default:
311                                         throw new DocumentException(String.format("Unknown field '%s' in vocabulary item payload.",
312                                                         fieldName));
313                         }
314                 }
315                 
316                 result = new PoxPayloadIn(VocabularyClient.SERVICE_ITEM_PAYLOAD_NAME, vocabularyItem, 
317                         VOCABULARYITEMS_COMMON);
318
319                 return result;
320         }
321     
322         private Response createAuthorityItem(
323                 CoreSessionInterface repoSession,
324                 ResourceMap resourceMap,
325                 UriInfo uriInfo,
326                 String parentIdentifier, // Either a CSID or a URN form -e.g., a8ad38ec-1d7d-4bf2-bd31 or urn:cspace:name(bugsbunny)
327                 PoxPayloadIn input) throws Exception {
328         Response result = null;
329         
330         ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), input, resourceMap, uriInfo);
331         ctx.setCurrentRepositorySession(repoSession);
332         
333         result = createAuthorityItem(ctx, parentIdentifier, AuthorityServiceUtils.UPDATE_REV,
334                         AuthorityServiceUtils.PROPOSED, AuthorityServiceUtils.NOT_SAS_ITEM);
335
336         return result;
337     }
338         
339         private PoxPayloadOut updateAuthorityItem(
340                 CoreSessionInterface repoSession,
341                 ResourceMap resourceMap,
342                 UriInfo uriInfo,
343                 String parentSpecifier, // Either a CSID or a URN form -e.g., a8ad38ec-1d7d-4bf2-bd31 or urn:cspace:name(bugsbunny)
344                 String itemSpecifier,   // Either a CSID or a URN form.
345                 PoxPayloadIn theUpdate) throws Exception {
346         PoxPayloadOut result = null;
347         
348         ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(getItemServiceName(), theUpdate, resourceMap, uriInfo);
349         ctx.setCurrentRepositorySession(repoSession);
350         
351         result = updateAuthorityItem(ctx, resourceMap, uriInfo, parentSpecifier, itemSpecifier, theUpdate,
352                         AuthorityServiceUtils.UPDATE_REV,                       // passing TRUE so rev num increases, passing
353                         AuthorityServiceUtils.NO_CHANGE,        // don't change the state of the "proposed" field -we could be performing a sync or just a plain update
354                         AuthorityServiceUtils.NO_CHANGE);       // don't change the state of the "sas" field -we could be performing a sync or just a plain update
355
356         return result;
357     }
358
359         @GET
360     @Path("{csid}")
361     @Override
362     public Response get(
363             @Context Request request,
364             @Context UriInfo uriInfo,
365             @PathParam("csid") String specifier) {
366         Response result = null;
367         uriInfo = new UriInfoWrapper(uriInfo);
368         
369         try {
370                 MultivaluedMap<String,String> queryParams = uriInfo.getQueryParameters();
371                 String showItemsValue = (String)queryParams.getFirst(VocabularyClient.SHOW_ITEMS_QP);
372             boolean showItems = Tools.isTrue(showItemsValue);
373             if (showItems == true) {
374                 //
375                 // We'll honor paging params if we find any; otherwise we'll set the page size to 0 to get ALL the items
376                 //
377                 if (queryParams.containsKey(IClientQueryParams.PAGE_SIZE_PARAM) == false) {
378                         queryParams.add(IClientQueryParams.PAGE_SIZE_PARAM, "0");
379                 }
380             }
381
382             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(request, uriInfo);
383             PoxPayloadOut payloadout = getAuthority(ctx, request, uriInfo, specifier, showItems);
384             result = buildResponse(ctx, payloadout);
385         } catch (Exception e) {
386             throw bigReThrow(e, ServiceMessages.GET_FAILED, specifier);
387         }
388
389         if (result == null) {
390             Response response = Response.status(Response.Status.NOT_FOUND).entity(
391                     "GET request failed. The requested Authority specifier:" + specifier + ": was not found.").type(
392                     "text/plain").build();
393             throw new CSWebApplicationException(response);
394         }
395
396         return result;
397     }
398     
399     @Override
400     public String getServiceName() {
401         return vocabularyServiceName;
402     }
403
404     @Override
405     public String getItemServiceName() {
406         return vocabularyItemServiceName;
407     }
408     
409         @Override
410         public Class<VocabulariesCommon> getCommonPartClass() {
411                 return VocabulariesCommon.class;
412         }
413
414     /**
415      * @return the name of the property used to specify references for items in this type of
416      * authority. For most authorities, it is ServiceBindingUtils.AUTH_REF_PROP ("authRef").
417      * Some types (like Vocabulary) use a separate property.
418      */
419         @Override
420     protected String getRefPropName() {
421         return ServiceBindingUtils.TERM_REF_PROP;
422     }
423         
424         @Override
425         protected String getOrderByField(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx) {
426                 String result = null;
427
428                 result = authorityItemCommonSchemaName + ":" + VocabularyItemJAXBSchema.DISPLAY_NAME;
429
430                 return result;
431         }
432         
433         @Override
434         protected String getPartialTermMatchField(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx) {
435                 return getOrderByField(ctx);
436         }
437
438         /*
439          * The item schema for the Vocabulary service does not support a multi-valued term list.  Only authorities that support
440          * term lists need to implement this method.
441          */
442         @Override
443         public String getItemTermInfoGroupXPathBase() {
444                 return null;
445         }
446 }