2 * This document is a part of the source code and related artifacts for
\r
3 * CollectionSpace, an open source collections management system for museums and
\r
4 * related institutions:
\r
6 * http://www.collectionspace.org http://wiki.collectionspace.org
\r
8 * Copyright 2009 University of California at Berkeley
\r
10 * Licensed under the Educational Community License (ECL), Version 2.0. You may
\r
11 * not use this file except in compliance with this License.
\r
13 * You may obtain a copy of the ECL 2.0 License at
\r
15 * https://source.collectionspace.org/collection-space/LICENSE.txt
\r
17 * Unless required by applicable law or agreed to in writing, software
\r
18 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
\r
19 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
\r
20 * License for the specific language governing permissions and limitations under
\r
23 package org.collectionspace.services.common.vocabulary;
\r
25 import java.util.ArrayList;
\r
26 import java.util.HashMap;
\r
27 import java.util.Iterator;
\r
28 import java.util.List;
\r
29 import java.util.Map;
\r
31 import org.nuxeo.ecm.core.api.ClientException;
\r
32 import org.nuxeo.ecm.core.api.DocumentModel;
\r
33 import org.nuxeo.ecm.core.api.DocumentModelList;
\r
34 import org.nuxeo.ecm.core.api.model.Property;
\r
35 import org.nuxeo.ecm.core.api.model.PropertyException;
\r
36 import org.nuxeo.ecm.core.api.model.PropertyNotFoundException;
\r
37 import org.nuxeo.ecm.core.api.model.impl.primitives.StringProperty;
\r
38 import org.nuxeo.ecm.core.api.repository.RepositoryInstance;
\r
39 import org.slf4j.Logger;
\r
40 import org.slf4j.LoggerFactory;
\r
42 import org.collectionspace.services.client.PoxPayloadIn;
\r
43 import org.collectionspace.services.client.PoxPayloadOut;
\r
44 import org.collectionspace.services.common.ServiceMain;
\r
45 import org.collectionspace.services.common.context.ServiceContext;
\r
46 import org.collectionspace.services.common.context.AbstractServiceContextImpl;
\r
47 import org.collectionspace.services.common.api.RefNameUtils;
\r
48 import org.collectionspace.services.common.api.Tools;
\r
49 import org.collectionspace.services.common.api.RefNameUtils.AuthorityTermInfo;
\r
50 import org.collectionspace.services.common.authorityref.AuthorityRefDocList;
\r
51 import org.collectionspace.services.common.config.TenantBindingConfigReaderImpl;
\r
52 import org.collectionspace.services.common.config.URIUtils;
\r
53 import org.collectionspace.services.common.context.ServiceBindingUtils;
\r
54 import org.collectionspace.services.common.document.DocumentException;
\r
55 import org.collectionspace.services.common.document.DocumentFilter;
\r
56 import org.collectionspace.services.common.document.DocumentNotFoundException;
\r
57 import org.collectionspace.services.common.document.DocumentUtils;
\r
58 import org.collectionspace.services.common.document.DocumentWrapper;
\r
59 import org.collectionspace.services.common.query.QueryManager;
\r
60 import org.collectionspace.services.common.repository.RepositoryClient;
\r
61 // import org.collectionspace.services.common.vocabulary.AuthorityItemJAXBSchema;
\r
62 import org.collectionspace.services.nuxeo.client.java.DocHandlerBase;
\r
63 import org.collectionspace.services.nuxeo.client.java.RepositoryJavaClientImpl;
\r
64 import org.collectionspace.services.common.security.SecurityUtils;
\r
65 import org.collectionspace.services.config.service.ServiceBindingType;
\r
66 import org.collectionspace.services.jaxb.AbstractCommonList;
\r
67 import org.collectionspace.services.nuxeo.util.NuxeoUtils;
\r
70 * RefNameServiceUtils is a collection of services utilities related to refName
\r
73 * $LastChangedRevision: $ $LastChangedDate: $
\r
75 public class RefNameServiceUtils {
\r
77 public static class AuthRefConfigInfo {
\r
79 public String getQualifiedDisplayName() {
\r
80 return (Tools.isBlank(schema))
\r
81 ? displayName : DocumentUtils.appendSchemaName(schema, displayName);
\r
84 public String getDisplayName() {
\r
88 public void setDisplayName(String displayName) {
\r
89 this.displayName = displayName;
\r
94 public String getSchema() {
\r
98 public void setSchema(String schema) {
\r
99 this.schema = schema;
\r
102 public String getFullPath() {
\r
106 public void setFullPath(String fullPath) {
\r
107 this.fullPath = fullPath;
\r
110 protected String[] pathEls;
\r
112 public AuthRefConfigInfo(AuthRefConfigInfo arci) {
\r
113 this.displayName = arci.displayName;
\r
114 this.schema = arci.schema;
\r
115 this.fullPath = arci.fullPath;
\r
116 this.pathEls = arci.pathEls;
\r
117 // Skip the pathElse check, since we are creatign from another (presumably valid) arci.
\r
120 public AuthRefConfigInfo(String displayName, String schema, String fullPath, String[] pathEls) {
\r
121 this.displayName = displayName;
\r
122 this.schema = schema;
\r
123 this.fullPath = fullPath;
\r
124 this.pathEls = pathEls;
\r
128 // Split a config value string like "intakes_common:collector", or
\r
129 // "collectionobjects_common:contentPeoples|contentPeople"
\r
130 // "collectionobjects_common:assocEventGroupList/*/assocEventPlace"
\r
131 // If has a pipe ('|') second part is a displayLabel, and first is path
\r
132 // Otherwise, entry is a path, and can use the last pathElement as displayName
\r
133 // Should be schema qualified.
\r
134 public AuthRefConfigInfo(String configString) {
\r
135 String[] pair = configString.split("\\|", 2);
\r
137 String displayName, fullPath;
\r
138 if (pair.length == 1) {
\r
139 // no label specifier, so we'll defer getting label
\r
140 fullPath = pair[0];
\r
141 pathEls = pair[0].split("/");
\r
142 displayName = pathEls[pathEls.length - 1];
\r
144 fullPath = pair[0];
\r
145 pathEls = pair[0].split("/");
\r
146 displayName = pair[1];
\r
148 String[] schemaSplit = pathEls[0].split(":", 2);
\r
150 if (schemaSplit.length == 1) { // schema not specified
\r
153 schema = schemaSplit[0];
\r
154 if (pair.length == 1 && pathEls.length == 1) { // simplest case of field in top level schema, no labelll
\r
155 displayName = schemaSplit[1]; // Have to fix up displayName to have no schema
\r
158 this.displayName = displayName;
\r
159 this.schema = schema;
\r
160 this.fullPath = fullPath;
\r
161 this.pathEls = pathEls;
\r
165 protected void checkPathEls() {
\r
166 int len = pathEls.length;
\r
168 throw new InternalError("Bad values in authRef info - caller screwed up:" + fullPath);
\r
170 // Handle case of them putting a leading slash on the path
\r
171 if (len > 1 && pathEls[0].endsWith(":")) {
\r
173 String[] newArray = new String[len];
\r
174 newArray[0] = pathEls[0] + pathEls[1];
\r
176 System.arraycopy(pathEls, 2, newArray, 1, len - 1);
\r
178 pathEls = newArray;
\r
183 public static class AuthRefInfo extends AuthRefConfigInfo {
\r
185 public Property getProperty() {
\r
189 public void setProperty(Property property) {
\r
190 this.property = property;
\r
194 public AuthRefInfo(String displayName, String schema, String fullPath, String[] pathEls, Property prop) {
\r
195 super(displayName, schema, fullPath, pathEls);
\r
196 this.property = prop;
\r
199 public AuthRefInfo(AuthRefConfigInfo arci, Property prop) {
\r
201 this.property = prop;
\r
204 private static final Logger logger = LoggerFactory.getLogger(RefNameServiceUtils.class);
\r
205 private static ArrayList<String> refNameServiceTypes = null;
\r
207 public static List<AuthRefConfigInfo> getConfiguredAuthorityRefs(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx) {
\r
208 List<String> authRefFields =
\r
209 ((AbstractServiceContextImpl) ctx).getAllPartsPropertyValues(
\r
210 ServiceBindingUtils.AUTH_REF_PROP, ServiceBindingUtils.QUALIFIED_PROP_NAMES);
\r
211 ArrayList<AuthRefConfigInfo> authRefsInfo = new ArrayList<AuthRefConfigInfo>(authRefFields.size());
\r
212 for (String spec : authRefFields) {
\r
213 AuthRefConfigInfo arci = new AuthRefConfigInfo(spec);
\r
214 authRefsInfo.add(arci);
\r
216 return authRefsInfo;
\r
219 public static AuthorityRefDocList getAuthorityRefDocs(
\r
220 RepositoryInstance repoSession,
\r
221 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
\r
222 RepositoryClient<PoxPayloadIn, PoxPayloadOut> repoClient,
\r
223 List<String> serviceTypes,
\r
225 String refPropName,
\r
226 DocumentFilter filter, boolean computeTotal)
\r
227 throws DocumentException, DocumentNotFoundException {
\r
228 AuthorityRefDocList wrapperList = new AuthorityRefDocList();
\r
229 AbstractCommonList commonList = (AbstractCommonList) wrapperList;
\r
230 int pageNum = filter.getStartPage();
\r
231 int pageSize = filter.getPageSize();
\r
233 List<AuthorityRefDocList.AuthorityRefDocItem> list =
\r
234 wrapperList.getAuthorityRefDocItem();
\r
236 Map<String, ServiceBindingType> queriedServiceBindings = new HashMap<String, ServiceBindingType>();
\r
237 Map<String, List<AuthRefConfigInfo>> authRefFieldsByService = new HashMap<String, List<AuthRefConfigInfo>>();
\r
239 RepositoryJavaClientImpl nuxeoRepoClient = (RepositoryJavaClientImpl) repoClient;
\r
241 // Ignore any provided page size and number query parameters in
\r
242 // the following call, as they pertain to the list of authority
\r
243 // references to be returned, not to the list of documents to be
\r
244 // scanned for those references.
\r
245 DocumentModelList docList = findAuthorityRefDocs(ctx, repoClient, repoSession,
\r
246 serviceTypes, refName, refPropName, queriedServiceBindings, authRefFieldsByService,
\r
247 filter.getWhereClause(), null, 0 /* pageSize */, 0 /* pageNum */, computeTotal);
\r
249 if (docList == null) { // found no authRef fields - nothing to process
\r
250 return wrapperList;
\r
253 // set the fieldsReturned list. Even though this is a fixed schema, app layer treats
\r
254 // this like other abstract common lists
\r
256 * <xs:element name="docType" type="xs:string" minOccurs="1" />
\r
257 * <xs:element name="docId" type="xs:string" minOccurs="1" />
\r
258 * <xs:element name="docNumber" type="xs:string" minOccurs="0" />
\r
259 * <xs:element name="docName" type="xs:string" minOccurs="0" />
\r
260 * <xs:element name="sourceField" type="xs:string" minOccurs="1" />
\r
261 * <xs:element name="uri" type="xs:anyURI" minOccurs="1" />
\r
262 * <xs:element name="updatedAt" type="xs:string" minOccurs="1" />
\r
263 * <xs:element name="workflowState" type="xs:string" minOccurs="1"
\r
266 String fieldList = "docType|docId|docNumber|docName|sourceField|uri|updatedAt|workflowState";
\r
267 commonList.setFieldsReturned(fieldList);
\r
269 // As a side-effect, the method called below modifies the value of
\r
270 // the 'list' variable, which holds the list of references to
\r
271 // an authority item.
\r
273 // There can be more than one reference to a particular authority
\r
274 // item within any individual document scanned, so the number of
\r
275 // authority references may potentially exceed the total number
\r
276 // of documents scanned.
\r
277 int nRefsFound = processRefObjsDocList(docList, refName, queriedServiceBindings, authRefFieldsByService, // the actual list size needs to be updated to the size of "list"
\r
280 commonList.setPageSize(pageSize);
\r
282 // Values returned in the pagination block above the list items
\r
283 // need to reflect the number of references to authority items
\r
284 // returned, rather than the number of documents originally scanned
\r
285 // to find such references.
\r
286 commonList.setPageNum(pageNum);
\r
287 commonList.setTotalItems(list.size());
\r
289 // Slice the list to return only the specified page of items
\r
290 // in the list results.
\r
292 // FIXME: There may well be a pattern-based way to do this
\r
293 // in our framework, and if we can eliminate much of the
\r
294 // non-DRY code below, that would be desirable.
\r
296 int startIndex = 0;
\r
299 // Return all results if pageSize is 0.
\r
300 if (pageSize == 0) {
\r
302 endIndex = list.size();
\r
304 startIndex = pageNum * pageSize;
\r
307 // Return an empty list when the start of the requested page is
\r
308 // beyond the last item in the list.
\r
309 if (startIndex > list.size()) {
\r
310 wrapperList.getAuthorityRefDocItem().clear();
\r
311 commonList.setItemsInPage(wrapperList.getAuthorityRefDocItem().size());
\r
312 return wrapperList;
\r
315 // Otherwise, return a list of items from the start of the specified
\r
316 // page through the last item on that page, or otherwise through the
\r
317 // last item in the entire list, if that occurs earlier than the end
\r
318 // of the specified page.
\r
319 if (endIndex == 0) {
\r
320 int pageEndIndex = ((startIndex + pageSize));
\r
321 endIndex = (pageEndIndex > list.size()) ? list.size() : pageEndIndex;
\r
324 // Slice the list to return only the specified page of results.
\r
325 // Note: the second argument to List.subList(), endIndex, is
\r
326 // exclusive of the item at its index position, reflecting the
\r
327 // zero-index nature of the list.
\r
328 List<AuthorityRefDocList.AuthorityRefDocItem> currentPageList =
\r
329 new ArrayList<AuthorityRefDocList.AuthorityRefDocItem>(list.subList(startIndex, endIndex));
\r
330 wrapperList.getAuthorityRefDocItem().clear();
\r
331 wrapperList.getAuthorityRefDocItem().addAll(currentPageList);
\r
332 commonList.setItemsInPage(currentPageList.size());
\r
334 if (logger.isDebugEnabled() && (nRefsFound < docList.size())) {
\r
335 logger.debug("Internal curiosity: got fewer matches of refs than # docs matched..."); // We found a ref to ourself and have excluded it.
\r
337 } catch (Exception e) {
\r
338 logger.error("Could not retrieve a list of documents referring to the specified authority item", e);
\r
339 wrapperList = null;
\r
342 return wrapperList;
\r
345 private static ArrayList<String> getRefNameServiceTypes() {
\r
346 if (refNameServiceTypes == null) {
\r
347 refNameServiceTypes = new ArrayList<String>();
\r
348 refNameServiceTypes.add(ServiceBindingUtils.SERVICE_TYPE_AUTHORITY);
\r
349 refNameServiceTypes.add(ServiceBindingUtils.SERVICE_TYPE_OBJECT);
\r
350 refNameServiceTypes.add(ServiceBindingUtils.SERVICE_TYPE_PROCEDURE);
\r
352 return refNameServiceTypes;
\r
354 // Seems like a good value - no real data to set this well.
\r
355 // Note: can set this value lower; e.g. to 3 during debugging; - ADR 2012-07-10
\r
356 private static final int N_OBJS_TO_UPDATE_PER_LOOP = 100;
\r
358 public static int updateAuthorityRefDocs(
\r
359 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
\r
360 RepositoryClient<PoxPayloadIn, PoxPayloadOut> repoClient,
\r
361 RepositoryInstance repoSession,
\r
364 String refPropName) throws Exception {
\r
365 Map<String, ServiceBindingType> queriedServiceBindings = new HashMap<String, ServiceBindingType>();
\r
366 Map<String, List<AuthRefConfigInfo>> authRefFieldsByService = new HashMap<String, List<AuthRefConfigInfo>>();
\r
368 int docsScanned = 0;
\r
369 int nRefsFound = 0;
\r
370 int currentPage = 0;
\r
371 int docsInCurrentPage = 0;
\r
372 final String WHERE_CLAUSE_ADDITIONS_VALUE = null;
\r
373 final String ORDER_BY_VALUE = "collectionspace_core:createdAt";
\r
375 if (!(repoClient instanceof RepositoryJavaClientImpl)) {
\r
376 throw new InternalError("updateAuthorityRefDocs() called with unknown repoClient type!");
\r
378 try { // REM - How can we deal with transaction and timeout issues here.
\r
379 final int pageSize = N_OBJS_TO_UPDATE_PER_LOOP;
\r
380 DocumentModelList docList;
\r
381 boolean morePages = true;
\r
382 while (morePages) {
\r
384 docList = findAuthorityRefDocs(ctx, repoClient, repoSession,
\r
385 getRefNameServiceTypes(), oldRefName, refPropName,
\r
386 queriedServiceBindings, authRefFieldsByService, WHERE_CLAUSE_ADDITIONS_VALUE, ORDER_BY_VALUE, pageSize, currentPage, false);
\r
388 if (docList == null) {
\r
389 logger.debug("updateAuthorityRefDocs: no documents could be found that referenced the old refName");
\r
392 docsInCurrentPage = docList.size();
\r
393 logger.debug("updateAuthorityRefDocs: current page=" + currentPage + " documents included in page=" + docsInCurrentPage);
\r
394 if (docsInCurrentPage == 0) {
\r
395 logger.debug("updateAuthorityRefDocs: no more documents requiring refName updates could be found");
\r
398 if (docsInCurrentPage < pageSize) {
\r
399 logger.debug("updateAuthorityRefDocs: assuming no more documents requiring refName updates will be found, as docsInCurrentPage < pageSize");
\r
403 int nRefsFoundThisPage = processRefObjsDocList(docList, oldRefName, queriedServiceBindings, authRefFieldsByService,
\r
405 if (nRefsFoundThisPage > 0) {
\r
406 ((RepositoryJavaClientImpl) repoClient).saveDocListWithoutHandlerProcessing(ctx, repoSession, docList, true);
\r
407 nRefsFound += nRefsFoundThisPage;
\r
410 // FIXME: Per REM, set a limit of num objects - something like
\r
411 // 1000K objects - and also add a log Warning after some threshold
\r
412 docsScanned += docsInCurrentPage;
\r
418 } catch (Exception e) {
\r
419 logger.error("Internal error updating the AuthorityRefDocs: " + e.getLocalizedMessage());
\r
420 logger.debug(Tools.errorToString(e, true));
\r
423 logger.debug("updateAuthorityRefDocs replaced a total of " + nRefsFound + " authority references, within as many as " + docsScanned + " scanned document(s)");
\r
427 private static DocumentModelList findAuthorityRefDocs(
\r
428 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
\r
429 RepositoryClient<PoxPayloadIn, PoxPayloadOut> repoClient,
\r
430 RepositoryInstance repoSession, List<String> serviceTypes,
\r
432 String refPropName,
\r
433 Map<String, ServiceBindingType> queriedServiceBindings,
\r
434 Map<String, List<AuthRefConfigInfo>> authRefFieldsByService,
\r
435 String whereClauseAdditions,
\r
436 String orderByClause,
\r
439 boolean computeTotal) throws DocumentException, DocumentNotFoundException {
\r
441 // Get the service bindings for this tenant
\r
442 TenantBindingConfigReaderImpl tReader =
\r
443 ServiceMain.getInstance().getTenantBindingConfigReader();
\r
444 // We need to get all the procedures, authorities, and objects.
\r
445 List<ServiceBindingType> servicebindings = tReader.getServiceBindingsByType(ctx.getTenantId(), serviceTypes);
\r
446 if (servicebindings == null || servicebindings.isEmpty()) {
\r
447 logger.error("RefNameServiceUtils.getAuthorityRefDocs: No services bindings found, cannot proceed!");
\r
450 // Filter the list for current user rights
\r
451 servicebindings = SecurityUtils.getReadableServiceBindingsForCurrentUser(servicebindings);
\r
453 ArrayList<String> docTypes = new ArrayList<String>();
\r
455 String query = computeWhereClauseForAuthorityRefDocs(refName, refPropName, docTypes, servicebindings,
\r
456 queriedServiceBindings, authRefFieldsByService);
\r
457 if (query == null) { // found no authRef fields - nothing to query
\r
460 // Additional qualifications, like workflow state
\r
461 if (Tools.notBlank(whereClauseAdditions)) {
\r
462 query += " AND " + whereClauseAdditions;
\r
464 // Now we have to issue the search
\r
465 RepositoryJavaClientImpl nuxeoRepoClient = (RepositoryJavaClientImpl) repoClient;
\r
466 DocumentWrapper<DocumentModelList> docListWrapper = nuxeoRepoClient.findDocs(ctx, repoSession,
\r
467 docTypes, query, orderByClause, pageSize, pageNum, computeTotal);
\r
468 // Now we gather the info for each document into the list and return
\r
469 DocumentModelList docList = docListWrapper.getWrappedObject();
\r
472 private static final boolean READY_FOR_COMPLEX_QUERY = true;
\r
474 private static String computeWhereClauseForAuthorityRefDocs(
\r
476 String refPropName,
\r
477 ArrayList<String> docTypes,
\r
478 List<ServiceBindingType> servicebindings,
\r
479 Map<String, ServiceBindingType> queriedServiceBindings,
\r
480 Map<String, List<AuthRefConfigInfo>> authRefFieldsByService) {
\r
482 boolean fFirst = true;
\r
483 List<String> authRefFieldPaths;
\r
484 for (ServiceBindingType sb : servicebindings) {
\r
485 // Gets the property names for each part, qualified with the part label (which
\r
486 // is also the table name, the way that the repository works).
\r
487 authRefFieldPaths =
\r
488 ServiceBindingUtils.getAllPartsPropertyValues(sb,
\r
489 refPropName, ServiceBindingUtils.QUALIFIED_PROP_NAMES);
\r
490 if (authRefFieldPaths.isEmpty()) {
\r
493 ArrayList<AuthRefConfigInfo> authRefsInfo = new ArrayList<AuthRefConfigInfo>();
\r
494 for (String spec : authRefFieldPaths) {
\r
495 AuthRefConfigInfo arci = new AuthRefConfigInfo(spec);
\r
496 authRefsInfo.add(arci);
\r
499 String docType = sb.getObject().getName();
\r
500 queriedServiceBindings.put(docType, sb);
\r
501 authRefFieldsByService.put(docType, authRefsInfo);
\r
502 docTypes.add(docType);
\r
505 if (fFirst) { // found no authRef fields - nothing to query
\r
508 // We used to build a complete matches query, but that was too complex.
\r
509 // Just build a keyword query based upon some key pieces - the urn syntax elements and the shortID
\r
510 // Note that this will also match the Item itself, but that will get filtered out when
\r
511 // we compute actual matches.
\r
512 AuthorityTermInfo authTermInfo = RefNameUtils.parseAuthorityTermInfo(refName);
\r
514 String keywords = RefNameUtils.URN_PREFIX
\r
515 + " AND " + (authTermInfo.inAuthority.name != null
\r
516 ? authTermInfo.inAuthority.name : authTermInfo.inAuthority.csid)
\r
517 + " AND " + (authTermInfo.name != null
\r
518 ? authTermInfo.name : authTermInfo.csid);
\r
520 String whereClauseStr = QueryManager.createWhereClauseFromKeywords(keywords);
\r
522 if (logger.isTraceEnabled()) {
\r
523 logger.trace("The 'where' clause to find refObjs is: ", whereClauseStr);
\r
526 return whereClauseStr;
\r
530 * Runs through the list of found docs, processing them. If list is
\r
531 * non-null, then processing means gather the info for items. If list is
\r
532 * null, and newRefName is non-null, then processing means replacing and
\r
533 * updating. If processing/updating, this must be called in teh context of
\r
534 * an open session, and caller must release Session after calling this.
\r
537 private static int processRefObjsDocList(
\r
538 DocumentModelList docList,
\r
540 Map<String, ServiceBindingType> queriedServiceBindings,
\r
541 Map<String, List<AuthRefConfigInfo>> authRefFieldsByService,
\r
542 List<AuthorityRefDocList.AuthorityRefDocItem> list,
\r
543 String newAuthorityRefName) {
\r
544 Iterator<DocumentModel> iter = docList.iterator();
\r
545 int nRefsFoundTotal = 0;
\r
546 while (iter.hasNext()) {
\r
547 DocumentModel docModel = iter.next();
\r
548 AuthorityRefDocList.AuthorityRefDocItem ilistItem;
\r
550 String docType = docModel.getDocumentType().getName();
\r
551 docType = ServiceBindingUtils.getUnqualifiedTenantDocType(docType);
\r
552 ServiceBindingType sb = queriedServiceBindings.get(docType);
\r
554 throw new RuntimeException(
\r
555 "getAuthorityRefDocs: No Service Binding for docType: " + docType);
\r
558 if (list == null) { // no list - should be update refName case.
\r
559 if (newAuthorityRefName == null) {
\r
560 throw new InternalError("processRefObjsDocList() called with neither an itemList nor a new RefName!");
\r
563 } else { // Have a list - refObjs case
\r
564 if (newAuthorityRefName != null) {
\r
565 throw new InternalError("processRefObjsDocList() called with both an itemList and a new RefName!");
\r
567 ilistItem = new AuthorityRefDocList.AuthorityRefDocItem();
\r
568 String csid = NuxeoUtils.getCsid(docModel);//NuxeoUtils.extractId(docModel.getPathAsString());
\r
569 ilistItem.setDocId(csid);
\r
571 // FIXME: Hack for CSPACE-5406; this instead should use the (forthcoming)
\r
572 // URL pattern-to-Doctype registry described in CSPACE-5471 - ADR 2012-07-18
\r
573 if (sb.getType().equalsIgnoreCase(URIUtils.AUTHORITY_SERVICE_CATEGORY)) {
\r
574 String authoritySvcName = URIUtils.getAuthoritySvcName(docType);
\r
575 String inAuthorityCsid;
\r
577 inAuthorityCsid = (String) docModel.getPropertyValue("inAuthority"); // AuthorityItemJAXBSchema.IN_AUTHORITY
\r
578 uri = URIUtils.getAuthorityItemUri(authoritySvcName, inAuthorityCsid, csid);
\r
579 } catch (Exception e) {
\r
580 logger.warn("Could not extract inAuthority property from authority item record: " + e.getMessage());
\r
583 uri = URIUtils.getUri(sb.getName(), csid);;
\r
585 ilistItem.setUri(uri);
\r
587 ilistItem.setWorkflowState(docModel.getCurrentLifeCycleState());
\r
588 ilistItem.setUpdatedAt(DocHandlerBase.getUpdatedAtAsString(docModel));
\r
589 } catch (Exception e) {
\r
590 logger.error("Error getting core values for doc [" + csid + "]: " + e.getLocalizedMessage());
\r
592 ilistItem.setDocType(docType);
\r
593 ilistItem.setDocNumber(
\r
594 ServiceBindingUtils.getMappedFieldInDoc(sb, ServiceBindingUtils.OBJ_NUMBER_PROP, docModel));
\r
595 ilistItem.setDocName(
\r
596 ServiceBindingUtils.getMappedFieldInDoc(sb, ServiceBindingUtils.OBJ_NAME_PROP, docModel));
\r
598 // Now, we have to loop over the authRefFieldsByService to figure
\r
599 // out which field(s) matched this.
\r
600 List<AuthRefConfigInfo> matchingAuthRefFields = authRefFieldsByService.get(docType);
\r
601 if (matchingAuthRefFields == null || matchingAuthRefFields.isEmpty()) {
\r
602 throw new RuntimeException(
\r
603 "getAuthorityRefDocs: internal logic error: can't fetch authRefFields for DocType.");
\r
605 //String authRefAncestorField = "";
\r
606 //String authRefDescendantField = "";
\r
607 //String sourceField = "";
\r
608 int nRefsFoundInDoc = 0;
\r
610 ArrayList<RefNameServiceUtils.AuthRefInfo> foundProps = new ArrayList<RefNameServiceUtils.AuthRefInfo>();
\r
612 findAuthRefPropertiesInDoc(docModel, matchingAuthRefFields, refName, foundProps);
\r
613 for (RefNameServiceUtils.AuthRefInfo ari : foundProps) {
\r
614 if (ilistItem != null) {
\r
615 if (nRefsFoundInDoc == 0) { // First one?
\r
616 ilistItem.setSourceField(ari.getQualifiedDisplayName());
\r
617 } else { // duplicates from one object
\r
618 ilistItem = cloneAuthRefDocItem(ilistItem, ari.getQualifiedDisplayName());
\r
620 list.add(ilistItem);
\r
621 } else { // update refName case
\r
622 Property propToUpdate = ari.getProperty();
\r
623 propToUpdate.setValue(newAuthorityRefName);
\r
627 } catch (ClientException ce) {
\r
628 throw new RuntimeException(
\r
629 "getAuthorityRefDocs: Problem fetching values from repo: " + ce.getLocalizedMessage());
\r
631 if (nRefsFoundInDoc == 0) {
\r
633 "getAuthorityRefDocs: Result: "
\r
634 + docType + " [" + NuxeoUtils.getCsid(docModel)
\r
635 + "] does not reference ["
\r
638 nRefsFoundTotal += nRefsFoundInDoc;
\r
640 return nRefsFoundTotal;
\r
643 private static AuthorityRefDocList.AuthorityRefDocItem cloneAuthRefDocItem(
\r
644 AuthorityRefDocList.AuthorityRefDocItem ilistItem, String sourceField) {
\r
645 AuthorityRefDocList.AuthorityRefDocItem newlistItem = new AuthorityRefDocList.AuthorityRefDocItem();
\r
646 newlistItem.setDocId(ilistItem.getDocId());
\r
647 newlistItem.setDocName(ilistItem.getDocName());
\r
648 newlistItem.setDocNumber(ilistItem.getDocNumber());
\r
649 newlistItem.setDocType(ilistItem.getDocType());
\r
650 newlistItem.setUri(ilistItem.getUri());
\r
651 newlistItem.setSourceField(sourceField);
\r
652 return newlistItem;
\r
655 public static List<AuthRefInfo> findAuthRefPropertiesInDoc(
\r
656 DocumentModel docModel,
\r
657 List<AuthRefConfigInfo> authRefFieldInfo,
\r
658 String refNameToMatch,
\r
659 List<AuthRefInfo> foundProps) {
\r
660 // Assume that authRefFieldInfo is keyed by the field name (possibly mapped for UI)
\r
661 // and the values are elPaths to the field, where intervening group structures in
\r
662 // lists of complex structures are replaced with "*". Thus, valid paths include
\r
663 // the following (note that the ServiceBindingUtils prepend schema names to configured values):
\r
664 // "schemaname:fieldname"
\r
665 // "schemaname:scalarlistname"
\r
666 // "schemaname:complexfieldname/fieldname"
\r
667 // "schemaname:complexlistname/*/fieldname"
\r
668 // "schemaname:complexlistname/*/scalarlistname"
\r
669 // "schemaname:complexlistname/*/complexfieldname/fieldname"
\r
670 // "schemaname:complexlistname/*/complexlistname/*/fieldname"
\r
672 for (AuthRefConfigInfo arci : authRefFieldInfo) {
\r
674 // Get first property and work down as needed.
\r
675 Property prop = docModel.getProperty(arci.pathEls[0]);
\r
676 findAuthRefPropertiesInProperty(foundProps, prop, arci, 0, refNameToMatch);
\r
677 } catch (Exception e) {
\r
678 logger.error("Problem fetching property: " + arci.pathEls[0]);
\r
684 public static List<AuthRefInfo> findAuthRefPropertiesInProperty(
\r
685 List<AuthRefInfo> foundProps,
\r
687 AuthRefConfigInfo arci,
\r
688 int pathStartIndex, // Supports recursion and we work down the path
\r
689 String refNameToMatch) {
\r
690 if (pathStartIndex >= arci.pathEls.length) {
\r
691 throw new ArrayIndexOutOfBoundsException("Index = " + pathStartIndex + " for path: "
\r
692 + arci.pathEls.toString());
\r
694 AuthRefInfo ari = null;
\r
695 if (prop == null) {
\r
699 if (prop instanceof StringProperty) { // scalar string
\r
700 addARIifMatches(refNameToMatch, arci, prop, foundProps);
\r
701 } else if (prop instanceof List) {
\r
702 List<Property> propList = (List<Property>) prop;
\r
703 // run through list. Must either be list of Strings, or Complex
\r
704 for (Property listItemProp : propList) {
\r
705 if (listItemProp instanceof StringProperty) {
\r
706 if (arci.pathEls.length - pathStartIndex != 1) {
\r
707 logger.error("Configuration for authRefs does not match schema structure: "
\r
708 + arci.pathEls.toString());
\r
711 addARIifMatches(refNameToMatch, arci, listItemProp, foundProps);
\r
713 } else if (listItemProp.isComplex()) {
\r
714 // Just recurse to handle this. Note that since this is a list of complex,
\r
715 // which should look like listName/*/... we add 2 to the path start index
\r
716 findAuthRefPropertiesInProperty(foundProps, listItemProp, arci,
\r
717 pathStartIndex + 2, refNameToMatch);
\r
719 logger.error("Configuration for authRefs does not match schema structure: "
\r
720 + arci.pathEls.toString());
\r
724 } else if (prop.isComplex()) {
\r
725 String localPropName = arci.pathEls[pathStartIndex];
\r
727 Property localProp = prop.get(localPropName);
\r
728 // Now just recurse, pushing down the path 1 step
\r
729 findAuthRefPropertiesInProperty(foundProps, localProp, arci,
\r
730 pathStartIndex, refNameToMatch);
\r
731 } catch (PropertyNotFoundException pnfe) {
\r
732 logger.error("Could not find property: [" + localPropName + "] in path: "
\r
733 + arci.getFullPath());
\r
734 // Fall through - ari will be null and we will continue...
\r
737 logger.error("Configuration for authRefs does not match schema structure: "
\r
738 + arci.pathEls.toString());
\r
742 foundProps.add(ari); //FIXME: REM - This is dead code. 'ari' is never touched after being initalized to null. Why?
\r
748 private static void addARIifMatches(
\r
749 String refNameToMatch,
\r
750 AuthRefConfigInfo arci,
\r
752 List<AuthRefInfo> foundProps) {
\r
753 // Need to either match a passed refName
\r
754 // OR have no refName to match but be non-empty
\r
756 String value = (String) prop.getValue();
\r
757 if (((refNameToMatch != null) && refNameToMatch.equals(value))
\r
758 || ((refNameToMatch == null) && Tools.notBlank(value))) {
\r
760 logger.debug("Found a match on property: " + prop.getPath() + " with value: [" + value + "]");
\r
761 AuthRefInfo ari = new AuthRefInfo(arci, prop);
\r
762 foundProps.add(ari);
\r
764 } catch (PropertyException pe) {
\r
765 logger.debug("PropertyException on: " + prop.getPath() + pe.getLocalizedMessage());
\r
770 * Identifies whether the refName was found in the supplied field. If passed
\r
771 * a new RefName, will set that into fields in which the old one was found.
\r
773 * Only works for: * Scalar fields * Repeatable scalar fields (aka
\r
774 * multi-valued fields)
\r
776 * Does not work for: * Structured fields (complexTypes) * Repeatable
\r
777 * structured fields (repeatable complexTypes) private static int
\r
778 * refNameFoundInField(String oldRefName, Property fieldValue, String
\r
779 * newRefName) { int nFound = 0; if (fieldValue instanceof List) {
\r
780 * List<Property> fieldValueList = (List) fieldValue; for (Property
\r
781 * listItemValue : fieldValueList) { try { if ((listItemValue instanceof
\r
782 * StringProperty) &&
\r
783 * oldRefName.equalsIgnoreCase((String)listItemValue.getValue())) {
\r
784 * nFound++; if(newRefName!=null) { fieldValue.setValue(newRefName); } else
\r
785 * { // We cannot quit after the first, if we are replacing values. // If we
\r
786 * are just looking (not replacing), finding one is enough. break; } } }
\r
787 * catch( PropertyException pe ) {} } } else { try { if ((fieldValue
\r
788 * instanceof StringProperty) &&
\r
789 * oldRefName.equalsIgnoreCase((String)fieldValue.getValue())) { nFound++;
\r
790 * if(newRefName!=null) { fieldValue.setValue(newRefName); } } } catch(
\r
791 * PropertyException pe ) {} } return nFound; }
\r