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.Collection;
\r
27 import java.util.HashMap;
\r
28 import java.util.Iterator;
\r
29 import java.util.List;
\r
30 import java.util.Map;
\r
32 import org.nuxeo.ecm.core.api.ClientException;
\r
33 import org.nuxeo.ecm.core.api.DocumentModel;
\r
34 import org.nuxeo.ecm.core.api.DocumentModelList;
\r
35 import org.nuxeo.ecm.core.api.model.Property;
\r
36 import org.nuxeo.ecm.core.api.model.PropertyException;
\r
37 import org.nuxeo.ecm.core.api.model.PropertyNotFoundException;
\r
38 import org.nuxeo.ecm.core.api.model.impl.primitives.StringProperty;
\r
39 import org.nuxeo.ecm.core.api.repository.RepositoryInstance;
\r
40 import org.slf4j.Logger;
\r
41 import org.slf4j.LoggerFactory;
\r
43 import org.collectionspace.services.client.PoxPayloadIn;
\r
44 import org.collectionspace.services.client.PoxPayloadOut;
\r
45 import org.collectionspace.services.common.ServiceMain;
\r
46 import org.collectionspace.services.common.context.ServiceContext;
\r
47 import org.collectionspace.services.common.context.AbstractServiceContextImpl;
\r
48 import org.collectionspace.services.common.api.RefNameUtils;
\r
49 import org.collectionspace.services.common.api.Tools;
\r
50 import org.collectionspace.services.common.api.RefNameUtils.AuthorityTermInfo;
\r
51 import org.collectionspace.services.common.authorityref.AuthorityRefDocList;
\r
52 import org.collectionspace.services.common.authorityref.AuthorityRefList;
\r
53 import org.collectionspace.services.common.config.TenantBindingConfigReaderImpl;
\r
54 import org.collectionspace.services.common.context.ServiceBindingUtils;
\r
55 import org.collectionspace.services.common.document.DocumentException;
\r
56 import org.collectionspace.services.common.document.DocumentFilter;
\r
57 import org.collectionspace.services.common.document.DocumentNotFoundException;
\r
58 import org.collectionspace.services.common.document.DocumentUtils;
\r
59 import org.collectionspace.services.common.document.DocumentWrapper;
\r
60 import org.collectionspace.services.common.query.QueryManager;
\r
61 import org.collectionspace.services.common.repository.RepositoryClient;
\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
69 import com.sun.xml.bind.v2.runtime.unmarshaller.XsiNilLoader.Array;
\r
72 * RefNameServiceUtils is a collection of services utilities related to refName
\r
75 * $LastChangedRevision: $ $LastChangedDate: $
\r
77 public class RefNameServiceUtils {
\r
79 public static class AuthRefConfigInfo {
\r
81 public String getQualifiedDisplayName() {
\r
82 return (Tools.isBlank(schema))
\r
83 ? displayName : DocumentUtils.appendSchemaName(schema, displayName);
\r
86 public String getDisplayName() {
\r
90 public void setDisplayName(String displayName) {
\r
91 this.displayName = displayName;
\r
96 public String getSchema() {
\r
100 public void setSchema(String schema) {
\r
101 this.schema = schema;
\r
104 public String getFullPath() {
\r
108 public void setFullPath(String fullPath) {
\r
109 this.fullPath = fullPath;
\r
112 protected String[] pathEls;
\r
114 public AuthRefConfigInfo(AuthRefConfigInfo arci) {
\r
115 this.displayName = arci.displayName;
\r
116 this.schema = arci.schema;
\r
117 this.fullPath = arci.fullPath;
\r
118 this.pathEls = arci.pathEls;
\r
119 // Skip the pathElse check, since we are creatign from another (presumably valid) arci.
\r
122 public AuthRefConfigInfo(String displayName, String schema, String fullPath, String[] pathEls) {
\r
123 this.displayName = displayName;
\r
124 this.schema = schema;
\r
125 this.fullPath = fullPath;
\r
126 this.pathEls = pathEls;
\r
130 // Split a config value string like "intakes_common:collector", or
\r
131 // "collectionobjects_common:contentPeoples|contentPeople"
\r
132 // "collectionobjects_common:assocEventGroupList/*/assocEventPlace"
\r
133 // If has a pipe ('|') second part is a displayLabel, and first is path
\r
134 // Otherwise, entry is a path, and can use the last pathElement as displayName
\r
135 // Should be schema qualified.
\r
136 public AuthRefConfigInfo(String configString) {
\r
137 String[] pair = configString.split("\\|", 2);
\r
139 String displayName, fullPath;
\r
140 if (pair.length == 1) {
\r
141 // no label specifier, so we'll defer getting label
\r
142 fullPath = pair[0];
\r
143 pathEls = pair[0].split("/");
\r
144 displayName = pathEls[pathEls.length - 1];
\r
146 fullPath = pair[0];
\r
147 pathEls = pair[0].split("/");
\r
148 displayName = pair[1];
\r
150 String[] schemaSplit = pathEls[0].split(":", 2);
\r
152 if (schemaSplit.length == 1) { // schema not specified
\r
155 schema = schemaSplit[0];
\r
156 if (pair.length == 1 && pathEls.length == 1) { // simplest case of field in top level schema, no labelll
\r
157 displayName = schemaSplit[1]; // Have to fix up displayName to have no schema
\r
160 this.displayName = displayName;
\r
161 this.schema = schema;
\r
162 this.fullPath = fullPath;
\r
163 this.pathEls = pathEls;
\r
167 protected void checkPathEls() {
\r
168 int len = pathEls.length;
\r
170 throw new InternalError("Bad values in authRef info - caller screwed up:" + fullPath);
\r
172 // Handle case of them putting a leading slash on the path
\r
173 if (len > 1 && pathEls[0].endsWith(":")) {
\r
175 String[] newArray = new String[len];
\r
176 newArray[0] = pathEls[0] + pathEls[1];
\r
178 System.arraycopy(pathEls, 2, newArray, 1, len - 1);
\r
180 pathEls = newArray;
\r
185 public static class AuthRefInfo extends AuthRefConfigInfo {
\r
187 public Property getProperty() {
\r
191 public void setProperty(Property property) {
\r
192 this.property = property;
\r
196 public AuthRefInfo(String displayName, String schema, String fullPath, String[] pathEls, Property prop) {
\r
197 super(displayName, schema, fullPath, pathEls);
\r
198 this.property = prop;
\r
201 public AuthRefInfo(AuthRefConfigInfo arci, Property prop) {
\r
203 this.property = prop;
\r
206 private static final Logger logger = LoggerFactory.getLogger(RefNameServiceUtils.class);
\r
207 private static ArrayList<String> refNameServiceTypes = null;
\r
209 public static List<AuthRefConfigInfo> getConfiguredAuthorityRefs(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx) {
\r
210 List<String> authRefFields =
\r
211 ((AbstractServiceContextImpl) ctx).getAllPartsPropertyValues(
\r
212 ServiceBindingUtils.AUTH_REF_PROP, ServiceBindingUtils.QUALIFIED_PROP_NAMES);
\r
213 ArrayList<AuthRefConfigInfo> authRefsInfo = new ArrayList<AuthRefConfigInfo>(authRefFields.size());
\r
214 for (String spec : authRefFields) {
\r
215 AuthRefConfigInfo arci = new AuthRefConfigInfo(spec);
\r
216 authRefsInfo.add(arci);
\r
218 return authRefsInfo;
\r
221 public static AuthorityRefDocList getAuthorityRefDocs(
\r
222 RepositoryInstance repoSession,
\r
223 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
\r
224 RepositoryClient<PoxPayloadIn, PoxPayloadOut> repoClient,
\r
225 List<String> serviceTypes,
\r
227 String refPropName,
\r
228 DocumentFilter filter, boolean computeTotal)
\r
229 throws DocumentException, DocumentNotFoundException {
\r
230 AuthorityRefDocList wrapperList = new AuthorityRefDocList();
\r
231 AbstractCommonList commonList = (AbstractCommonList) wrapperList;
\r
232 int pageNum = filter.getStartPage();
\r
233 int pageSize = filter.getPageSize();
\r
234 commonList.setPageNum(pageNum);
\r
235 commonList.setPageSize(pageSize);
\r
236 List<AuthorityRefDocList.AuthorityRefDocItem> list =
\r
237 wrapperList.getAuthorityRefDocItem();
\r
239 Map<String, ServiceBindingType> queriedServiceBindings = new HashMap<String, ServiceBindingType>();
\r
240 Map<String, List<AuthRefConfigInfo>> authRefFieldsByService = new HashMap<String, List<AuthRefConfigInfo>>();
\r
242 RepositoryJavaClientImpl nuxeoRepoClient = (RepositoryJavaClientImpl) repoClient;
\r
244 DocumentModelList docList = findAuthorityRefDocs(ctx, repoClient, repoSession,
\r
245 serviceTypes, refName, refPropName, queriedServiceBindings, authRefFieldsByService,
\r
246 filter.getWhereClause(), null, pageSize, pageNum, computeTotal);
\r
248 if (docList == null) { // found no authRef fields - nothing to process
\r
249 return wrapperList;
\r
251 // Set num of items in list. this is useful to our testing framework.
\r
252 commonList.setItemsInPage(docList.size());
\r
253 // set the total result size
\r
254 commonList.setTotalItems(docList.totalSize());
\r
255 // set the fieldsReturned list. Even though this is a fixed schema, app layer treats
\r
256 // this like other abstract common lists
\r
258 * <xs:element name="docType" type="xs:string" minOccurs="1" />
\r
259 * <xs:element name="docId" type="xs:string" minOccurs="1" />
\r
260 * <xs:element name="docNumber" type="xs:string" minOccurs="0" />
\r
261 * <xs:element name="docName" type="xs:string" minOccurs="0" />
\r
262 * <xs:element name="sourceField" type="xs:string" minOccurs="1" />
\r
263 * <xs:element name="uri" type="xs:anyURI" minOccurs="1" />
\r
264 * <xs:element name="updatedAt" type="xs:string" minOccurs="1" />
\r
265 * <xs:element name="workflowState" type="xs:string" minOccurs="1"
\r
268 String fieldList = "docType|docId|docNumber|docName|sourceField|uri|updatedAt|workflowState";
\r
269 commonList.setFieldsReturned(fieldList);
\r
271 int nRefsFound = processRefObjsDocList(docList, refName, queriedServiceBindings, authRefFieldsByService, // the actual list size needs to be updated to the size of "list"
\r
273 if (logger.isDebugEnabled() && (nRefsFound < docList.size())) {
\r
274 logger.debug("Internal curiosity: got fewer matches of refs than # docs matched..."); // We found a ref to ourself and have excluded it.
\r
276 } catch (Exception e) {
\r
277 logger.error("Could not retrieve the Nuxeo repository", e);
\r
278 wrapperList = null;
\r
281 return wrapperList;
\r
284 private static ArrayList<String> getRefNameServiceTypes() {
\r
285 if (refNameServiceTypes == null) {
\r
286 refNameServiceTypes = new ArrayList<String>();
\r
287 refNameServiceTypes.add(ServiceBindingUtils.SERVICE_TYPE_AUTHORITY);
\r
288 refNameServiceTypes.add(ServiceBindingUtils.SERVICE_TYPE_OBJECT);
\r
289 refNameServiceTypes.add(ServiceBindingUtils.SERVICE_TYPE_PROCEDURE);
\r
291 return refNameServiceTypes;
\r
293 // Seems like a good value - no real data to set this well.
\r
294 // private static final int N_OBJS_TO_UPDATE_PER_LOOP = 100;
\r
295 // FIXME: This value is set to 3 during debugging; needs to be set higher during production - ADR 2012-07-10
\r
296 private static final int N_OBJS_TO_UPDATE_PER_LOOP = 3;
\r
298 public static int updateAuthorityRefDocs(
\r
299 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
\r
300 RepositoryClient<PoxPayloadIn, PoxPayloadOut> repoClient,
\r
301 RepositoryInstance repoSession,
\r
304 String refPropName) throws Exception {
\r
305 Map<String, ServiceBindingType> queriedServiceBindings = new HashMap<String, ServiceBindingType>();
\r
306 Map<String, List<AuthRefConfigInfo>> authRefFieldsByService = new HashMap<String, List<AuthRefConfigInfo>>();
\r
308 int docsScanned = 0;
\r
309 int nRefsFound = 0;
\r
310 int currentPage = 0;
\r
311 int docsInCurrentPage = 0;
\r
312 final String WHERE_CLAUSE_ADDITIONS_VALUE = null;
\r
313 final String ORDER_BY_VALUE = "collectionspace_core:createdAt";
\r
315 if (!(repoClient instanceof RepositoryJavaClientImpl)) {
\r
316 throw new InternalError("updateAuthorityRefDocs() called with unknown repoClient type!");
\r
318 try { // REM - How can we deal with transaction and timeout issues here.
\r
319 final int pageSize = N_OBJS_TO_UPDATE_PER_LOOP;
\r
320 DocumentModelList docList;
\r
321 boolean morePages = true;
\r
322 while (morePages) {
\r
324 docList = findAuthorityRefDocs(ctx, repoClient, repoSession,
\r
325 getRefNameServiceTypes(), oldRefName, refPropName,
\r
326 queriedServiceBindings, authRefFieldsByService, WHERE_CLAUSE_ADDITIONS_VALUE, ORDER_BY_VALUE, pageSize, currentPage, false);
\r
328 if (docList == null) {
\r
329 logger.debug("updateAuthorityRefDocs: no documents could be found that referenced the old refName");
\r
332 docsInCurrentPage = docList.size();
\r
333 logger.debug("updateAuthorityRefDocs: current page=" + currentPage + " documents included in page=" + docsInCurrentPage);
\r
334 if (docsInCurrentPage == 0) {
\r
335 logger.debug("updateAuthorityRefDocs: no more documents requiring refName updates could be found");
\r
338 if (docsInCurrentPage < pageSize) {
\r
339 logger.debug("updateAuthorityRefDocs: assuming no more documents requiring refName updates will be found, as docsInCurrentPage < pageSize");
\r
343 int nRefsFoundThisPage = processRefObjsDocList(docList, oldRefName, queriedServiceBindings, authRefFieldsByService,
\r
345 if (nRefsFoundThisPage > 0) {
\r
346 ((RepositoryJavaClientImpl) repoClient).saveDocListWithoutHandlerProcessing(ctx, repoSession, docList, true);
\r
347 nRefsFound += nRefsFoundThisPage;
\r
350 // FIXME: Per REM, set a limit of num objects - something like
\r
351 // 1000K objects - and also add a log Warning after some threshold
\r
352 docsScanned += docsInCurrentPage;
\r
358 } catch (Exception e) {
\r
359 logger.error("Internal error updating the AuthorityRefDocs: " + e.getLocalizedMessage());
\r
360 logger.debug(Tools.errorToString(e, true));
\r
363 logger.debug("updateAuthorityRefDocs replaced a total of " + nRefsFound + " authority references, within as many as " + docsScanned + " scanned document(s)");
\r
367 private static DocumentModelList findAuthorityRefDocs(
\r
368 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
\r
369 RepositoryClient<PoxPayloadIn, PoxPayloadOut> repoClient,
\r
370 RepositoryInstance repoSession, List<String> serviceTypes,
\r
372 String refPropName,
\r
373 Map<String, ServiceBindingType> queriedServiceBindings,
\r
374 Map<String, List<AuthRefConfigInfo>> authRefFieldsByService,
\r
375 String whereClauseAdditions,
\r
376 String orderByClause,
\r
379 boolean computeTotal) throws DocumentException, DocumentNotFoundException {
\r
381 // Get the service bindings for this tenant
\r
382 TenantBindingConfigReaderImpl tReader =
\r
383 ServiceMain.getInstance().getTenantBindingConfigReader();
\r
384 // We need to get all the procedures, authorities, and objects.
\r
385 List<ServiceBindingType> servicebindings = tReader.getServiceBindingsByType(ctx.getTenantId(), serviceTypes);
\r
386 if (servicebindings == null || servicebindings.isEmpty()) {
\r
387 logger.error("RefNameServiceUtils.getAuthorityRefDocs: No services bindings found, cannot proceed!");
\r
390 // Filter the list for current user rights
\r
391 servicebindings = SecurityUtils.getReadableServiceBindingsForCurrentUser(servicebindings);
\r
393 ArrayList<String> docTypes = new ArrayList<String>();
\r
395 String query = computeWhereClauseForAuthorityRefDocs(refName, refPropName, docTypes, servicebindings,
\r
396 queriedServiceBindings, authRefFieldsByService);
\r
397 if (query == null) { // found no authRef fields - nothing to query
\r
400 // Additional qualifications, like workflow state
\r
401 if (Tools.notBlank(whereClauseAdditions)) {
\r
402 query += " AND " + whereClauseAdditions;
\r
404 // Now we have to issue the search
\r
405 RepositoryJavaClientImpl nuxeoRepoClient = (RepositoryJavaClientImpl) repoClient;
\r
406 DocumentWrapper<DocumentModelList> docListWrapper = nuxeoRepoClient.findDocs(ctx, repoSession,
\r
407 docTypes, query, pageSize, pageNum, computeTotal);
\r
408 // Now we gather the info for each document into the list and return
\r
409 DocumentModelList docList = docListWrapper.getWrappedObject();
\r
412 private static final boolean READY_FOR_COMPLEX_QUERY = true;
\r
414 private static String computeWhereClauseForAuthorityRefDocs(
\r
416 String refPropName,
\r
417 ArrayList<String> docTypes,
\r
418 List<ServiceBindingType> servicebindings,
\r
419 Map<String, ServiceBindingType> queriedServiceBindings,
\r
420 Map<String, List<AuthRefConfigInfo>> authRefFieldsByService) {
\r
422 boolean fFirst = true;
\r
423 List<String> authRefFieldPaths;
\r
424 for (ServiceBindingType sb : servicebindings) {
\r
425 // Gets the property names for each part, qualified with the part label (which
\r
426 // is also the table name, the way that the repository works).
\r
427 authRefFieldPaths =
\r
428 ServiceBindingUtils.getAllPartsPropertyValues(sb,
\r
429 refPropName, ServiceBindingUtils.QUALIFIED_PROP_NAMES);
\r
430 if (authRefFieldPaths.isEmpty()) {
\r
433 ArrayList<AuthRefConfigInfo> authRefsInfo = new ArrayList<AuthRefConfigInfo>();
\r
434 for (String spec : authRefFieldPaths) {
\r
435 AuthRefConfigInfo arci = new AuthRefConfigInfo(spec);
\r
436 authRefsInfo.add(arci);
\r
439 String docType = sb.getObject().getName();
\r
440 queriedServiceBindings.put(docType, sb);
\r
441 authRefFieldsByService.put(docType, authRefsInfo);
\r
442 docTypes.add(docType);
\r
445 if (fFirst) { // found no authRef fields - nothing to query
\r
448 // We used to build a complete matches query, but that was too complex.
\r
449 // Just build a keyword query based upon some key pieces - the urn syntax elements and the shortID
\r
450 // Note that this will also match the Item itself, but that will get filtered out when
\r
451 // we compute actual matches.
\r
452 AuthorityTermInfo authTermInfo = RefNameUtils.parseAuthorityTermInfo(refName);
\r
454 String keywords = RefNameUtils.URN_PREFIX
\r
455 + " AND " + (authTermInfo.inAuthority.name != null
\r
456 ? authTermInfo.inAuthority.name : authTermInfo.inAuthority.csid)
\r
457 + " AND " + (authTermInfo.name != null
\r
458 ? authTermInfo.name : authTermInfo.csid);
\r
460 String whereClauseStr = QueryManager.createWhereClauseFromKeywords(keywords);
\r
462 if (logger.isTraceEnabled()) {
\r
463 logger.trace("The 'where' clause to find refObjs is: ", whereClauseStr);
\r
466 return whereClauseStr;
\r
470 * Runs through the list of found docs, processing them. If list is
\r
471 * non-null, then processing means gather the info for items. If list is
\r
472 * null, and newRefName is non-null, then processing means replacing and
\r
473 * updating. If processing/updating, this must be called in teh context of
\r
474 * an open session, and caller must release Session after calling this.
\r
477 private static int processRefObjsDocList(
\r
478 DocumentModelList docList,
\r
480 Map<String, ServiceBindingType> queriedServiceBindings,
\r
481 Map<String, List<AuthRefConfigInfo>> authRefFieldsByService,
\r
482 List<AuthorityRefDocList.AuthorityRefDocItem> list,
\r
483 String newAuthorityRefName) {
\r
484 Iterator<DocumentModel> iter = docList.iterator();
\r
485 int nRefsFoundTotal = 0;
\r
486 while (iter.hasNext()) {
\r
487 DocumentModel docModel = iter.next();
\r
488 AuthorityRefDocList.AuthorityRefDocItem ilistItem;
\r
490 String docType = docModel.getDocumentType().getName();
\r
491 docType = ServiceBindingUtils.getUnqualifiedTenantDocType(docType);
\r
492 ServiceBindingType sb = queriedServiceBindings.get(docType);
\r
494 throw new RuntimeException(
\r
495 "getAuthorityRefDocs: No Service Binding for docType: " + docType);
\r
497 String serviceContextPath = "/" + sb.getName().toLowerCase() + "/";
\r
499 if (list == null) { // no list - should be update refName case.
\r
500 if (newAuthorityRefName == null) {
\r
501 throw new InternalError("processRefObjsDocList() called with neither an itemList nor a new RefName!");
\r
504 } else { // Have a list - refObjs case
\r
505 if (newAuthorityRefName != null) {
\r
506 throw new InternalError("processRefObjsDocList() called with both an itemList and a new RefName!");
\r
508 ilistItem = new AuthorityRefDocList.AuthorityRefDocItem();
\r
509 String csid = NuxeoUtils.getCsid(docModel);//NuxeoUtils.extractId(docModel.getPathAsString());
\r
510 ilistItem.setDocId(csid);
\r
511 ilistItem.setUri(serviceContextPath + csid);
\r
513 ilistItem.setWorkflowState(docModel.getCurrentLifeCycleState());
\r
514 ilistItem.setUpdatedAt(DocHandlerBase.getUpdatedAtAsString(docModel));
\r
515 } catch (Exception e) {
\r
516 logger.error("Error getting core values for doc [" + csid + "]: " + e.getLocalizedMessage());
\r
518 // The id and URI are the same on all doctypes
\r
519 ilistItem.setDocType(docType);
\r
520 ilistItem.setDocNumber(
\r
521 ServiceBindingUtils.getMappedFieldInDoc(sb, ServiceBindingUtils.OBJ_NUMBER_PROP, docModel));
\r
522 ilistItem.setDocName(
\r
523 ServiceBindingUtils.getMappedFieldInDoc(sb, ServiceBindingUtils.OBJ_NAME_PROP, docModel));
\r
525 // Now, we have to loop over the authRefFieldsByService to figure
\r
526 // out which field(s) matched this.
\r
527 List<AuthRefConfigInfo> matchingAuthRefFields = authRefFieldsByService.get(docType);
\r
528 if (matchingAuthRefFields == null || matchingAuthRefFields.isEmpty()) {
\r
529 throw new RuntimeException(
\r
530 "getAuthorityRefDocs: internal logic error: can't fetch authRefFields for DocType.");
\r
532 //String authRefAncestorField = "";
\r
533 //String authRefDescendantField = "";
\r
534 //String sourceField = "";
\r
535 int nRefsFoundInDoc = 0;
\r
537 ArrayList<RefNameServiceUtils.AuthRefInfo> foundProps = new ArrayList<RefNameServiceUtils.AuthRefInfo>();
\r
539 findAuthRefPropertiesInDoc(docModel, matchingAuthRefFields, refName, foundProps);
\r
540 for (RefNameServiceUtils.AuthRefInfo ari : foundProps) {
\r
541 if (ilistItem != null) {
\r
542 if (nRefsFoundInDoc == 0) { // First one?
\r
543 ilistItem.setSourceField(ari.getQualifiedDisplayName());
\r
544 } else { // duplicates from one object
\r
545 ilistItem = cloneAuthRefDocItem(ilistItem, ari.getQualifiedDisplayName());
\r
547 list.add(ilistItem);
\r
548 } else { // update refName case
\r
549 Property propToUpdate = ari.getProperty();
\r
550 propToUpdate.setValue(newAuthorityRefName);
\r
554 } catch (ClientException ce) {
\r
555 throw new RuntimeException(
\r
556 "getAuthorityRefDocs: Problem fetching values from repo: " + ce.getLocalizedMessage());
\r
558 if (nRefsFoundInDoc == 0) {
\r
560 "getAuthorityRefDocs: Result: "
\r
561 + docType + " [" + NuxeoUtils.getCsid(docModel)
\r
562 + "] does not reference ["
\r
565 nRefsFoundTotal += nRefsFoundInDoc;
\r
567 return nRefsFoundTotal;
\r
570 private static AuthorityRefDocList.AuthorityRefDocItem cloneAuthRefDocItem(
\r
571 AuthorityRefDocList.AuthorityRefDocItem ilistItem, String sourceField) {
\r
572 AuthorityRefDocList.AuthorityRefDocItem newlistItem = new AuthorityRefDocList.AuthorityRefDocItem();
\r
573 newlistItem.setDocId(ilistItem.getDocId());
\r
574 newlistItem.setDocName(ilistItem.getDocName());
\r
575 newlistItem.setDocNumber(ilistItem.getDocNumber());
\r
576 newlistItem.setDocType(ilistItem.getDocType());
\r
577 newlistItem.setUri(ilistItem.getUri());
\r
578 newlistItem.setSourceField(sourceField);
\r
579 return newlistItem;
\r
582 public static List<AuthRefInfo> findAuthRefPropertiesInDoc(
\r
583 DocumentModel docModel,
\r
584 List<AuthRefConfigInfo> authRefFieldInfo,
\r
585 String refNameToMatch,
\r
586 List<AuthRefInfo> foundProps) {
\r
587 // Assume that authRefFieldInfo is keyed by the field name (possibly mapped for UI)
\r
588 // and the values are elPaths to the field, where intervening group structures in
\r
589 // lists of complex structures are replaced with "*". Thus, valid paths include
\r
590 // the following (note that the ServiceBindingUtils prepend schema names to configured values):
\r
591 // "schemaname:fieldname"
\r
592 // "schemaname:scalarlistname"
\r
593 // "schemaname:complexfieldname/fieldname"
\r
594 // "schemaname:complexlistname/*/fieldname"
\r
595 // "schemaname:complexlistname/*/scalarlistname"
\r
596 // "schemaname:complexlistname/*/complexfieldname/fieldname"
\r
597 // "schemaname:complexlistname/*/complexlistname/*/fieldname"
\r
599 for (AuthRefConfigInfo arci : authRefFieldInfo) {
\r
601 // Get first property and work down as needed.
\r
602 Property prop = docModel.getProperty(arci.pathEls[0]);
\r
603 findAuthRefPropertiesInProperty(foundProps, prop, arci, 0, refNameToMatch);
\r
604 } catch (Exception e) {
\r
605 logger.error("Problem fetching property: " + arci.pathEls[0]);
\r
611 public static List<AuthRefInfo> findAuthRefPropertiesInProperty(
\r
612 List<AuthRefInfo> foundProps,
\r
614 AuthRefConfigInfo arci,
\r
615 int pathStartIndex, // Supports recursion and we work down the path
\r
616 String refNameToMatch) {
\r
617 if (pathStartIndex >= arci.pathEls.length) {
\r
618 throw new ArrayIndexOutOfBoundsException("Index = " + pathStartIndex + " for path: "
\r
619 + arci.pathEls.toString());
\r
621 AuthRefInfo ari = null;
\r
622 if (prop == null) {
\r
626 if (prop instanceof StringProperty) { // scalar string
\r
627 addARIifMatches(refNameToMatch, arci, prop, foundProps);
\r
628 } else if (prop instanceof List) {
\r
629 List<Property> propList = (List<Property>) prop;
\r
630 // run through list. Must either be list of Strings, or Complex
\r
631 for (Property listItemProp : propList) {
\r
632 if (listItemProp instanceof StringProperty) {
\r
633 if (arci.pathEls.length - pathStartIndex != 1) {
\r
634 logger.error("Configuration for authRefs does not match schema structure: "
\r
635 + arci.pathEls.toString());
\r
638 addARIifMatches(refNameToMatch, arci, listItemProp, foundProps);
\r
640 } else if (listItemProp.isComplex()) {
\r
641 // Just recurse to handle this. Note that since this is a list of complex,
\r
642 // which should look like listName/*/... we add 2 to the path start index
\r
643 findAuthRefPropertiesInProperty(foundProps, listItemProp, arci,
\r
644 pathStartIndex + 2, refNameToMatch);
\r
646 logger.error("Configuration for authRefs does not match schema structure: "
\r
647 + arci.pathEls.toString());
\r
651 } else if (prop.isComplex()) {
\r
652 String localPropName = arci.pathEls[pathStartIndex];
\r
654 Property localProp = prop.get(localPropName);
\r
655 // Now just recurse, pushing down the path 1 step
\r
656 findAuthRefPropertiesInProperty(foundProps, localProp, arci,
\r
657 pathStartIndex, refNameToMatch);
\r
658 } catch (PropertyNotFoundException pnfe) {
\r
659 logger.error("Could not find property: [" + localPropName + "] in path: "
\r
660 + arci.getFullPath());
\r
661 // Fall through - ari will be null and we will continue...
\r
664 logger.error("Configuration for authRefs does not match schema structure: "
\r
665 + arci.pathEls.toString());
\r
669 foundProps.add(ari); //FIXME: REM - This is dead code. 'ari' is never touched after being initalized to null. Why?
\r
675 private static void addARIifMatches(
\r
676 String refNameToMatch,
\r
677 AuthRefConfigInfo arci,
\r
679 List<AuthRefInfo> foundProps) {
\r
680 // Need to either match a passed refName
\r
681 // OR have no refName to match but be non-empty
\r
683 String value = (String) prop.getValue();
\r
684 if (((refNameToMatch != null) && refNameToMatch.equals(value))
\r
685 || ((refNameToMatch == null) && Tools.notBlank(value))) {
\r
687 logger.debug("Found a match on property: " + prop.getPath() + " with value: [" + value + "]");
\r
688 AuthRefInfo ari = new AuthRefInfo(arci, prop);
\r
689 foundProps.add(ari);
\r
691 } catch (PropertyException pe) {
\r
692 logger.debug("PropertyException on: " + prop.getPath() + pe.getLocalizedMessage());
\r
697 * Identifies whether the refName was found in the supplied field. If passed
\r
698 * a new RefName, will set that into fields in which the old one was found.
\r
700 * Only works for: * Scalar fields * Repeatable scalar fields (aka
\r
701 * multi-valued fields)
\r
703 * Does not work for: * Structured fields (complexTypes) * Repeatable
\r
704 * structured fields (repeatable complexTypes) private static int
\r
705 * refNameFoundInField(String oldRefName, Property fieldValue, String
\r
706 * newRefName) { int nFound = 0; if (fieldValue instanceof List) {
\r
707 * List<Property> fieldValueList = (List) fieldValue; for (Property
\r
708 * listItemValue : fieldValueList) { try { if ((listItemValue instanceof
\r
709 * StringProperty) &&
\r
710 * oldRefName.equalsIgnoreCase((String)listItemValue.getValue())) {
\r
711 * nFound++; if(newRefName!=null) { fieldValue.setValue(newRefName); } else
\r
712 * { // We cannot quit after the first, if we are replacing values. // If we
\r
713 * are just looking (not replacing), finding one is enough. break; } } }
\r
714 * catch( PropertyException pe ) {} } } else { try { if ((fieldValue
\r
715 * instanceof StringProperty) &&
\r
716 * oldRefName.equalsIgnoreCase((String)fieldValue.getValue())) { nFound++;
\r
717 * if(newRefName!=null) { fieldValue.setValue(newRefName); } } } catch(
\r
718 * PropertyException pe ) {} } return nFound; }
\r