comparison m-toolbox/classes/@ltpda_uo/submit.m @ 20:d58813ab1b92 database-connection-manager

Update ltpda_uo.submit
author Daniele Nicolodi <nicolodi@science.unitn.it>
date Mon, 05 Dec 2011 16:20:06 +0100
parents f0afece42f48
children ca0b8d4dcdb6
comparison
equal deleted inserted replaced
19:69e3d49b4b0c 20:d58813ab1b92
40 % new table in the database. Then this list is passed to validate so that 40 % new table in the database. Then this list is passed to validate so that
41 % if the 'validate' plist option (which needs to be added) is set to true, 41 % if the 'validate' plist option (which needs to be added) is set to true,
42 % then we call validate on the object before submitting. If validate is 42 % then we call validate on the object before submitting. If validate is
43 % true, then we set the validated flag in the database after submission if 43 % true, then we set the validated flag in the database after submission if
44 % it passes. 44 % it passes.
45 % 45 %
46 % 46 %
47 47
48 48
49 function varargout = submit(varargin) 49 function varargout = submit(varargin)
50 50
51 % check if this is a call for parameters 51 % check if this is a call for parameters
52 if utils.helper.isinfocall(varargin{:}) 52 if utils.helper.isinfocall(varargin{:})
53 varargout{1} = getInfo(varargin{3}); 53 varargout{1} = getInfo(varargin{3});
54 return 54 return
55 end 55 end
56 56
57 import utils.const.* 57 import utils.const.*
58 utils.helper.msg(msg.PROC3, 'running %s/%s', mfilename('class'), mfilename); 58 utils.helper.msg(msg.PROC3, 'running %s/%s', mfilename('class'), mfilename);
59 59
60 % collect all AOs 60 % collect all AOs
61 [pls, invars, rest] = utils.helper.collect_objects(varargin(:), 'plist'); 61 [pls, invars, rest] = utils.helper.collect_objects(varargin(:), 'plist');
62 [sinfo, invars, objs] = utils.helper.collect_objects(rest(:), 'struct'); 62 [sinfo, invars, objs] = utils.helper.collect_objects(rest(:), 'struct');
63 63
64 % identify plists which are only used for the submission process 64 % identify plists which are only used for the submission process
65 mask = false(numel(pls), 1); 65 mask = false(numel(pls), 1);
66 for ii = 1:numel(pls) 66 for ii = 1:numel(pls)
67 if ~utils.helper.isSubmissionPlist(pls(ii)) 67 if ~utils.helper.isSubmissionPlist(pls(ii))
68 mask(ii) = true; 68 mask(ii) = true;
75 end 75 end
76 % keep all the other as parameters plist 76 % keep all the other as parameters plist
77 if sum(~mask) 77 if sum(~mask)
78 pls = combine(pls(~mask)); 78 pls = combine(pls(~mask));
79 end 79 end
80 80
81 % rearrange nested objects lists into a single cell array 81 % rearrange nested objects lists into a single cell array
82 objs = flatten(objs); 82 objs = flatten(objs);
83 83
84 if isempty(objs) 84 if isempty(objs)
85 error('### input at least one object to submit to the repository'); 85 error('### input at least one object to submit to the repository');
86 end 86 end
87 87
88 % combine user plist with default 88 % combine user plist with default
89 pls = fixPlist(pls); 89 pls = fixPlist(pls);
90 dpl = getDefaultPlist(); 90 dpl = getDefaultPlist();
91 pls = combine(pls, dpl.pset('HOSTNAME', '')); 91 pls = combine(pls, dpl.pset('HOSTNAME', ''));
92 92
93 % for backwards compatibility convert any user supplied sinfo-structure into a plist 93 % for backwards compatibility convert any user supplied sinfo-structure into a plist
94 pls = ltpda_uo.convertSinfo2Plist(pls, sinfo); 94 pls = ltpda_uo.convertSinfo2Plist(pls, sinfo);
95 95
96 % read XML submission informations file 96 % read XML submission informations file
97 filename = pls.find('sinfo filename'); 97 filename = pls.find('sinfo filename');
98 if ~isempty(filename) 98 if ~isempty(filename)
99 try 99 try
100 pl = fixPlist(utils.xml.read_sinfo_xml(filename)); 100 pl = fixPlist(utils.xml.read_sinfo_xml(filename));
101 pls = combine(pl, pls); 101 pls = combine(pl, pls);
102 catch err 102 catch err
103 error('### unable to read specified file: %s', filename); 103 error('### unable to read specified file: %s', filename);
104 end 104 end
105 end 105 end
106 106
107 % collect additional informations 107 % collect additional informations
108 sinfo = ltpda_uo.submitDialog(pls); 108 sinfo = ltpda_uo.submitDialog(pls);
109 if isempty(sinfo) 109 if isempty(sinfo)
110 [varargout{1}, varargout{2}] = userCanceled(); 110 [varargout{1}, varargout{2}] = userCanceled();
111 return 111 return
112 end 112 end
113 113
114 % check completeness of user supplied informations 114 % check completeness of user supplied informations
115 sinfo = checkSinfo(sinfo); 115 sinfo = checkSinfo(sinfo);
116 116
117 % user supplied connection 117 % database connection
118 conn = find(pls, 'conn'); 118 c = LTPDADatabaseConnectionManager().connect(pls);
119 119
120 % obtain a connection from the connection manager
121 rm = LTPDARepositoryManager();
122
123 if isempty(conn)
124 connections = rm.findConnections(pls);
125 if isempty(connections)
126 % no connection found. create a new one
127 conn = rm.newConnection(pls);
128 if isempty(conn)
129 [varargout{1}, varargout{2}] = userCanceled();
130 return
131 end
132 elseif numel(connections) == 1 && ~utils.prog.yes2true(pls.find('use selector'))
133 % found only one connection and we are not forcing the use of the selector
134 conn = connections(1);
135 else
136 % make the user select the desired database connection
137 conn = rm.manager.selectConnection([]);
138 if isempty(conn)
139 [varargout{1}, varargout{2}] = userCanceled();
140 return
141 end
142 end
143 end
144
145 % avoid to ask for a password if one was specified in the plist
146 if isjava(conn) && ~conn.isConnected()
147 passwd = pls.find('password');
148 if ~isempty(passwd)
149 conn.setPassword(passwd);
150 end
151 end
152
153 % connect to the database
154 if isempty(conn.openConnection())
155 error('### unable to connect to the database')
156 end
157
158 utils.helper.msg(msg.PROC1, 'submitting %d objects to repository', numel(objs)); 120 utils.helper.msg(msg.PROC1, 'submitting %d objects to repository', numel(objs));
159 121
160 try 122 try
161 % lock connection to avoid expire during processing 123 % get username and userid
162 conn.setLocked(true); 124 [username, userid] = utils.repository.getUser(c);
163
164 % reload connection table if we have a GUI
165 if ~isempty(rm.gui)
166 rm.gui.reloadConnectionTable();
167 end
168
169 % look-up user id
170 userid = utils.jmysql.getUserID(conn);
171 username = conn.getUsername();
172
173 utils.helper.msg(msg.PROC1, 'got user id %d for user: %s', userid, char(username));
174 if userid < 1 || isnan(userid) || strcmp(userid, 'No Data') || ischar(userid)
175 error('### Unknown username.');
176 end
177 125
178 % author of the data: let's take the username 126 % author of the data: let's take the username
179 author = username; 127 author = username;
180 128
181 % date for the transaction.transdata and objmeta.submitted columns as UTC time string 129 % date for the transaction.transdata and objmeta.submitted columns as UTC time string
182 t = time(); 130 t = time();
183 tdate = format(t, 'yyyy-mm-dd HH:MM:SS', 'UTC'); 131 tdate = format(t, 'yyyy-mm-dd HH:MM:SS', 'UTC');
184 132
185 % machine details 133 % machine details
186 prov = provenance(); 134 prov = provenance();
187 135
188 utils.helper.msg(msg.IMPORTANT, 'submitting to %s@%s:%s', ...
189 char(conn.getUsername), char(conn.getHostname), char(conn.getDatabase));
190
191 % obtain the underlying connection
192 c = conn.getConn();
193
194 % start a transaction. either we submitt all objects or we roll back all changes 136 % start a transaction. either we submitt all objects or we roll back all changes
195 c.setAutoCommit(false); 137 c.setAutoCommit(false);
196 138
197 % process each object and collect id numbers 139 % process each object and collect id numbers
198 ids = zeros(numel(objs), 1); cid = []; 140 ids = zeros(numel(objs), 1); cid = [];
199 for kk = 1:numel(objs) 141 for kk = 1:numel(objs)
200 142
201 % this object 143 % this object
202 obj = objs{kk}; 144 obj = objs{kk};
203 145
204 utils.helper.msg(msg.PROC1, 'submitting object: %s / %s', class(obj), obj.name); 146 utils.helper.msg(msg.PROC1, 'submitting object: %s / %s', class(obj), obj.name);
205 147
206 % format object creation time as UTC time string 148 % format object creation time as UTC time string
207 if isa(obj, 'plist') 149 if isa(obj, 'plist')
208 % plist-objects stores creatins time as milli secs since the epoch 150 % plist-objects stores creatins time as milli secs since the epoch
209 created = time().format('yyyy-mm-dd HH:MM:SS', 'UTC'); 151 created = time().format('yyyy-mm-dd HH:MM:SS', 'UTC');
210 else 152 else
211 created = obj.created.format('yyyy-mm-dd HH:MM:SS', 'UTC'); 153 created = obj.created.format('yyyy-mm-dd HH:MM:SS', 'UTC');
212 end 154 end
213 155
214 % Set the UUID if it is empty. This should only happen for PLIST 156 % Set the UUID if it is empty. This should only happen for PLIST
215 % objects. 157 % objects.
216 if isempty(obj.UUID) 158 if isempty(obj.UUID)
217 obj.UUID = char(java.util.UUID.randomUUID); 159 obj.UUID = char(java.util.UUID.randomUUID);
218 end 160 end
219 161
220 % create an XML representaion of the object 162 % create an XML representaion of the object
221 if utils.prog.yes2true(pls.find('binary')); 163 if utils.prog.yes2true(pls.find('binary'));
222 utils.helper.msg(msg.PROC2, 'binary submit'); 164 utils.helper.msg(msg.PROC2, 'binary submit');
223 otxt = ['binary submit ' datestr(now)]; 165 otxt = ['binary submit ' datestr(now)];
224 else 166 else
225 utils.helper.msg(msg.PROC2, 'xml submit'); 167 utils.helper.msg(msg.PROC2, 'xml submit');
226 otxt = utils.prog.obj2xml(obj); 168 otxt = utils.prog.obj2xml(obj);
227 end 169 end
228 170
229 % create an MD5 hash of the xml representation 171 % create an MD5 hash of the xml representation
230 md5hash = utils.prog.hash(otxt, 'MD5'); 172 md5hash = utils.prog.hash(otxt, 'MD5');
231 173
232 % create a binary representaion of the object 174 % create a binary representaion of the object
233 bobj = utils.prog.obj2binary(obj); 175 bobj = utils.prog.obj2binary(obj);
234 if isempty(bobj) 176 if isempty(bobj)
235 error('### failed to obtain a binary representation'); 177 error('### failed to obtain a binary representation');
236 end 178 end
237 179
238 % submit object to objs table 180 % submit object to objs table
239 stmt = c.prepareStatement(... 181 stmt = c.prepareStatement(...
240 'INSERT INTO objs (xml, hash, uuid) VALUES (?, ?, ?)'); 182 'INSERT INTO objs (xml, hash, uuid) VALUES (?, ?, ?)');
241 stmt.setObject(1, otxt); 183 stmt.setObject(1, otxt);
242 stmt.setObject(2, char(md5hash)); 184 stmt.setObject(2, char(md5hash));
243 stmt.setObject(3, obj.UUID); 185 stmt.setObject(3, obj.UUID);
244 stmt.executeUpdate(); 186 stmt.executeUpdate();
245 187
246 % obtain object id 188 % obtain object id
247 rs = stmt.getGeneratedKeys(); 189 rs = stmt.getGeneratedKeys();
248 if rs.next() 190 if rs.next()
249 objid = rs.getInt(1); 191 objid = rs.getInt(1);
250 else 192 else
251 objid = []; 193 objid = [];
252 end 194 end
253 rs.close(); 195 rs.close();
254 stmt.close(); 196 stmt.close();
255 197
256 % insert binary representation 198 % insert binary representation
257 stmt = c.prepareStatement(... 199 stmt = c.prepareStatement(...
258 'INSERT INTO bobjs (obj_id, mat) VALUES (?,?)'); 200 'INSERT INTO bobjs (obj_id, mat) VALUES (?,?)');
259 stmt.setObject(1, objid); 201 stmt.setObject(1, objid);
260 stmt.setObject(2, bobj); 202 stmt.setObject(2, bobj);
261 stmt.execute(); 203 stmt.execute();
262 stmt.close(); 204 stmt.close();
263 205
264 % reference IDs are stored in a CSV string 206 % reference IDs are stored in a CSV string
265 if ischar(sinfo.reference_ids) 207 if ischar(sinfo.reference_ids)
266 refids = sinfo.reference_ids; 208 refids = sinfo.reference_ids;
267 else 209 else
268 refids = utils.prog.csv(sinfo.reference_ids); 210 refids = utils.prog.csv(sinfo.reference_ids);
269 end 211 end
270 212
271 % insert object meta data 213 % insert object meta data
272 stmt = c.prepareStatement(... 214 stmt = c.prepareStatement(...
273 [ 'INSERT INTO objmeta (obj_id, obj_type, name, created, version, ' ... 215 [ 'INSERT INTO objmeta (obj_id, obj_type, name, created, version, ' ...
274 'ip, hostname, os, submitted, experiment_title, experiment_desc, ' ... 216 'ip, hostname, os, submitted, experiment_title, experiment_desc, ' ...
275 'reference_ids, additional_comments, additional_authors, keywords, ' ... 217 'reference_ids, additional_comments, additional_authors, keywords, ' ...
292 stmt.setObject(16, java.lang.String(sinfo.quantity)); 234 stmt.setObject(16, java.lang.String(sinfo.quantity));
293 stmt.setObject(17, java.lang.String(sinfo.analysis_description)); 235 stmt.setObject(17, java.lang.String(sinfo.analysis_description));
294 stmt.setObject(18, java.lang.String(author)); 236 stmt.setObject(18, java.lang.String(author));
295 stmt.execute(); 237 stmt.execute();
296 stmt.close(); 238 stmt.close();
297 239
298 % update other meta-data tables 240 % update other meta-data tables
299 cols = utils.jmysql.execute(c, 'SHOW COLUMNS FROM tsdata'); 241 cols = utils.mysql.execute(c, 'SHOW COLUMNS FROM tsdata');
300 if utils.helper.ismember('obj_id', cols(:,1)) 242 if utils.helper.ismember('obj_id', cols(:,1))
301 % the tsdata table contains an obj id column. use the new database schema 243 % the tsdata table contains an obj id column. use the new database schema
302 utils.jmysql.insertObjMetadata(c, obj, objid); 244 utils.repository.insertObjMetadata(c, obj, objid);
303 else 245 else
304 % otherwise use the old one 246 % otherwise use the old one
305 utils.helper.msg(msg.PROC2, 'using back-compatibility code'); 247 utils.helper.msg(msg.PROC2, 'using back-compatibility code');
306 utils.jmysql.insertObjMetadataV1(c, obj, objid); 248 utils.repository.insertObjMetadataV1(c, obj, objid);
307 end 249 end
308 250
309 % update transactions table 251 % update transactions table
310 stmt = c.prepareStatement(... 252 stmt = c.prepareStatement(...
311 'INSERT INTO transactions (obj_id, user_id, transdate, direction) VALUES (?, ?, ?, ?)'); 253 'INSERT INTO transactions (obj_id, user_id, transdate, direction) VALUES (?, ?, ?, ?)');
312 stmt.setObject(1, objid); 254 stmt.setObject(1, objid);
313 stmt.setObject(2, userid); 255 stmt.setObject(2, userid);
314 stmt.setObject(3, java.lang.String(tdate)); 256 stmt.setObject(3, java.lang.String(tdate));
315 stmt.setObject(4, java.lang.String('in')); 257 stmt.setObject(4, java.lang.String('in'));
316 stmt.execute(); 258 stmt.execute();
317 stmt.close(); 259 stmt.close();
318 260
319 % collect the id of the submitted object 261 % collect the id of the submitted object
320 ids(kk) = objid; 262 ids(kk) = objid;
321 end 263 end
322 264
323 % make collection entry 265 % make collection entry
324 if numel(objs) > 1 266 if numel(objs) > 1
325 267
326 % insert record into collections table 268 % insert record into collections table
327 stmt = c.prepareStatement(... 269 stmt = c.prepareStatement(...
328 'INSERT INTO collections (nobjs, obj_ids) VALUES (?, ?)'); 270 'INSERT INTO collections (nobjs, obj_ids) VALUES (?, ?)');
329 stmt.setObject(1, length(ids)); 271 stmt.setObject(1, length(ids));
330 stmt.setObject(2, java.lang.String(utils.prog.csv(ids))); 272 stmt.setObject(2, java.lang.String(utils.prog.csv(ids)));
331 stmt.executeUpdate(); 273 stmt.executeUpdate();
332 274
333 % obtain collection id 275 % obtain collection id
334 rs = stmt.getGeneratedKeys(); 276 rs = stmt.getGeneratedKeys();
335 if rs.next() 277 if rs.next()
336 cid = rs.getInt(1); 278 cid = rs.getInt(1);
337 else 279 else
338 cid = []; 280 cid = [];
339 end 281 end
340 rs.close(); 282 rs.close();
341 stmt.close(); 283 stmt.close();
342 284
343 end 285 end
344 286
345 catch ex 287 catch ex
346 utils.helper.msg(msg.IMPORTANT, 'submission error. no object submitted') 288 utils.helper.msg(msg.IMPORTANT, 'submission error. no object submitted')
347 c.close() 289 c.close()
348 conn.setLocked(false);
349 rethrow(ex) 290 rethrow(ex)
350 end 291 end
351 292
352 % commit the transaction 293 % commit the transaction
353 c.commit(); 294 c.commit();
354 295
296 % close the connection if we own it
297 if isempty(find(pls, 'conn'))
298 c.close();
299 end
300
355 % report IDs of the inserted objects 301 % report IDs of the inserted objects
356 for kk = 1:numel(objs) 302 for kk = 1:numel(objs)
357 utils.helper.msg(msg.IMPORTANT, 'submitted %s object with ID: %d, UUID: %s, name: %s', ... 303 utils.helper.msg(msg.IMPORTANT, 'submitted %s object with ID: %d UUID: %s name: %s', ...
358 class(objs{kk}), ids(kk), objs{kk}.UUID, objs{kk}.name); 304 class(objs{kk}), ids(kk), objs{kk}.UUID, objs{kk}.name);
359 end 305 end
360 if ~isempty(cid) 306 if ~isempty(cid)
361 utils.helper.msg(msg.IMPORTANT, 'made collection entry with ID: %d', cid); 307 utils.helper.msg(msg.IMPORTANT, 'made collection entry with ID: %d', cid);
362 end 308 end
363 309
364 % unlock connection expire
365 conn.setLocked(false);
366
367 % reset timer
368 LTPDARepositoryManager.resetTimer(rm.timerClearPass, conn);
369 LTPDARepositoryManager.resetTimer(rm.timerDisconnect, conn);
370
371 % pass back outputs 310 % pass back outputs
372 if nargout > 0 311 if nargout > 0
373 varargout{1} = ids; 312 varargout{1} = ids;
374 end 313 end
375 if nargout > 1 314 if nargout > 1
387 end 326 end
388 327
389 328
390 function sinfo = checkSinfo(sinfo) 329 function sinfo = checkSinfo(sinfo)
391 % check sinfo structure 330 % check sinfo structure
392 331
393 import utils.const.* 332 import utils.const.*
394 333
395 % fieldnames 334 % fieldnames
396 mainfields = {'experiment_title', 'experiment_description', 'analysis_description'}; 335 mainfields = {'experiment_title', 'experiment_description', 'analysis_description'};
397 extrafields = {'quantity', 'keywords', 'reference_ids', 'additional_comments', 'author', 'additional_authors'}; 336 extrafields = {'quantity', 'keywords', 'reference_ids', 'additional_comments', 'author', 'additional_authors'};
398 337
399 % fieldnames of the input structure 338 % fieldnames of the input structure
400 fnames = fieldnames(sinfo); 339 fnames = fieldnames(sinfo);
401 340
402 % check mandatory fields 341 % check mandatory fields
403 for jj = 1:length(mainfields) 342 for jj = 1:length(mainfields)
404 if ~ismember(fnames, mainfields{jj}) 343 if ~ismember(fnames, mainfields{jj})
405 error('### the sinfo structure should contain a ''%s'' field', mainfields{jj}); 344 error('### the sinfo structure should contain a ''%s'' field', mainfields{jj});
406 end 345 end
407 end 346 end
408 347
409 % check extra fields 348 % check extra fields
410 for jj = 1:length(extrafields) 349 for jj = 1:length(extrafields)
411 if ~ismember(fnames, extrafields{jj}) 350 if ~ismember(fnames, extrafields{jj})
412 utils.helper.msg(msg.PROC2, 'setting default for field %s', extrafields{jj}); 351 utils.helper.msg(msg.PROC2, 'setting default for field %s', extrafields{jj});
413 sinfo.(extrafields{jj}) = ''; 352 sinfo.(extrafields{jj}) = '';
414 end 353 end
415 end 354 end
416 355
417 % additional checks 356 % additional checks
418 if length(sinfo.experiment_title) < 5 357 if length(sinfo.experiment_title) < 5
419 error('### ''experiment title'' should be at least 5 characters long'); 358 error('### ''experiment title'' should be at least 5 characters long');
420 end 359 end
421 if length(sinfo.experiment_description) < 10 360 if length(sinfo.experiment_description) < 10
422 error('### ''experiment description'' should be at least 10 characters long'); 361 error('### ''experiment description'' should be at least 10 characters long');
423 end 362 end
424 if length(sinfo.analysis_description) < 10 363 if length(sinfo.analysis_description) < 10
425 error('### ''analysis description'' should be at least 10 characters long'); 364 error('### ''analysis description'' should be at least 10 characters long');
426 end 365 end
427 366
428 end 367 end
429 368
430 369
431 function ii = getInfo(varargin) 370 function ii = getInfo(varargin)
432 if nargin == 1 && strcmpi(varargin{1}, 'None') 371 if nargin == 1 && strcmpi(varargin{1}, 'None')
449 end 388 end
450 plout = pl; 389 plout = pl;
451 end 390 end
452 391
453 function plo = buildplist() 392 function plo = buildplist()
454 393
455 plo = plist.TO_REPOSITORY_PLIST; 394 plo = plist.TO_REPOSITORY_PLIST;
456 395
457 p = param({'sinfo filename', 'Path to an XML file containing submission metadata'}, paramValue.EMPTY_STRING); 396 p = param({'sinfo filename', 'Path to an XML file containing submission metadata'}, paramValue.EMPTY_STRING);
458 plo.append(p); 397 plo.append(p);
459 398
460 p = param({'binary', 'Submit only binary version of the objects'}, paramValue.FALSE_TRUE); 399 p = param({'binary', 'Submit only binary version of the objects'}, paramValue.FALSE_TRUE);
461 plo.append(p); 400 plo.append(p);
462 end 401 end
463 402
464 403
472 end 411 end
473 412
474 413
475 function flat = flatten(objs) 414 function flat = flatten(objs)
476 % flatten nested lists into a single cell array 415 % flatten nested lists into a single cell array
477 416
478 flat = {}; 417 flat = {};
479 418
480 while iscell(objs) && numel(objs) == 1 419 while iscell(objs) && numel(objs) == 1
481 objs = objs{1}; 420 objs = objs{1};
482 end 421 end
483 422
484 if numel(objs) == 1 423 if numel(objs) == 1
485 flat = {objs}; 424 flat = {objs};
486 return; 425 return;
487 end 426 end
488 427
489 for jj = 1:numel(objs) 428 for jj = 1:numel(objs)
490 obj = flatten(objs(jj)); 429 obj = flatten(objs(jj));
491 for kk = 1:numel(obj) 430 for kk = 1:numel(obj)
492 flat = [ flat obj(kk) ]; 431 flat = [ flat obj(kk) ];
493 end 432 end
494 end 433 end
495 434
496 end 435 end