1 package org.collectionspace.services.listener;
3 import java.io.BufferedReader;
4 import java.io.IOException;
5 import java.io.InputStream;
6 import java.io.InputStreamReader;
7 import java.sql.Connection;
8 import java.sql.ResultSet;
9 import java.sql.SQLException;
10 import java.sql.Statement;
11 import java.util.ArrayList;
12 import java.util.GregorianCalendar;
13 import java.util.HashMap;
14 import java.util.List;
16 import org.apache.commons.logging.Log;
17 import org.apache.commons.logging.LogFactory;
18 import org.collectionspace.services.client.workflow.WorkflowClient;
19 import org.collectionspace.services.common.api.GregorianCalendarDateTimeUtils;
20 import org.collectionspace.services.common.api.RefNameUtils;
21 import org.collectionspace.services.common.api.Tools;
22 import org.collectionspace.services.common.storage.JDBCTools;
23 import org.collectionspace.services.movement.nuxeo.MovementConstants;
24 import org.collectionspace.services.nuxeo.util.NuxeoUtils;
25 import org.nuxeo.ecm.core.api.ClientException;
26 import org.nuxeo.ecm.core.api.CoreSession;
27 import org.nuxeo.ecm.core.api.DocumentModel;
28 import org.nuxeo.ecm.core.api.DocumentModelList;
29 import org.nuxeo.ecm.core.event.Event;
30 import org.nuxeo.ecm.core.event.EventContext;
31 import org.nuxeo.ecm.core.event.EventListener;
32 import org.nuxeo.ecm.core.event.impl.DocumentEventContext;
34 public class UpdateObjectLocationOnMove implements EventListener {
36 // FIXME: We might experiment here with using log4j instead of Apache Commons Logging;
37 // am using the latter to follow Ray's pattern for now
38 private final Log logger = LogFactory.getLog(UpdateObjectLocationOnMove.class);
39 // FIXME: Make the following message, or its equivalent, a constant usable by all event listeners
40 private final String NO_FURTHER_PROCESSING_MESSAGE =
41 "This event listener will not continue processing this event ...";
42 private final List<String> relevantDocTypesList = new ArrayList<String>();
43 GregorianCalendar EARLIEST_COMPARISON_DATE = new GregorianCalendar(1600, 1, 1);
44 private final String DATABASE_RESOURCE_DIRECTORY_NAME = "db";
45 // FIXME: Currently hard-coded; get this database name value from JDBC utilities or equivalent
46 private final String DATABASE_SYSTEM_NAME = "postgresql";
47 private final static String STORED_FUNCTION_NAME = "computecurrentlocation";
48 private final static String SQL_FILENAME_EXTENSION = ".sql";
49 private final String SQL_RESOURCE_PATH =
50 DATABASE_RESOURCE_DIRECTORY_NAME + "/"
51 + DATABASE_SYSTEM_NAME + "/"
52 + STORED_FUNCTION_NAME + SQL_FILENAME_EXTENSION;
53 // The name of the relevant column in the JDBC ResultSet is currently identical
54 // to the function name, regardless of the 'SELECT ... AS' clause in the SQL query.
55 private final static String COMPUTED_CURRENT_LOCATION_COLUMN = STORED_FUNCTION_NAME;
56 // FIXME: Get this line separator value from already-declared constant elsewhere, if available
57 private final String LINE_SEPARATOR = System.getProperty("line.separator");
58 private final static String RELATIONS_COMMON_SCHEMA = "relations_common"; // FIXME: Get from external constant
59 final String RELATION_DOCTYPE = "Relation"; // FIXME: Get from external constant
60 private final static String SUBJECT_CSID_PROPERTY = "subjectCsid"; // FIXME: Get from external constant
61 private final static String OBJECT_CSID_PROPERTY = "objectCsid"; // FIXME: Get from external constant
62 private final static String COLLECTIONOBJECTS_COMMON_SCHEMA = "collectionobjects_common"; // FIXME: Get from external constant
63 final String COLLECTIONOBJECT_DOCTYPE = "CollectionObject"; // FIXME: Get from external constant
64 final String COMPUTED_CURRENT_LOCATION_PROPERTY = "computedCurrentLocation"; // FIXME: Create and then get from external constant
65 final String MOVEMENTS_COMMON_SCHEMA = "movements_common"; // FIXME: Get from external constant
66 private final static String MOVEMENT_DOCTYPE = "Movement"; // FIXME: Get from external constant
67 final String LOCATION_DATE_PROPERTY = "locationDate"; // FIXME: Get from external constant
68 final String CURRENT_LOCATION_PROPERTY = "currentLocation"; // FIXME: Get from external constant
70 // ####################################################################
71 // FIXME: Per Rick, what happens if a relation record is updated,
72 // that either adds or removes a relation between a Movement
73 // record and a CollectionObject record? Do we need to listen
74 // for that event as well and update the CollectionObject record's
75 // computedCurrentLocation accordingly?
77 // The following code is currently only handling create and
78 // update events affecting Movement records.
79 // ####################################################################
80 // FIXME: We'll likely also need to handle workflow state transition and
81 // deletion events, where the soft or hard deletion of a Movement or
82 // Relation record effectively changes the current location for a CollectionObject.
84 public void handleEvent(Event event) throws ClientException {
86 logger.trace("In handleEvent in UpdateObjectLocationOnMove ...");
88 // FIXME: Check for database product type here.
89 // If our database type is one for which we don't yet
90 // have tested SQL code to perform this operation, return here.
92 EventContext eventContext = event.getContext();
93 if (eventContext == null) {
97 if (!(eventContext instanceof DocumentEventContext)) {
98 logger.debug("This event does not involve a document ...");
99 logger.debug(NO_FURTHER_PROCESSING_MESSAGE);
102 DocumentEventContext docEventContext = (DocumentEventContext) eventContext;
103 DocumentModel docModel = docEventContext.getSourceDocument();
105 // If this event does not involve one of our relevant doctypes,
106 // return without further handling the event.
107 boolean involvesRelevantDocType = false;
108 relevantDocTypesList.add(MovementConstants.NUXEO_DOCTYPE);
109 // FIXME: We will likely need to add the Relation doctype here,
110 // along with additional code to handle such changes.
111 for (String docType : relevantDocTypesList) {
112 if (documentMatchesType(docModel, docType)) {
113 involvesRelevantDocType = true;
117 logger.debug("This event involves a document of type " + docModel.getDocumentType().getName());
118 if (!involvesRelevantDocType) {
119 logger.debug("This event does not involve a document of a relevant type ...");
120 logger.debug(NO_FURTHER_PROCESSING_MESSAGE);
123 if (!isActiveDocument(docModel)) {
124 logger.debug("This event does not involve an active document ...");
125 logger.debug(NO_FURTHER_PROCESSING_MESSAGE);
129 logger.debug("An event involving an active document of the relevant type(s) was received by UpdateObjectLocationOnMove ...");
131 // Test whether a SQL function exists to supply the computed
132 // current location of a CollectionObject.
134 // If the function does not exist in the database, load the
135 // SQL command to create that function from a resource
136 // available to this class, and run a JDBC command to create
137 // that function in the database.
139 // For now, assume this function will be created in the
142 // FIXME: Future work to create per-tenant repositories will
143 // likely require that our JDBC statements connect to the
144 // appropriate tenant-specific database.
146 // It doesn't appear we can reliably create this function via
147 // 'ant create_nuxeo db' during the build process, because
148 // there's a substantial likelihood at that point that
149 // tables referred to by the function (e.g. movements_common
150 // and collectionobjects_common) will not yet exist.
151 // (PostgreSQL will not permit the function to be created if
152 // any of its referred-to tables do not exist.)
153 if (!storedFunctionExists(STORED_FUNCTION_NAME)) {
154 logger.trace("Stored function " + STORED_FUNCTION_NAME + " does NOT exist in the database.");
155 String sql = getStringFromResource(SQL_RESOURCE_PATH);
156 if (Tools.isBlank(sql)) {
157 logger.warn("Could not obtain SQL command to create stored function.");
158 logger.debug(NO_FURTHER_PROCESSING_MESSAGE);
164 result = JDBCTools.executeUpdate(JDBCTools.getDataSource(JDBCTools.NUXEO_REPOSITORY_NAME), sql);
165 } catch (Exception e) {
167 // FIXME: Need to verify that the original '-1' value is preserved if an Exception is caught here.
169 logger.trace("Result of executeUpdate=" + result);
171 logger.warn("Could not create stored function in the database.");
172 logger.debug(NO_FURTHER_PROCESSING_MESSAGE);
175 logger.info("Stored function " + STORED_FUNCTION_NAME + " was successfully created in the database.");
178 logger.trace("Stored function " + STORED_FUNCTION_NAME + " already exists in the database.");
181 String movementCsid = NuxeoUtils.getCsid(docModel);
182 logger.debug("Movement record CSID=" + movementCsid);
184 // FIXME: Temporary, for debugging: check whether the movement record's
185 // location date field value is stale in Nuxeo at this point
186 GregorianCalendar cal = (GregorianCalendar) docModel.getProperty(MOVEMENTS_COMMON_SCHEMA, LOCATION_DATE_PROPERTY);
187 logger.debug("location date=" + GregorianCalendarDateTimeUtils.formatAsISO8601Date(cal));
189 // Find CollectionObject records that are related to this Movement record:
191 // Via an NXQL query, get a list of (non-deleted) relation records where:
192 // * This movement record's CSID is the subject CSID of the relation.
193 // * The object document type is a CollectionObject doctype.
195 // Note: this assumes that every such relation is captured by
196 // relations with Movement-as-subject and CollectionObject-as-object,
197 // logic that matches that of the SQL function to obtain the computed
198 // current location of the CollectionObject.
200 // That may NOT always be the case; it's possible some such relations may
201 // exist only with CollectionObject-as-subject and Movement-as-object.
202 CoreSession coreSession1 = docEventContext.getCoreSession();
203 CoreSession coreSession = docModel.getCoreSession();
204 if (coreSession1 == coreSession || coreSession1.equals(coreSession)) {
205 logger.debug("Core sessions are equal.");
207 logger.debug("Core sessions are NOT equal.");
210 // Check whether closing and opening a transaction here might
211 // flush any hypothetical caching that Nuxeo is doing at this point
213 String query = String.format(
214 "SELECT * FROM %1$s WHERE " // collectionspace_core:tenantId = 1 "
215 + "(relations_common:subjectCsid = '%2$s' "
216 + "AND relations_common:objectDocumentType = '%3$s') "
217 + "AND (ecm:currentLifeCycleState <> 'deleted') "
218 + "AND ecm:isProxy = 0 "
219 + "AND ecm:isCheckedInVersion = 0", RELATION_DOCTYPE, movementCsid, COLLECTIONOBJECT_DOCTYPE);
220 DocumentModelList relatedDocModels = coreSession.query(query);
221 if (relatedDocModels == null || relatedDocModels.isEmpty()) {
224 logger.trace("Found " + relatedDocModels.size() + " related documents.");
227 // Iterate through the list of Relation records found and build
228 // a list of CollectionObject CSIDs, by extracting the object CSIDs
229 // from those Relation records.
231 // FIXME: The following code might be refactored into a generic 'get property
232 // values from a list of document models' method, if this doesn't already exist.
234 List<String> collectionObjectCsids = new ArrayList<String>();
235 for (DocumentModel relatedDocModel : relatedDocModels) {
236 csid = (String) relatedDocModel.getProperty(RELATIONS_COMMON_SCHEMA, OBJECT_CSID_PROPERTY);
237 if (Tools.notBlank(csid)) {
238 collectionObjectCsids.add(csid);
241 if (collectionObjectCsids == null || collectionObjectCsids.isEmpty()) {
242 logger.warn("Could not obtain any CSIDs of related CollectionObject records.");
243 logger.debug(NO_FURTHER_PROCESSING_MESSAGE);
246 logger.debug("Found " + collectionObjectCsids.size() + " CSIDs of related CollectionObject records.");
249 // Iterate through the list of CollectionObject CSIDs found.
250 DocumentModel collectionObjectDocModel = null;
251 String computedCurrentLocationRefName = "";
252 Map<DocumentModel, String> docModelsToUpdate = new HashMap<DocumentModel, String>();
253 for (String collectionObjectCsid : collectionObjectCsids) {
255 // Verify that the CollectionObject record is active.
256 collectionObjectDocModel = getDocModelFromCsid(coreSession, collectionObjectCsid);
257 if (!isActiveDocument(collectionObjectDocModel)) {
261 // Obtain the computed current location of that CollectionObject.
263 // JDBC/SQL query method:
264 // computedCurrentLocationRefName = computeCurrentLocation(collectionObjectCsid);
266 // Nuxeo (NXQL or CMIS) query method, currently with some
267 // non-performant procedural augmentation:
268 computedCurrentLocationRefName = computeCurrentLocation(coreSession, collectionObjectCsid, movementCsid);
269 logger.debug("computedCurrentLocation refName=" + computedCurrentLocationRefName);
271 // Check that the value returned from the SQL function, which
272 // is expected to be a reference (refName) to a storage location
273 // authority term, is, at a minimum:
274 // * Non-null and non-blank. (We need to verify this assumption; can a
275 // CollectionObject's computed current location meaningfully be 'un-set'?)
276 // * Capable of being successfully parsed by an authority item parser;
277 // that is, returning a non-null parse result.
278 if ((Tools.notBlank(computedCurrentLocationRefName)
279 && (RefNameUtils.parseAuthorityTermInfo(computedCurrentLocationRefName) != null))) {
280 logger.debug("refName passes basic validation tests.");
282 // If the value returned from the function passes validation,
283 // compare that value to the value in the computedCurrentLocation
284 // field of that CollectionObject
286 // If the CollectionObject does not already have a
287 // computedCurrentLocation value, or if the two values differ ...
288 String existingComputedCurrentLocationRefName =
289 (String) collectionObjectDocModel.getProperty(COLLECTIONOBJECTS_COMMON_SCHEMA, COMPUTED_CURRENT_LOCATION_PROPERTY);
290 if (Tools.isBlank(existingComputedCurrentLocationRefName)
291 || !computedCurrentLocationRefName.equals(existingComputedCurrentLocationRefName)) {
292 logger.debug("Existing computedCurrentLocation refName=" + existingComputedCurrentLocationRefName);
293 logger.debug("computedCurrentLocation refName requires updating.");
294 // ... identify this CollectionObject's docModel and new field value for subsequent updating
295 docModelsToUpdate.put(collectionObjectDocModel, computedCurrentLocationRefName);
298 logger.debug("computedCurrentLocation refName does NOT require updating.");
303 // For each CollectionObject record that has been identified for updating,
304 // update its computedCurrentLocation field with its computed current
305 // location value returned from the SQL function.
306 for (Map.Entry<DocumentModel, String> entry : docModelsToUpdate.entrySet()) {
307 DocumentModel dmodel = entry.getKey();
308 String newCurrentLocationValue = entry.getValue();
309 dmodel.setProperty(COLLECTIONOBJECTS_COMMON_SCHEMA, COMPUTED_CURRENT_LOCATION_PROPERTY, newCurrentLocationValue);
310 coreSession.saveDocument(dmodel);
311 if (logger.isTraceEnabled()) {
312 String afterUpdateComputedCurrentLocationRefName =
313 (String) dmodel.getProperty(COLLECTIONOBJECTS_COMMON_SCHEMA, COMPUTED_CURRENT_LOCATION_PROPERTY);
314 logger.trace("Following update, new computedCurrentLocation refName value=" + afterUpdateComputedCurrentLocationRefName);
320 // FIXME: Generic methods like many of those below might be split off,
321 // into an event utilities class, base classes, or otherwise. - ADR 2012-12-05
323 // FIXME: Identify whether the equivalent of the documentMatchesType utility
324 // method is already implemented and substitute a call to the latter if so.
325 // This may well already exist.
327 * Identifies whether a document matches a supplied document type.
329 * @param docModel a document model.
330 * @param docType a document type string.
331 * @return true if the document matches the supplied document type; false if
334 private boolean documentMatchesType(DocumentModel docModel, String docType) {
335 if (docModel == null || Tools.isBlank(docType)) {
338 if (docModel.getType().startsWith(docType)) {
346 * Identifies whether a document is an active document; that is, if it is
347 * not a versioned record; not a proxy (symbolic link to an actual record);
348 * and not in the 'deleted' workflow state.
350 * (A note relating the latter: Nuxeo appears to send 'documentModified'
351 * events even on workflow transitions, such when records are 'soft deleted'
352 * by being transitioned to the 'deleted' workflow state.)
355 * @return true if the document is an active document; false if it is not.
357 private boolean isActiveDocument(DocumentModel docModel) {
358 if (docModel == null) {
361 boolean isActiveDocument = false;
363 if (!docModel.isVersion()
364 && !docModel.isProxy()
365 && !docModel.getCurrentLifeCycleState().equals(WorkflowClient.WORKFLOWSTATE_DELETED)) {
366 isActiveDocument = true;
368 } catch (ClientException ce) {
369 logger.warn("Error while identifying whether document is an active document: ", ce);
371 return isActiveDocument;
374 // FIXME: The following method is specific to PostgreSQL, because of
375 // the SQL command executed; it may need to be generalized.
376 // Note: It may be necessary in some cases to provide additional
377 // parameters beyond a function name (such as a function signature)
378 // to uniquely identify a function. So far, however, this need
379 // hasn't arisen in our specific use case here.
381 * Identifies whether a stored function exists in a database.
383 * @param functionname the name of the function.
384 * @return true if the function exists in the database; false if it does
387 private boolean storedFunctionExists(String functionname) {
388 if (Tools.isBlank(functionname)) {
391 boolean storedFunctionExists = false;
392 String sql = "SELECT proname FROM pg_proc WHERE proname='" + functionname + "'";
393 Connection conn = null;
394 Statement stmt = null;
396 boolean storedAutoCommitState = true;
398 conn = JDBCTools.getConnection(JDBCTools.getDataSource(JDBCTools.NUXEO_REPOSITORY_NAME));
399 stmt = conn.createStatement(ResultSet.CONCUR_UPDATABLE, ResultSet.CLOSE_CURSORS_AT_COMMIT);
400 storedAutoCommitState = conn.getAutoCommit();
401 conn.setAutoCommit(true);
402 rs = stmt.executeQuery(sql);
404 storedFunctionExists = true;
408 conn.setAutoCommit(storedAutoCommitState);
410 } catch (Exception e) {
411 logger.debug("Error when identifying whether stored function " + functionname + "exists :", e);
421 conn.setAutoCommit(storedAutoCommitState);
424 } catch (SQLException sqle) {
425 logger.debug("SQL Exception closing statement/connection in "
426 + "UpdateObjectLocationOnMove.storedFunctionExists: "
427 + sqle.getLocalizedMessage());
430 return storedFunctionExists;
434 * Returns the computed current location of a CollectionObject (aka
435 * Cataloging) record.
437 * @param csid the CSID of a CollectionObject record.
438 * @return the computed current location of the CollectionObject record.
440 private String computeCurrentLocation(String csid) {
441 String computedCurrentLocation = "";
442 if (Tools.isBlank(csid)) {
443 return computedCurrentLocation;
445 String sql = String.format("SELECT %1$s('%2$s')", STORED_FUNCTION_NAME, csid);
446 Connection conn = null;
447 Statement stmt = null;
450 conn = JDBCTools.getConnection(JDBCTools.getDataSource(JDBCTools.NUXEO_REPOSITORY_NAME));
451 stmt = conn.createStatement(ResultSet.CONCUR_UPDATABLE, ResultSet.CLOSE_CURSORS_AT_COMMIT);
452 rs = stmt.executeQuery(sql);
454 computedCurrentLocation = rs.getString(COMPUTED_CURRENT_LOCATION_COLUMN);
455 logger.debug("computedCurrentLocation first=" + computedCurrentLocation);
457 // Experiment with performing an update before the query
458 // as a possible means of refreshing data.
459 String updateSql = getStringFromResource(SQL_RESOURCE_PATH);
462 result = JDBCTools.executeUpdate(JDBCTools.getDataSource(JDBCTools.NUXEO_REPOSITORY_NAME), updateSql);
463 } catch (Exception e) {
465 logger.trace("Result of executeUpdate=" + result);
466 // String randomSql = String.format("SELECT now()");
467 // rs = stmt.executeQuery(randomSql);
470 stmt = conn.createStatement();
471 rs = stmt.executeQuery(sql);
473 computedCurrentLocation = rs.getString(COMPUTED_CURRENT_LOCATION_COLUMN);
474 logger.debug("computedCurrentLocation second=" + computedCurrentLocation);
479 } catch (Exception e) {
480 logger.debug("Error when attempting to obtain the computed current location of an object :", e);
492 } catch (SQLException sqle) {
493 logger.debug("SQL Exception closing statement/connection in "
494 + "UpdateObjectLocationOnMove.computeCurrentLocation: "
495 + sqle.getLocalizedMessage());
498 return computedCurrentLocation;
501 // FIXME: A quick first pass, using an only partly query-based technique for
502 // getting the current location, augmented by procedural code.
504 // Should be replaced by a more performant method, based entirely, or nearly so,
507 // E.g. a sample CMIS query for retrieving Movement records related to a CollectionObject;
508 // we can see if the ORDER BY clause can refer to a Movement locationDate field.
510 "SELECT DOC.nuxeo:pathSegment, DOC.dc:title, REL.dc:title,"
511 + "REL.relations_common:objectCsid, REL.relations_common:subjectCsid FROM Movement DOC "
512 + "JOIN Relation REL ON REL.relations_common:objectCsid = DOC.nuxeo:pathSegment "
513 + "WHERE REL.relations_common:subjectCsid = '5b4c617e-53a0-484b-804e' "
514 + "AND DOC.nuxeo:isVersion = false "
515 + "ORDER BY DOC.collectionspace_core:updatedAt DESC";
517 private String computeCurrentLocation(CoreSession session, String collectionObjectCsid,
518 String movementCsid) throws ClientException {
519 String computedCurrentLocation = "";
520 // Get Relation records for Movments related to this CollectionObject
521 String query = String.format(
522 "SELECT * FROM %1$s WHERE " // collectionspace_core:tenantId = 1 "
523 + "(relations_common:subjectCsid = '%2$s' "
524 + "AND relations_common:objectDocumentType = '%3$s') "
525 + "AND (ecm:currentLifeCycleState <> 'deleted') "
526 + "AND ecm:isProxy = 0 "
527 + "AND ecm:isCheckedInVersion = 0 ",
528 RELATION_DOCTYPE, collectionObjectCsid, MOVEMENT_DOCTYPE, movementCsid, COLLECTIONOBJECT_DOCTYPE);
529 logger.debug("query=" + query);
530 DocumentModelList relatedDocModels = session.query(query);
531 if (relatedDocModels == null || relatedDocModels.isEmpty()) {
532 logger.trace("Found " + relatedDocModels.size() + " related documents.");
535 logger.trace("Found " + relatedDocModels.size() + " related documents.");
537 // Get the CollectionObject's current location from the related Movement
538 // record with the most recent location date.
539 GregorianCalendar mostRecentLocationDate = EARLIEST_COMPARISON_DATE;
540 DocumentModel movementDocModel = null;
542 String location = "";
543 for (DocumentModel relatedDocModel : relatedDocModels) {
544 // The object CSID in the relation is the related Movement record's CSID
545 csid = (String) relatedDocModel.getProperty(RELATIONS_COMMON_SCHEMA, OBJECT_CSID_PROPERTY);
546 movementDocModel = getDocModelFromCsid(session, csid);
547 GregorianCalendar locationDate = (GregorianCalendar) movementDocModel.getProperty(MOVEMENTS_COMMON_SCHEMA, LOCATION_DATE_PROPERTY);
548 if (locationDate == null) {
551 if (locationDate.after(mostRecentLocationDate)) {
552 mostRecentLocationDate = locationDate;
553 location = (String) movementDocModel.getProperty(MOVEMENTS_COMMON_SCHEMA, CURRENT_LOCATION_PROPERTY);
555 if (Tools.notBlank(location)) {
556 computedCurrentLocation = location;
559 return computedCurrentLocation;
563 * Returns a string representation of the contents of an input stream.
565 * @param instream an input stream.
566 * @return a string representation of the contents of the input stream.
567 * @throws an IOException if an error occurs when reading the input stream.
569 private String stringFromInputStream(InputStream instream) throws IOException {
570 if (instream == null) {
572 BufferedReader bufreader = new BufferedReader(new InputStreamReader(instream));
573 StringBuilder sb = new StringBuilder();
575 while (line != null) {
577 line = bufreader.readLine();
578 sb.append(LINE_SEPARATOR);
580 return sb.toString();
584 * Returns a string representation of a resource available to the current
587 * @param resourcePath a path to the resource.
588 * @return a string representation of the resource. Returns null if the
589 * resource cannot be read, or if it cannot be successfully represented as a
592 private String getStringFromResource(String resourcePath) {
594 ClassLoader classLoader = getClass().getClassLoader();
595 InputStream instream = classLoader.getResourceAsStream(resourcePath);
596 if (instream == null) {
597 logger.warn("Could not read from resource from path " + resourcePath);
601 str = stringFromInputStream(instream);
602 } catch (IOException ioe) {
603 logger.warn("Could not create string from stream: ", ioe);
609 private DocumentModel getDocModelFromCsid(CoreSession coreSession, String collectionObjectCsid) {
610 DocumentModelList collectionObjectDocModels = null;
612 final String query = "SELECT * FROM "
613 + NuxeoUtils.BASE_DOCUMENT_TYPE
615 + NuxeoUtils.getByNameWhereClause(collectionObjectCsid);
616 collectionObjectDocModels = coreSession.query(query);
617 } catch (Exception e) {
618 logger.warn("Exception in query to get document model for CollectionObject: ", e);
620 if (collectionObjectDocModels == null || collectionObjectDocModels.isEmpty()) {
621 logger.warn("Could not get document models for CollectionObject(s).");
622 } else if (collectionObjectDocModels.size() != 1) {
623 logger.debug("Found more than 1 document with CSID=" + collectionObjectCsid);
625 return collectionObjectDocModels.get(0);