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