]> git.aero2k.de Git - tmp/jakarta-migration.git/blob
2593c545b20b988d969aad3451e485d07eca5c29
[tmp/jakarta-migration.git] /
1 package org.collectionspace.services.advancedsearch;
2
3 import java.nio.charset.StandardCharsets;
4 import java.util.ArrayList;
5 import java.util.HashMap;
6 import java.util.List;
7
8 import javax.ws.rs.Consumes;
9 import javax.ws.rs.GET;
10 import javax.ws.rs.Path;
11 import javax.ws.rs.Produces;
12 import javax.ws.rs.core.Context;
13 import javax.ws.rs.core.MultivaluedMap;
14 import javax.ws.rs.core.Request;
15 import javax.ws.rs.core.Response;
16 import javax.ws.rs.core.UriInfo;
17 import javax.xml.datatype.DatatypeConfigurationException;
18 import javax.xml.datatype.DatatypeFactory;
19 import javax.xml.datatype.XMLGregorianCalendar;
20
21 import org.collectionspace.services.advancedsearch.AdvancedsearchCommonList.AdvancedsearchListItem;
22 import org.collectionspace.services.advancedsearch.model.BriefDescriptionListModel;
23 import org.collectionspace.services.advancedsearch.model.ContentConceptListModel;
24 import org.collectionspace.services.advancedsearch.model.ObjectNameListModel;
25 import org.collectionspace.services.advancedsearch.model.ResponsibleDepartmentsListModel;
26 import org.collectionspace.services.advancedsearch.model.TitleGroupListModel;
27 import org.collectionspace.services.client.AdvancedSearchClient;
28 import org.collectionspace.services.client.CollectionObjectClient;
29 import org.collectionspace.services.client.CollectionSpaceClient;
30 import org.collectionspace.services.client.IQueryManager;
31 import org.collectionspace.services.client.PayloadInputPart;
32 import org.collectionspace.services.client.PoxPayloadIn;
33 import org.collectionspace.services.collectionobject.CollectionObjectResource;
34 import org.collectionspace.services.collectionobject.CollectionobjectsCommon;
35 import org.collectionspace.services.common.AbstractCollectionSpaceResourceImpl;
36 import org.collectionspace.services.common.ResourceMap;
37 import org.collectionspace.services.common.UriInfoWrapper;
38 import org.collectionspace.services.common.context.RemoteServiceContextFactory;
39 import org.collectionspace.services.common.context.ServiceContextFactory;
40 import org.collectionspace.services.jaxb.AbstractCommonList;
41 import org.collectionspace.services.jaxb.AbstractCommonList.ListItem;
42 import org.collectionspace.services.media.MediaResource;
43 import org.dom4j.DocumentException;
44 import org.jboss.resteasy.spi.ResteasyProviderFactory;
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
47 import org.w3c.dom.Element;
48
49 // FIXME: The *Client pattern should be deprecated; it's only really used in unit tests, and trying to use it in services creates authorization challenges. It's repeated here only to maintain parity with existing services.
50 /**
51  * This class defines the advanced search endpoints.
52  */
53 @Path(AdvancedSearchClient.SERVICE_PATH)
54 @Consumes("application/xml")
55 @Produces("application/xml")
56 public class AdvancedSearch
57                 extends AbstractCollectionSpaceResourceImpl<AdvancedsearchListItem, AdvancedsearchListItem> {
58         private static final String FIELDS_RETURNED = "uri|csid|refName|blobCsid|updatedAt|objectId|objectNumber|objectName|title|computedCurrentLocation|responsibleDepartments|responsibleDepartment|contentConcepts|briefDescription";
59         private static final String COMMON_PART_NAME = CollectionObjectClient.SERVICE_NAME + CollectionSpaceClient.PART_LABEL_SEPARATOR + CollectionSpaceClient.PART_COMMON_LABEL; // FIXME: it's not great to hardcode this here
60
61         private final Logger logger = LoggerFactory.getLogger(AdvancedSearch.class);
62         private final CollectionObjectResource cor = new CollectionObjectResource();
63         private final MediaResource mr = new MediaResource();
64
65         public AdvancedSearch() {
66                 super();
67         }
68
69         /**
70          * Primary advanced search API endpoint.
71          * 
72          * @param request The incoming request. Injected. 
73          * @param uriInfo The URI info of the incoming request, including query parameters and other search control parameters. Injected.
74          * @return A possibly-empty AbstractCommonList of the advanced search results corresponding to the query
75          */
76         @GET
77         public AbstractCommonList getList(@Context Request request, @Context UriInfo uriInfo) {
78                 logger.info("advancedsearch called with path: {}", uriInfo.getPath());
79                 MultivaluedMap<String, String> queryParams = uriInfo.getQueryParameters(true);
80                 logger.info("advancedsearch called with query params: {}", queryParams);
81
82                 // the list to return
83                 ObjectFactory objectFactory = new ObjectFactory();
84                 AdvancedsearchCommonList resultsList = objectFactory.createAdvancedsearchCommonList();
85                 // FIXME: this shouldn't be necessary?
86                 resultsList.advancedsearchListItem = new ArrayList<AdvancedsearchListItem>();
87
88                 // the logic here is to use CollectionObjectResource to perform the search, then
89                 // loop over the results retrieving corresponding CollectionobjectsCommon objects, 
90                 // which have more fields
91                 AbstractCommonList collectionObjectList = cor.getList(uriInfo);
92                 List<ListItem> collectionObjectListItems = collectionObjectList.getListItem();
93
94                 HashMap<String, String> collectionObjectValuesMap = new HashMap<String, String>();
95                 for (ListItem item : collectionObjectListItems) {
96                         // FIXME: is there no better way to do this? We should at least abstract this logic out of here
97                         List<Element> els = item.getAny();
98                         for (Element el : els) {
99                                 String elementName = el.getTagName();
100                                 String elementText = el.getTextContent();
101                                 collectionObjectValuesMap.put(elementName, elementText);
102                         }
103                         String csid = collectionObjectValuesMap.get("csid");
104                         UriInfoWrapper wrappedUriInfo = new UriInfoWrapper(uriInfo);
105                         List<String> blobCsids = findBlobCsids(csid, wrappedUriInfo);
106
107                         /*
108                          * NOTE: code below is partly based on
109                          * CollectionObjectServiceTest.readCollectionObjectCommonPart and
110                          * AbstractPoxServiceTestImpl
111                          */
112                         ResourceMap resourceMap = ResteasyProviderFactory.getContextData(ResourceMap.class);
113                         Response res = cor.get(request, resourceMap, uriInfo, csid);
114                         int statusCode = res.getStatus();
115                         logger.warn("advancedsearch: call to CollectionObjectResource for csid {} returned status {}", csid, statusCode);
116                         CollectionobjectsCommon collectionObject = null;
117                         // FIXME: is there no better way to do this? We should at least abstract this logic out of here
118                         PoxPayloadIn input = null;
119                         try {
120                 String responseXml = new String((byte[]) res.getEntity(),StandardCharsets.UTF_8);
121                 input = new PoxPayloadIn(responseXml);
122                         } catch (DocumentException e) {
123                                 // TODO: need better error handling
124                                 logger.error("advancedsearch: could not create PoxPayloadIn", e);
125                                 continue;
126                         }
127                         if (null != input) {
128                                 PayloadInputPart payloadInputPart = input.getPart(COMMON_PART_NAME);
129                                 if (null != payloadInputPart) {
130                                         collectionObject = (CollectionobjectsCommon) payloadInputPart.getBody();
131                                 }
132                         }
133                         // build up a listitem for the result list using the additional fields in CollectionObjectsCommon
134                         if (null != collectionObject) {
135                                 AdvancedsearchListItem listItem = objectFactory.createAdvancedsearchCommonListAdvancedsearchListItem();
136                                 listItem.setBriefDescription(BriefDescriptionListModel
137                                                 .briefDescriptionListToDisplayString(collectionObject.getBriefDescriptions()));
138                                 // TODO: collectionObject.getComputedCurrentLocation() is (can be?) a refname.
139                                 // code below extracts display name. there's probably something in RefName or
140                                 // similar to do this kind of thing see also
141                                 // ContentConceptListModel.displayNameFromRefName
142                                 String currLoc = collectionObject.getComputedCurrentLocation();
143                                 String currLocDisplayName = currLoc;
144                                 if (null != currLoc && currLoc.indexOf("'") < currLoc.lastIndexOf("'")) {
145                                         currLocDisplayName = currLoc.substring(currLoc.indexOf("'") + 1, currLoc.lastIndexOf("'"));
146                                 }
147                                 listItem.setComputedCurrentLocation(currLocDisplayName); // "Computed Current
148                                                                                                                                                         // Location: Display
149                                                                                                                                                         // full string" from
150                                                                                                                                                         // https://docs.google.com/spreadsheets/d/103jyxa2oCtt8U0IQ25xsOyIxqwKvPNXlcCtcjGlT5tQ/edit?gid=0#gid=0
151                                 listItem.setObjectName(
152                                                 ObjectNameListModel.objectNameListToDisplayString(collectionObject.getObjectNameList()));
153                                 listItem.setTitle(
154                                                 TitleGroupListModel.titleGroupListToDisplayString(collectionObject.getTitleGroupList()));
155                                 ResponsibleDepartmentsList rdl = ResponsibleDepartmentsListModel
156                                                 .responsibleDepartmentListToResponsibleDepartmentsList(
157                                                                 collectionObject.getResponsibleDepartments());
158                                 listItem.setResponsibleDepartments(rdl);
159                                 listItem.setResponsibleDepartment(
160                                                 ResponsibleDepartmentsListModel.responsibleDepartmentsListDisplayString(rdl));
161
162                                 listItem.setContentConcepts(
163                                                 ContentConceptListModel.contentConceptListDisplayString(collectionObject.getContentConcepts()));
164
165                                 // from media resource
166                                 if (blobCsids.size() > 0) {
167                                         listItem.setBlobCsid(blobCsids.get(0));
168                                 }
169
170                                 // from collectionobject itself
171                                 listItem.setCsid(collectionObjectValuesMap.get("csid"));
172                                 listItem.setObjectId(collectionObjectValuesMap.get("objectId")); // "Identification Number: Display full
173                                                                                                                                                                         // string" from
174                                                                                                                                                                         // https://docs.google.com/spreadsheets/d/103jyxa2oCtt8U0IQ25xsOyIxqwKvPNXlcCtcjGlT5tQ/edit?gid=0#gid=0
175                                 listItem.setObjectNumber(collectionObjectValuesMap.get("objectNumber"));
176                                 listItem.setRefName(collectionObjectValuesMap.get("refName"));
177                                 listItem.setUri(collectionObjectValuesMap.get("uri"));
178                                 try {
179                                         XMLGregorianCalendar date = DatatypeFactory.newInstance()
180                                                         .newXMLGregorianCalendar(collectionObjectValuesMap.get("updatedAt"));
181                                         listItem.setUpdatedAt(date); // "Last Updated Date: Display Date, if updated same day can we display
182                                                                                                         // x number of hours ago" from
183                                                                                                         // https://docs.google.com/spreadsheets/d/103jyxa2oCtt8U0IQ25xsOyIxqwKvPNXlcCtcjGlT5tQ/edit?gid=0#gid=0
184                                 } catch (DatatypeConfigurationException e) {
185                                         // FIXME need better error handling
186                                         logger.error("advancedsearch: could not create XMLGregorianCalendar for updatedAt ", e);
187                                         logger.error("advancedsearch: updatedAt: {}", collectionObjectValuesMap.get("updatedAt"));
188                                 }
189
190                                 // add the populated item to the results
191                                 resultsList.getAdvancedsearchListItem().add(listItem);
192                         } else {
193                                 logger.warn("advancedsearch: could not find CollectionobjectsCommon associated with csid {}", csid);
194                         }
195                         res.close();
196                 }
197
198                 // NOTE: I think this is necessary for the front end to know what to do with
199                 // what's returned (?)
200                 AbstractCommonList abstractList = (AbstractCommonList) resultsList;
201                 abstractList.setItemsInPage(collectionObjectList.getItemsInPage());
202                 abstractList.setPageNum(collectionObjectList.getPageNum());
203                 abstractList.setPageSize(collectionObjectList.getPageSize());
204                 abstractList.setTotalItems(collectionObjectList.getTotalItems());
205                 // FIXME: is there a way to generate this rather than hardcode it?
206                 abstractList.setFieldsReturned(FIELDS_RETURNED);
207
208                 return resultsList;
209         }
210
211         /** 
212          * Retrieves the blob CSIDs associated with a given object's CSID
213          * 
214          * @param csid The CSID of an object whose associated blobs (thumbnails) is desired
215          * @param wrappedUriInfo The wrapped (mutable) UriInfo of the incoming query that ultimately triggered this call
216          * @return A possibly-empty list of strings of the blob CSIDs associated with CSID
217          */
218         private List<String> findBlobCsids(String csid, UriInfoWrapper wrappedUriInfo) {
219                 MultivaluedMap<String, String> wrappedQueryParams = wrappedUriInfo.getQueryParameters();
220                 wrappedQueryParams.clear();
221                 wrappedQueryParams.add(IQueryManager.SEARCH_RELATED_TO_CSID_AS_SUBJECT, csid);
222                 wrappedQueryParams.add("pgSz", "1");
223                 wrappedQueryParams.add("pgNum", "0");
224                 wrappedQueryParams.add("sortBy", "media_common:title");
225                 AbstractCommonList associatedMedia = mr.getList(wrappedUriInfo);
226                 HashMap<String, String> mediaResourceValuesMap = new HashMap<String, String>();
227                 ArrayList<String> blobCsids = new ArrayList<String>();
228                 for (ListItem item : associatedMedia.getListItem()) {
229                         // FIXME: is there no better way to do this? we should at least abstract out this logic
230                         List<Element> els = item.getAny();
231                         for (Element el : els) {
232                                 String elementName = el.getTagName();
233                                 String elementText = el.getTextContent();
234                                 mediaResourceValuesMap.put(elementName, elementText);
235                         }
236                         String blobCsid = mediaResourceValuesMap.get("blobCsid");
237                         if (null != blobCsid) {
238                                 blobCsids.add(blobCsid);
239                         }
240                 }
241                 return blobCsids;
242         }
243
244         @Override
245         public Class<?> getCommonPartClass() {
246                 return null;
247         }
248
249         @Override
250         public ServiceContextFactory<AdvancedsearchListItem, AdvancedsearchListItem> getServiceContextFactory() {
251                 return (ServiceContextFactory<AdvancedsearchListItem, AdvancedsearchListItem>) RemoteServiceContextFactory
252                                 .get();
253         }
254
255         @Override
256         public String getServiceName() {
257                 return AdvancedSearchClient.SERVICE_NAME;
258         }
259
260         @Override
261         protected String getVersionString() {
262                 return "0.01";
263         }
264 }