001: /*
002: * This file or a portion of this file is licensed under the terms of
003: * the Globus Toolkit Public License, found in file ../GTPL, or at
004: * http://www.globus.org/toolkit/download/license.html. This notice must
005: * appear in redistributions of this file, with or without modification.
006: *
007: * Redistributions of this Software, with or without modification, must
008: * reproduce the GTPL in: (1) the Software, or (2) the Documentation or
009: * some other similar material which is provided with the Software (if
010: * any).
011: *
012: * Copyright 1999-2004 University of Chicago and The University of
013: * Southern California. All rights reserved.
014: */
015: package org.griphyn.vdl.dbschema;
016:
017: import java.sql.*;
018: import java.util.*;
019: import java.io.*;
020: import java.lang.reflect.*;
021: import java.net.InetAddress;
022: import org.griphyn.vdl.util.ChimeraProperties;
023: import org.griphyn.common.util.Separator;
024: import org.griphyn.vdl.invocation.*;
025: import org.griphyn.vdl.util.Logging;
026:
027: /**
028: * This class provides basic functionalities to interact with the
029: * backend database for invocation records, such as insertion, deletion,
030: * and search.
031: *
032: * @author Jens-S. Vöckler
033: * @author Yong Zhao
034: * @version $Revision: 148 $
035: */
036: public class InvocationSchema extends DatabaseSchema implements PTC {
037: /**
038: * Default constructor for the provenance tracking.
039: *
040: * @param dbDriverName is the database driver name
041: */
042: public InvocationSchema(String dbDriverName)
043: throws ClassNotFoundException, NoSuchMethodException,
044: InstantiationException, IllegalAccessException,
045: InvocationTargetException, SQLException, IOException {
046: // load the driver from the properties
047: super (dbDriverName, PROPERTY_PREFIX);
048: Logging.instance().log("dbschema", 3,
049: "done with parent schema c'tor");
050:
051: // Note: Does not rely on optional JDBC3 features
052: this .m_dbdriver.insertPreparedStatement("stmt.save.uname",
053: "INSERT INTO ptc_uname(id,archmode,sysname,os_release,machine) "
054: + "VALUES (?,?,?,?,?)");
055: this .m_dbdriver
056: .insertPreparedStatement(
057: "stmt.save.rusage",
058: "INSERT INTO ptc_rusage(id,utime,stime,minflt,majflt,nswaps,"
059: + "nsignals,nvcsw,nivcsw) VALUES (?,?,?,?,?,?,?,?,?)");
060: this .m_dbdriver
061: .insertPreparedStatement(
062: "stmt.save.stat",
063: "INSERT INTO ptc_stat(id,errno,fname,fdesc,size,mode,inode,atime,"
064: + "ctime,mtime,uid,gid) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)");
065: this .m_dbdriver
066: .insertPreparedStatement(
067: "stmt.save.ivr",
068: "INSERT INTO ptc_invocation(id,creator,creationtime,wf_label,"
069: + "wf_time,version,start,duration,tr_namespace,tr_name,tr_version,"
070: + "dv_namespace,dv_name,dv_version,resource,host,pid,"
071: + "uid,gid,cwd,arch,total)"
072: + "VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)");
073:
074: this .m_dbdriver
075: .insertPreparedStatement(
076: "stmt.save.job",
077: "INSERT INTO ptc_job(id,type,start,duration,pid,rusage,stat,"
078: + "exitcode,exit_msg,args) VALUES (?,?,?,?,?,?,?,?,?,?)");
079: this .m_dbdriver
080: .insertPreparedStatement("stmt.save.lfn",
081: "INSERT INTO ptc_lfn(id,stat,initial,lfn) VALUES (?,?,?,?)");
082:
083: this .m_dbdriver
084: .insertPreparedStatement("stmt.select.ivr.sk",
085: "SELECT id FROM ptc_invocation WHERE start=? AND host=? AND pid=?");
086: this .m_dbdriver.insertPreparedStatement("stmt.select.uname.sk",
087: "SELECT id FROM ptc_uname WHERE archmode=? AND sysname=? "
088: + "AND os_release=? AND machine=?");
089: }
090:
091: /**
092: * Converts a regular datum into an SQL timestamp.
093: * @param date is a regular Java date
094: * @return a SQL timestamp obtained from the Date.
095: */
096: protected java.sql.Timestamp toStamp(java.util.Date date) {
097: return new java.sql.Timestamp(date.getTime());
098: }
099:
100: /**
101: * Checks the existence of an invocation record in the database.
102: * The information is based on the (start,host,pid) tuple, although
103: * with private networks, cases may arise that have this tuple
104: * identical, yet are different.
105: *
106: * @param start is the start time of the grid launcher
107: * @param host is the address of the host it ran upon
108: * @param pid is the process id of the grid launcher itself.
109: * @return the id of the existing record, or -1
110: */
111: public long getInvocationID(java.util.Date start, InetAddress host,
112: int pid) throws SQLException {
113: long result = -1;
114: Logging.instance().log("xaction", 1,
115: "START select invocation id");
116:
117: PreparedStatement ps = m_dbdriver
118: .getPreparedStatement("stmt.select.ivr.sk");
119:
120: int i = 1;
121: ps.setTimestamp(i++, toStamp(start));
122: ps.setString(i++, host.getHostAddress());
123: ps.setInt(i++, pid);
124:
125: Logging.instance().log("chunk", 2, "SELECT id FROM invocation");
126: ResultSet rs = ps.executeQuery();
127: if (rs.next())
128: result = rs.getLong(1);
129: rs.close();
130: Logging.instance().log("xaction", 1,
131: "FINAL select invocation id");
132: return result;
133: }
134:
135: /**
136: * Determines the id of an existing identical architecture, or creates
137: * a new entry.
138: *
139: * @param arch is the architecture description
140: * @return the id of the architecture, either new or existing.
141: */
142: public long saveArchitecture(Architecture arch) throws SQLException {
143: long result = -1;
144: Logging.instance().log("xaction", 1, "START select uname id");
145:
146: int i = 1;
147: PreparedStatement ps = m_dbdriver
148: .getPreparedStatement("stmt.select.uname.sk");
149: stringOrNull(ps, i++, arch.getArchMode());
150: stringOrNull(ps, i++, arch.getSystemName());
151: stringOrNull(ps, i++, arch.getRelease());
152: stringOrNull(ps, i++, arch.getMachine());
153:
154: Logging.instance().log("chunk", 2, "SELECT id FROM uname");
155: ResultSet rs = ps.executeQuery();
156: if (rs.next())
157: result = rs.getLong(1);
158: rs.close();
159: Logging.instance().log("xaction", 1, "FINAL select uname id");
160:
161: if (result == -1) {
162: // nothing found, need to really insert things
163: Logging.instance().log("xaction", 1, "START save uname");
164:
165: try {
166: result = m_dbdriver.sequence1("uname_id_seq");
167: } catch (SQLException e) {
168: Logging.instance().log(
169: "app",
170: 0,
171: "During rusage sequence number: "
172: + e.toString().trim());
173: Logging.instance().log("xaction", 1, "START rollback");
174: m_dbdriver.rollback();
175: Logging.instance().log("xaction", 1, "FINAL rollback");
176: throw e; // re-throw
177: }
178:
179: // add ID explicitely from sequence to insertion
180: ps = m_dbdriver.getPreparedStatement("stmt.save.uname");
181: i = 1;
182: longOrNull(ps, i++, result);
183:
184: stringOrNull(ps, i++, arch.getArchMode());
185: stringOrNull(ps, i++, arch.getSystemName());
186: stringOrNull(ps, i++, arch.getRelease());
187: stringOrNull(ps, i++, arch.getMachine());
188:
189: // save prepared values
190: Logging.instance().log("chunk", 2, "INSERT INTO uname");
191: try {
192: int rc = ps.executeUpdate();
193: if (result == -1)
194: result = m_dbdriver
195: .sequence2(ps, "uname_id_seq", 1);
196: } catch (SQLException e) {
197: // race condition possibility: try once more to find info
198: result = -1;
199: Logging.instance().log("xaction", 1,
200: "START select uname id");
201:
202: i = 1;
203: ps = m_dbdriver
204: .getPreparedStatement("stmt.select.uname.sk");
205: stringOrNull(ps, i++, arch.getArchMode());
206: stringOrNull(ps, i++, arch.getSystemName());
207: stringOrNull(ps, i++, arch.getRelease());
208: stringOrNull(ps, i++, arch.getMachine());
209:
210: Logging.instance().log("chunk", 2,
211: "SELECT id FROM uname");
212: rs = ps.executeQuery();
213: if (rs.next())
214: result = rs.getLong(1);
215: rs.close();
216: Logging.instance().log("xaction", 1,
217: "FINAL select uname id");
218:
219: if (result == -1) {
220: Logging.instance().log(
221: "app",
222: 0,
223: "While inserting into rusage: "
224: + e.toString().trim());
225: // rollback in saveInvocation()
226: m_dbdriver
227: .cancelPreparedStatement("stmt.save.uname");
228: throw e; // re-throw
229: }
230: }
231: Logging.instance().log("xaction", 1,
232: "FINAL save uname: ID=" + result);
233: }
234:
235: // done
236: return result;
237: }
238:
239: /**
240: * Inserts an invocation record into the database.
241: *
242: * @param ivr is the invocation record to store.
243: * @return true, if insertion was successful, false otherwise.
244: */
245: public boolean saveInvocation(InvocationRecord ivr)
246: throws SQLException {
247: // big outer try
248: try {
249: long id = -1;
250: try {
251: id = m_dbdriver.sequence1("invocation_id_seq");
252: } catch (SQLException e) {
253: Logging.instance().log(
254: "app",
255: 0,
256: "During IVR sequence number: "
257: + e.toString().trim());
258: throw e; // re-throw
259: }
260:
261: // add ID explicitely from sequence to insertion
262: Logging.instance().log("xaction", 1,
263: "START save invocation");
264: PreparedStatement ps = m_dbdriver
265: .getPreparedStatement("stmt.save.ivr");
266: int i = 1;
267: longOrNull(ps, i++, id);
268:
269: // current_user()
270: stringOrNull(ps, i++, System.getProperty("user.name"));
271:
272: // now()
273: ps.setTimestamp(i++, toStamp(new java.util.Date()));
274:
275: // wf_label, wf_time: not available at the moment...
276: if (ivr.getWorkflowLabel() == null)
277: ps.setNull(i++, Types.VARCHAR);
278: else
279: ps.setString(i++, ivr.getWorkflowLabel());
280:
281: if (ivr.getWorkflowTimestamp() == null)
282: ps.setNull(i++, Types.TIMESTAMP);
283: else
284: ps.setTimestamp(i++,
285: toStamp(ivr.getWorkflowTimestamp()));
286:
287: // version
288: ps.setString(i++, ivr.getVersion());
289:
290: // start, duration
291: ps.setTimestamp(i++, toStamp(ivr.getStart()));
292: ps.setDouble(i++, ivr.getDuration());
293:
294: // TR
295: i = splitDefinition(ps, ivr.getTransformation(), i);
296:
297: // DV: not available at the moment
298: i = splitDefinition(ps, ivr.getDerivation(), i);
299:
300: // resource (site handle)
301: if (ivr.getResource() == null)
302: ps.setNull(i++, Types.VARCHAR);
303: else
304: ps.setString(i++, ivr.getResource());
305:
306: // host
307: ps.setString(i++, ivr.getHostAddress().getHostAddress());
308:
309: // [pug]id
310: ps.setInt(i++, ivr.getPID());
311: ps.setInt(i++, ivr.getUID());
312: ps.setInt(i++, ivr.getGID());
313:
314: // cwd
315: stringOrNull(ps, i++, ivr.getWorkingDirectory().getValue());
316:
317: // uname
318: ps.setLong(i++, saveArchitecture(ivr.getArchitecture()));
319:
320: // save usage and remember id
321: ps.setLong(i++, saveUsage(ivr.getUsage()));
322:
323: // save prepared values
324: Logging.instance()
325: .log("chunk", 2, "INSERT INTO invocation");
326:
327: int rc = ps.executeUpdate();
328: if (id == -1)
329: id = m_dbdriver.sequence2(ps, "invocation_id_seq", 1);
330: Logging.instance().log("xaction", 1,
331: "FINAL save invocation: ID=" + id);
332:
333: // save jobs belonging to invocation
334: for (Iterator j = ivr.iterateJob(); j.hasNext();) {
335: saveJob(id, ((Job) j.next()));
336: }
337:
338: // jsv 20050815: more stat info for Prophesy
339: for (Iterator j = ivr.iterateStatCall(); j.hasNext();) {
340: StatCall s = (StatCall) j.next();
341: String sch = s.getHandle().toLowerCase();
342: if (sch.equals("initial") || sch.equals("final")) {
343: saveLFN(id, s);
344: }
345: }
346:
347: // done
348: m_dbdriver.commit();
349: return true;
350: } catch (SQLException e) {
351: // show complete exception chain
352: for (SQLException walk = e; walk != null; walk = walk
353: .getNextException()) {
354: Logging.instance().log(
355: "app",
356: 0,
357: walk.getSQLState() + ": " + walk.getErrorCode()
358: + ": " + walk.getMessage().trim());
359:
360: StackTraceElement[] ste = walk.getStackTrace();
361: for (int n = 0; n < 5 && n < ste.length; ++n) {
362: Logging.instance().log("app", 0, ste[n].toString());
363: }
364: }
365:
366: Logging.instance().log("xaction", 1, "START rollback");
367: m_dbdriver.cancelPreparedStatement("stmt.save.ivr");
368: m_dbdriver.rollback();
369: Logging.instance().log("xaction", 1, "FINAL rollback");
370: throw e; // re-throw
371: }
372: }
373:
374: /**
375: * Splits the canonical FQDN of a definition into its components,
376: * and save each component into the database.
377: *
378: * @param in is the canonical form.
379: * @param i is the current position in the invocation prepared stmt.
380: * @return the updated position (usually i+3)
381: */
382: private int splitDefinition(PreparedStatement ps, String in, int i)
383: throws SQLException {
384: if (in == null) {
385: // no input, insert all NULL values
386: ps.setNull(i++, Types.VARCHAR);
387: ps.setNull(i++, Types.VARCHAR);
388: ps.setNull(i++, Types.VARCHAR);
389: } else {
390: // there is input after all, insert appropriate values
391: String ns = null;
392: String vs = null;
393:
394: // separate namespace
395: int p1 = in.indexOf(Separator.NAMESPACE);
396: if (p1 == -1) {
397: // no namespace
398: p1 = 0;
399: } else {
400: // there is a namespace
401: ns = in.substring(0, p1);
402: p1 += Separator.NAMESPACE.length();
403: }
404:
405: // separate version
406: int p2 = in.indexOf(Separator.NAME, p1);
407: if (p2 == -1) {
408: // no version attached
409: p2 = in.length();
410: } else {
411: vs = in.substring(p2 + Separator.NAME.length());
412: }
413:
414: // separate identifier -- this is a must-have
415: String nm = in.substring(p1, p2);
416:
417: stringOrNull(ps, i++, ns);
418: stringOrNull(ps, i++, nm);
419: stringOrNull(ps, i++, vs);
420: }
421:
422: // done
423: return i;
424: }
425:
426: /**
427: * Helper function to insert a chunk of the invocation record. This piece
428: * deals with the rusage information.
429: *
430: * @param u is the usage record to insert into the database
431: * @return the sequence number under which it was inserted.
432: * @exception SQLException if something goes awry during insertion.
433: */
434: protected long saveUsage(Usage u) throws SQLException {
435: if (u == null)
436: throw new RuntimeException("usage record is null");
437:
438: long id = -1;
439: try {
440: id = m_dbdriver.sequence1("rusage_id_seq");
441: } catch (SQLException e) {
442: Logging.instance().log(
443: "app",
444: 0,
445: "During rusage sequence number: "
446: + e.toString().trim());
447: Logging.instance().log("xaction", 1, "START rollback");
448: m_dbdriver.rollback();
449: Logging.instance().log("xaction", 1, "FINAL rollback");
450: throw e; // re-throw
451: }
452:
453: // add ID explicitely from sequence to insertion
454: Logging.instance().log("xaction", 1, "START save rusage");
455: PreparedStatement ps = m_dbdriver
456: .getPreparedStatement("stmt.save.rusage");
457: int i = 1;
458: longOrNull(ps, i++, id);
459:
460: // add rest of rusage record
461: ps.setDouble(i++, u.getUserTime());
462: ps.setDouble(i++, u.getSystemTime());
463: ps.setInt(i++, u.getMinorFaults());
464: ps.setInt(i++, u.getMajorFaults());
465: ps.setInt(i++, u.getSwaps());
466: ps.setInt(i++, u.getSignals());
467: ps.setInt(i++, u.getVoluntarySwitches());
468: ps.setInt(i++, u.getInvoluntarySwitches());
469:
470: // save prepared values
471: Logging.instance().log("chunk", 2, "INSERT INTO rusage");
472: try {
473: ps.executeUpdate();
474: if (id == -1)
475: id = m_dbdriver.sequence2(ps, "rusage_id_seq", 1);
476: } catch (SQLException e) {
477: Logging.instance().log(
478: "app",
479: 0,
480: "While inserting into rusage: "
481: + e.toString().trim());
482: // rollback in saveInvocation()
483: this .m_dbdriver.cancelPreparedStatement("stmt.save.rusage");
484: throw e; // re-throw
485: }
486:
487: // done
488: Logging.instance().log("xaction", 1,
489: "FINAL save rusage: ID=" + id);
490: return id;
491: }
492:
493: /**
494: * Helper function to insert a chunk of the invocation record. This piece
495: * deals with the stat and fstat information.
496: *
497: * @param s is the stat record to insert into the database
498: * @return the sequence number under which it was inserted.
499: * @exception SQLException if something goes awry during insertion.
500: */
501: protected long saveStat(StatCall s) throws SQLException {
502:
503: long id = -1;
504: try {
505: id = m_dbdriver.sequence1("stat_id_seq");
506: } catch (SQLException e) {
507: Logging.instance().log(
508: "app",
509: 0,
510: "During stat sequence number: "
511: + e.toString().trim());
512: Logging.instance().log("xaction", 1, "START rollback");
513: m_dbdriver.rollback();
514: Logging.instance().log("xaction", 1, "FINAL rollback");
515: throw e; // re-throw
516: }
517:
518: // add ID explicitely from sequence to insertion
519: Logging.instance().log("xaction", 1, "START save stat");
520: PreparedStatement ps = this .m_dbdriver
521: .getPreparedStatement("stmt.save.stat");
522: int i = 1;
523: longOrNull(ps, i++, id);
524:
525: // add rest of stat record, as appropriate. Many things will stay
526: // NULL during the fill-in of incomplete (failed) stat info
527: ps.setInt(i++, s.getError());
528: org.griphyn.vdl.invocation.File f = s.getFile();
529: if (f != null) {
530: if (f instanceof HasFilename
531: && ((HasFilename) f).getFilename() != null)
532: ps.setString(i++, ((HasFilename) f).getFilename());
533: else
534: ps.setNull(i++, Types.VARCHAR);
535:
536: if (f instanceof HasDescriptor
537: && ((HasDescriptor) f).getDescriptor() != -1)
538: ps.setInt(i++, ((HasDescriptor) f).getDescriptor());
539: else
540: ps.setNull(i++, Types.INTEGER);
541: } else {
542: i += 2;
543: }
544:
545: StatInfo si = s.getStatInfo();
546: if (si != null) {
547: ps.setLong(i++, si.getSize());
548: ps.setInt(i++, si.getMode());
549: ps.setLong(i++, si.getINode());
550:
551: ps.setTimestamp(i++, toStamp(si.getAccessTime()));
552: ps.setTimestamp(i++, toStamp(si.getCreationTime()));
553: ps.setTimestamp(i++, toStamp(si.getModificationTime()));
554:
555: ps.setInt(i++, si.getUID());
556: ps.setInt(i++, si.getGID());
557: } else {
558: // bug fixed 20040908 jsv:
559: // we don't know anything about those, so fill in NULL for rDBMS
560: // that don't automagically default empty columns to NULL (sigh).
561: ps.setNull(i++, Types.BIGINT);
562: ps.setNull(i++, Types.INTEGER);
563: ps.setNull(i++, Types.BIGINT);
564:
565: ps.setNull(i++, Types.TIMESTAMP);
566: ps.setNull(i++, Types.TIMESTAMP);
567: ps.setNull(i++, Types.TIMESTAMP);
568:
569: ps.setNull(i++, Types.INTEGER);
570: ps.setNull(i++, Types.INTEGER);
571: }
572:
573: // save prepared values
574: Logging.instance().log("chunk", 2, "INSERT INTO stat");
575: try {
576: ps.executeUpdate();
577: if (id == -1)
578: id = m_dbdriver.sequence2(ps, "stat_id_seq", 1);
579: } catch (SQLException e) {
580: Logging.instance()
581: .log(
582: "app",
583: 0,
584: "While inserting into stat: "
585: + e.toString().trim());
586: // rollback in safeInvocation()
587: this .m_dbdriver.cancelPreparedStatement("stmt.save.stat");
588: throw e; // re-throw
589: }
590:
591: // done
592: Logging.instance().log("xaction", 1,
593: "FINAL save stat: ID=" + id);
594: return id;
595: }
596:
597: /**
598: * Helper function to insert a LFN PFN mapping stat call into the
599: * stat information records.
600: *
601: * @param iid is the invocation record id to which this job belongs.
602: * @param s is an instance of a stat call from the initial or final list
603: * @exception SQLException if something goes awry during insertion.
604: */
605: protected void saveLFN(long iid, StatCall s) throws SQLException {
606: Logging.instance().log("xaction", 1, "START save lfn");
607:
608: PreparedStatement ps = m_dbdriver
609: .getPreparedStatement("stmt.save.lfn");
610: int i = 1;
611:
612: // add foreign ID explicitely
613: ps.setLong(i++, iid);
614:
615: // stat (foreign key)
616: ps.setLong(i++, saveStat(s));
617:
618: // set the modifier before or after
619: String id = s.getHandle().toLowerCase();
620: if (id.equals("initial") || id.equals("final")) {
621: ps.setString(i++, id.substring(0, 1));
622: } else {
623: ps.setNull(i++, Types.CHAR);
624: }
625:
626: // set the LFN
627: stringOrNull(ps, i++, s.getLFN());
628:
629: // save prepared values
630: Logging.instance().log("chunk", 2, "INSERT INTO lfn");
631: try {
632: ps.executeUpdate();
633: } catch (SQLException e) {
634: Logging.instance().log("app", 0,
635: "While inserting into lfn: " + e.toString().trim());
636: // rollback in safeInvocation()
637: m_dbdriver.cancelPreparedStatement("stmt.save.lfn");
638: throw e; // re-throw
639: }
640:
641: // done
642: Logging.instance().log("xaction", 1, "FINAL save lfn");
643: }
644:
645: /**
646: * Helper function to insert a chunk of the invocation record. This piece
647: * deals with the jobs themselves
648: *
649: * @param iid is the invocation record id to which this job belongs.
650: * @param job is the job to insert.
651: * @exception SQLException if something goes awry during insertion.
652: */
653: protected void saveJob(long iid, Job job) throws SQLException {
654: Logging.instance().log("xaction", 1, "START save job");
655:
656: PreparedStatement ps = m_dbdriver
657: .getPreparedStatement("stmt.save.job");
658: int i = 1;
659:
660: // add foreign ID explicitely
661: ps.setLong(i++, iid);
662:
663: // type
664: String tag = job.getTag();
665: if (tag.equals("mainjob")) {
666: ps.setString(i++, "M");
667: } else if (tag.equals("prejob")) {
668: ps.setString(i++, "P");
669: } else if (tag.equals("postjob")) {
670: ps.setString(i++, "p");
671: } else if (tag.equals("cleanup")) {
672: ps.setString(i++, "c");
673: } else if (tag.equals("setup")) {
674: ps.setString(i++, "S");
675: } else {
676: throw new SQLException("illegal job type \"" + tag + "\"");
677: }
678:
679: // start, duration
680: ps.setTimestamp(i++, toStamp(job.getStart()));
681: ps.setDouble(i++, job.getDuration());
682:
683: // pid
684: ps.setInt(i++, job.getPID());
685:
686: // usage (foreign key)
687: ps.setLong(i++, saveUsage(job.getUsage()));
688:
689: // stat (foreign key)
690: ps.setLong(i++, saveStat(job.getExecutable()));
691:
692: // exitcode, exit_msg
693: Status status = job.getStatus();
694: ps.setInt(i++, status.getStatus());
695: JobStatus js = status.getJobStatus();
696: String msg = null;
697: if (js instanceof HasText)
698: msg = ((HasText) js).getValue();
699: stringOrNull(ps, i++, msg);
700:
701: // args
702: Arguments args = job.getArguments();
703: stringOrNull(ps, i++, args.getValue());
704:
705: // save prepared values
706: Logging.instance().log("chunk", 2, "INSERT INTO job");
707: try {
708: ps.executeUpdate();
709: } catch (SQLException e) {
710: Logging.instance().log("app", 0,
711: "While inserting into job: " + e.toString().trim());
712: // rollback in safeInvocation()
713: m_dbdriver.cancelPreparedStatement("stmt.save.job");
714: throw e; // re-throw
715: }
716:
717: // done
718: Logging.instance().log("xaction", 1, "FINAL save job");
719: }
720: }
|