]> git.aero2k.de Git - tmp/jakarta-migration.git/blob
3eb0290e7af058e55f2aff862d279b8ef296c902
[tmp/jakarta-migration.git] /
1 package org.collectionspace.services.listener;
2
3 import java.sql.Connection;
4 import java.sql.ResultSet;
5 import java.sql.SQLException;
6 import java.sql.Statement;
7 import java.util.List;
8
9 import javax.sql.DataSource;
10
11 import org.apache.commons.logging.Log;
12 import org.apache.commons.logging.LogFactory;
13
14 import org.collectionspace.services.client.workflow.WorkflowClient;
15 import org.collectionspace.services.common.api.Tools;
16 import org.collectionspace.services.common.storage.DatabaseProductType;
17 import org.collectionspace.services.common.storage.JDBCTools;
18 import org.collectionspace.services.movement.nuxeo.MovementConstants;
19
20 import org.nuxeo.ecm.core.api.ClientException;
21 import org.nuxeo.ecm.core.api.DocumentModel;
22 import org.nuxeo.ecm.core.event.Event;
23 import org.nuxeo.ecm.core.event.EventContext;
24 import org.nuxeo.ecm.core.event.EventListener;
25 import org.nuxeo.ecm.core.event.impl.DocumentEventContext;
26
27 public class UpdateObjectLocationOnMove implements EventListener {
28
29     // FIXME: We might experiment here with using log4j instead of Apache Commons Logging;
30     // am using the latter to follow Ray's pattern for now
31     final Log logger = LogFactory.getLog(UpdateObjectLocationOnMove.class);
32     private final String DATABASE_RESOURCE_DIRECTORY_NAME = "db";
33     // FIXME: Currently hard-coded; get this from JDBC utilities or equivalent
34     private final String DATABASE_SYSTEM_NAME = "postgresql";
35     private final String STORED_FUNCTION_NAME = "computecurrentlocation";
36
37     // ####################################################################
38     // FIXME: Per Rick, what happens if a relation record is updated,
39     // that either adds or removes a relation between a Movement
40     // record and a CollectionObject record?  Do we need to listen
41     // for that event as well and update the CollectionObject record's
42     // computedCurrentLocation accordingly?
43     //
44     // The following code is currently checking only for creates or
45     // updates to Movement records.
46     // ####################################################################
47     @Override
48     public void handleEvent(Event event) throws ClientException {
49
50         logger.trace("In handleEvent in UpdateObjectLocationOnMove ...");
51
52         // FIXME: Check for database product type here.
53         // If our database type is one for which we don't yet
54         // have tested SQL code to perform this operation, return here.
55
56         EventContext eventContext = event.getContext();
57         if (eventContext == null) {
58             return;
59         }
60         DocumentEventContext docEventContext = (DocumentEventContext) eventContext;
61         DocumentModel docModel = docEventContext.getSourceDocument();
62         if (isMovementDocument(docModel) && isActiveDocument(docModel)) {
63             logger.debug("A create or update event for an active Movement document was received by UpdateObjectLocationOnMove ...");
64
65             // Pseudocode:
66
67             // Test whether a SQL function exists to supply the computed
68             // current location of a CollectionObject.
69             if (storedFunctionExists(STORED_FUNCTION_NAME)) {
70                 logger.debug("Stored function " + STORED_FUNCTION_NAME + "exists.");
71             } else {
72                 logger.debug("Stored function " + STORED_FUNCTION_NAME + "does NOT exist.");
73             }
74             
75             // FIXME: For incremental debugging, as we work through implementing
76             // this pseudocode.
77             return;
78
79             // If it does not, load the function from the resources available
80             // to this class, and run a JDBC command to create that function.
81
82             // At the moment, that function is named computeCurrentLocation(),
83             // and resides in the resources of the current module.
84             //
85             // For now, assume this function will exist in the 'nuxeo' database;
86             // future work to create per-tenant repositories will likely require that
87             // our JDBC statements connect to the appropriate tenant-specific database.
88
89             // It doesn't appear we can create this function via 'ant create_nuxeo db'
90             // during the build process, because there's a substantial likelihood at
91             // that point that tables referred to by the function (movements_common
92             // and collectionobjects_common) will not exist, and PostgreSQL will not
93             // permit the function to be created if that is the case.
94
95             /*
96             ClassLoader classLoader = getClass().getClassLoader();
97             String functionResourcePath =
98                     DATABASE_RESOURCE_DIRECTORY_NAME + "/"
99                     + DATABASE_SYSTEM_NAME + "/"
100                     + STORED_FUNCTION_NAME + ".sql";
101             classLoader.getResourceAsStream(functionResourcePath);
102             */
103
104             // If the create attempt fails, bail (return) from this method.
105
106             // Get this Movement record's CSID via the document model.
107
108             // Find every CollectionObject record related to this Movement record:
109             //
110             // Via an NXQL query, get a list of (non-deleted) relation records where:
111             // * This movement record's CSID is the subject CSID of the relation.
112             // * The object document type is a CollectionObject doctype.
113
114             // Iterate through that list of Relation records and build a list of
115             // CollectionObject CSIDs, by extracting the object CSIDs of those records.
116
117             // For each such CollectionObject:
118
119             // Verify that the CollectionObject record is active (use isActiveDocument(), below).
120
121             // Via a JDBC call, invoke the SQL function to supply the last
122             // identified location of that CollectionObject, giving it the CSID
123             // of the CollectionObject record as an argument.
124
125             // Check that the SQL function's returned value, which is expected
126             // to be a reference (refName) to a storage location authority term,
127             // is at a minimum:
128             // * Non-null
129             // * Capable of being successfully parsed by an authority item parser,
130             //   returning a non-null parse result.
131
132             // Compare that returned value to the value in the
133             // lastIdentifiedLocation field of that CollectionObject
134
135             // If the two values differ, update the CollectionObject record,
136             // setting the value of the lastIdentifiedLocation field of that
137             // CollectionObject record to the value returned from the SQL function.
138         }
139
140     }
141
142     /**
143      * Identifies whether a document is a Movement document
144      *
145      * @param docModel a document model
146      * @return true if the document is a Movement document; false if it is not.
147      */
148     private boolean isMovementDocument(DocumentModel docModel) {
149         return documentMatchesType(docModel, MovementConstants.NUXEO_DOCTYPE);
150     }
151
152     // FIXME: Generic methods like the following might be split off
153     // into an event utilities class. - ADR 2012-12-05
154     // FIXME: Identify whether the equivalent of this utility method is
155     // already implemented and substitute a call to the latter if so.
156     /**
157      * Identifies whether a document matches a supplied document type.
158      *
159      * @param docModel a document model.
160      * @param docType a document type string.
161      * @return true if the document matches the supplied document type; false if
162      * it does not.
163      */
164     private boolean documentMatchesType(DocumentModel docModel, String docType) {
165         if (docModel == null || Tools.isBlank(docType)) {
166             return false;
167         }
168         if (docModel.getType().startsWith(docType)) {
169             return true;
170         } else {
171             return false;
172         }
173     }
174
175     /**
176      * Identifies whether a document is an active document; that is, if it is
177      * not a versioned record; not a proxy (symbolic link to an actual record);
178      * and not in the 'deleted' workflow state.
179      *
180      * (A note relating the latter: Nuxeo appears to send 'documentModified'
181      * events even on workflow transitions, such when records are 'soft deleted'
182      * by being transitioned to the 'deleted' workflow state.)
183      *
184      * @param docModel
185      * @return true if the document is an active document; false if it is not.
186      */
187     private boolean isActiveDocument(DocumentModel docModel) {
188         if (docModel == null) {
189             return false;
190         }
191         boolean isActiveDocument = false;
192         try {
193             if (!docModel.isVersion()
194                     && !docModel.isProxy()
195                     && !docModel.getCurrentLifeCycleState().equals(WorkflowClient.WORKFLOWSTATE_DELETED)) {
196                 isActiveDocument = true;
197             }
198         } catch (ClientException ce) {
199             logger.warn("Error while identifying whether document is an active document: ", ce);
200         }
201         return isActiveDocument;
202     }
203
204     private boolean storedFunctionExists(String functionname) {
205         if (Tools.isBlank(functionname)) {
206             return false;
207         }
208
209         boolean storedFunctionExists = false;
210         String sql = "SELECT proname FROM pg_proc WHERE proname='" + functionname + "'";
211         Connection conn = null;
212         Statement stmt = null;
213         ResultSet rs = null;
214
215         try {
216             conn = JDBCTools.getConnection(JDBCTools.getDataSource(JDBCTools.NUXEO_REPOSITORY_NAME));
217             stmt = conn.createStatement();
218             rs = stmt.executeQuery(sql);
219             if (rs.next()) {
220                 storedFunctionExists = true;
221             }
222             rs.close();
223             stmt.close();
224             conn.close();
225         } catch (Exception e) {
226             logger.debug("Error when identifying whether stored function " + functionname + "exists :", e);
227         } finally {
228             try {
229                 if (rs != null) {
230                     rs.close();
231                 }
232                 if (stmt != null) {
233                     stmt.close();
234                 }
235                 if (conn != null) {
236                     conn.close();
237                 }
238             } catch (SQLException sqle) {
239                 logger.debug("SQL Exception closing statement/connection in UpdateObjectLocationOnMove.storedFunctionExists: " + sqle.getLocalizedMessage());
240             }
241         }
242         return storedFunctionExists;
243     }
244 }