Distributed Resource Management Application API

This guide is a tutorial for getting started programming with the DRMAA JavaTM language binding. It assumes that you already know what DRMAA is and know how to find and install the Grid Engine release. If you do not already know these things, try these web sites:

Note that the example programs in this howto can be found in the source tree.

Starting and Stopping a Session

The following code segment shows the most basic DRMAA Java language binding program:

Example 1

01: package com.sun.grid.drmaa.howto;
02:
03: import org.ggf.drmaa.DrmaaException;
04: import org.ggf.drmaa.Session;
05: import org.ggf.drmaa.SessionFactory;
06:
07: public class Howto1 {
08:    public static void main(String[] args) {
09:       SessionFactory factory = SessionFactory.getFactory();
10:       Session session = factory.getSession();
11:
12:       try {
13:          session.init("");
14:          session.exit();
15:       } catch (DrmaaException e) {
16:          System.out.println("Error: " + e.getMessage());
17:       }
18:    }
19: }

Everything that you as a programmer will do with DRMAA, you will do through a Session object. You will get the Session object from a SessionFactory. You will get the SessionFactory from the static SessionFactory.getFactory() method. The reason for this chain is that the org.ggf.drmaa.* classes should be considered an immutable package to be used by every DRMAA Java language binding implementation. Because the package is immutable, to load a specific implementation, the SessionFactory uses a system property to find the implementation's session factory, which it then loads. That session factory is then responsible for creating the session in whatever why it sees fit. Without the session factory, the Session would have to do that work, and the implementation-defined session object would have to be instantiated with some predefined constructor signature. With the session factory, the implementation is free to define for itself how the session object will be instatiated. It should be noted that even though there is a session factory, only one session may exist at a time.

Now let's look at the code. First, on line 9, we get a session factory instance with SessionFactory.getFactory(), and on line 10 we get the session instance with SessionFactory.getSession(). Once we have the session, we can initialize it on line 13 by calling Session.init(). We pass in "" as the contact string because we're creating a new session, and the C binding upon which the Java language binding is built does not recognize any initialization arguments. (See Example 1.1.) The plan is that at some point in the future the contact string will be a combination of $SGE_ROOT and $SGE_CELL.

Session.init() creates a session and starts an event client listener thread. The session is used for organizing jobs submitted through DRMAA, and the thread is used to receive updates from the queue master about the state of jobs and the system in general. Once Session.init() has been called successfully, it is the responsibility of the calling application to also call Session.exit() before terminating. If an application does not call Session.exit() before terminating, session state may be left behind in the user's home directory (under .sge/drmaa), and the queue master may be left with a dead event client handle, which can decrease queue master performance. I recommend using the Runtime.addShutdownHook () method to make sure Session.exit() gets called.

At the end of our program, on line 14, we call Session.exit(). Session.exit() cleans up the session and stops the event client listener thread. Most other DRMAA methods must be called before Session.exit(). Some functions, like Session.getContact(), can be called after exit(), but these functions only provide general information. Any function that does work, such as Session.runJob() or Session.wait() must be called before Session.exit() is called. If such a function is called after exit() is called, it will throw a NoActiveSessionException.

Example 1.1

01: package com.sun.grid.drmaa.howto;
02:
03: import org.ggf.drmaa.DrmaaException;
04: import org.ggf.drmaa.Session;
05: import org.ggf.drmaa.SessionFactory;
06:
07: public class Howto1_1 {
08:    public static void main(String[] args) {
09:       SessionFactory factory = SessionFactory.getFactory();
10:       Session session = factory.getSession();
11:
12:       try {
13:          session.init("");
14:          String contact = session.getContact();
15:          session.exit();
16:
17:          session.init(contact);
18:          session.exit();
19:       } catch (DrmaaException e) {
20:          System.out.println("Error: " + e.getMessage());
21:       }
22:    }
23: }

This example is very similar to Example 1. The difference is that it uses the Grid Engine feature of reconnectable sessions. The DRMAA concept of a session is translated into a session tag in the Grid Engine job structure. That means that every job knows to which session it belongs. With reconnectable sessions, it's possible to initialize the DRMAA library to a previous session, allowing the library access to that session's job list. The only limitation, though, is that jobs which end between the calls to exit() and init() will be lost, as the reconnecting session will no longer see these jobs, and so won't know about them.

Through line 13, this example is identical to Example 1. On line 14, however, we use the getContact() method to get the contact information for this session. On line 15 we then exit the session. On line 17, we use the stored contact information to reconnect to the previous session. Had we submitted jobs before calling exit(), those jobs would now be available again for operations such as wait() and synchronize(). Finally, on line 18 we exit the session a second time.

Running a Job

The following code segment shows how to use the DRMAA Java language binding to submit a job to Grid Engine:

Example 2

01: package com.sun.grid.drmaa.howto;
02:
03: import java.util.Collections;
04: import org.ggf.drmaa.DrmaaException;
05: import org.ggf.drmaa.JobTemplate;
06: import org.ggf.drmaa.Session;
07: import org.ggf.drmaa.SessionFactory;
08:
09: public class Howto2 {
10:    public static void main(String[] args) {
11:       SessionFactory factory = SessionFactory.getFactory();
12:       Session session = factory.getSession();
13:
14:       try {
15:          session.init("");
16:          JobTemplate jt = session.createJobTemplate();
17:          jt.setRemoteCommand("sleeper.sh");
18:          jt.setArgs(Collections.singletonList("5"));
19:
20:          String id = session.runJob(jt);
21:
22:          System.out.println("Your job has been submitted with id " + id);
23:
24:          session.deleteJobTemplate(jt);
25:          session.exit();
26:       } catch (DrmaaException e) {
27:          System.out.println("Error: " + e.getMessage());
28:       }
29:    }
30: }

The beginning and end of this program are the same as the previous one. What's different is in lines 16-24. On line 16 we ask DRMAA to allocate a JobTemplate for us. A JobTemplate is an Object used to store information about a job to be submitted. The same template can be reused for multiple calls to Session.runJob() or Session.runBulkJobs().

On line 17 we set the remoteCommand attribute. This attribute tells DRMAA where to find the program we want to run. Its value is the path to the executable. The path be be either relative or absolute. If relative, it is relative to the workingDirectory attribute, which if not set defaults to the user's home directory. For more information on DRMAA attributes, please see the drmaa_attributes man page. Note that for this program to work, the script "sleeper.sh" must be in your default path, i.e. the path set by your shell script when you log in.

On line 18 we set the args attribute. This attribute tells DRMAA what arguments to pass to the executable. For more information on DRMAA attributes, please see the drmaa_attributes man page.

On line 20 we submit the job with Session.runJob(). This method will return the id assigned to the job by the queue master. The job is now running as though submitted by qsub. At this point calling Session.exit() and/or terminating the program will have no effect on the job.

To clean things up, we delete the job template on line 24. This frees the memory DRMAA set aside for the job template, but has no effect on submitted jobs.

If instead of a single job we had wanted to submit an array job, we could have replaced the code on lines 20-22 with the following:

Example 2.1

20: java.util.List ids = session.runBulkJobs(jt, 1, 30, 2);
21: java.util.Iterator i = ids.iterator();
22:
23: while (i.hasNext()) {
24:    System.out.println("Your job has been submitted with id " + i.next());
25: }

This code segment submits an array job with 15 tasks numbered 1, 3, 5, 7, etc. An important difference to note is that Session.runBulkJobs() returns the job ids in a java.util.List. On line 21 we get an Iterator for the list and loop through, printing each submitted job's id, on lines 23-25.

Waiting for a Job

Now we're going to extend our example to include waiting for a job to finish.

Example 3

01: package com.sun.grid.drmaa.howto;
02:
03: import java.util.Collections;
04: import java.util.Iterator;
05: import java.util.Map;
06: import org.ggf.drmaa.DrmaaException;
07: import org.ggf.drmaa.JobInfo;
08: import org.ggf.drmaa.JobTemplate;
09: import org.ggf.drmaa.Session;
10: import org.ggf.drmaa.SessionFactory;
11:
12: public class Howto3 {
13:    public static void main(String[] args) {
14:       SessionFactory factory = SessionFactory.getFactory();
15:       Session session = factory.getSession();
16:
17:       try {
18:          session.init("");
19:          JobTemplate jt = session.createJobTemplate();
20:          jt.setRemoteCommand("sleeper.sh");
21:          jt.setArgs(Collections.singletonList("5"));
22:
23:          String id = session.runJob(jt);
24:
25:          System.out.println("Your job has been submitted with id " + id);
26:
27:          session.deleteJobTemplate(jt);
28:
29:          JobInfo info = session.wait(id, Session.TIMEOUT_WAIT_FOREVER);
30:
31:          if (info.wasAborted()) {
32:             System.out.println("Job " + info.getJobId() + " never ran");
33:          } else if (info.hasExited()) {
34:             System.out.println("Job " + info.getJobId() +
35:                   " finished regularly with exit status " +
36:                   info.getExitStatus());
37:          } else if (info.hasSignaled()) {
38:             System.out.println("Job " + info.getJobId() +
39:                   " finished due to signal " +
40:                   info.getTerminatingSignal());
41:          } else {
42:             System.out.println("Job " + info.getJobId() +
43:                   " finished with unclear conditions");
44:          }
45:
46:          System.out.println("Job Usage:");
47:
48:          Map rmap = info.getResourceUsage();
49:          Iterator i = rmap.keySet().iterator();
50:
51:          while (i.hasNext()) {
52:             String name = (String)i.next();
53:             String value = (String)rmap.get(name);
54:
55:             System.out.println("  " + name + "=" + value);
56:          }
57:
58:          session.exit();
59:       } catch (DrmaaException e) {
60:          System.out.println("Error: " + e.getMessage());
61:       }
62:    }
63: }

This example is very similar to Example 2 except for lines 29-56. (The line numbers are shifted from Example 2 because of the additional import statements) On line 29 we call Session.wait() to wait for the job to end. We have to give Session.wait() both the id of the job for which we want to wait and how long we are willing to wait for the job to finish. The later could be a number of seconds, or it could be either Session.DRMAA_TIMEOUT_WAIT_FOREVER or Session.DRMAA_TIMEOUT_NO_WAIT. Session.wait() returns a JobInfo object, which contains useful information how the job exited and how much resources it used. The JobInfo object also contains the id of the job for which we actually waited because the job id we pass in could be Session.DRMAA_JOB_IDS_SESSION_ANY, in which case Session.wait() must have a way of tell us which job is the one that made it return.

Assuming the wait worked, we query the job's exit status on lines 31-44 using the JobInfo accessor methods. This if structure is a common usage pattern for Session.wait() and should be encapsulated in a method for ease of use.

After checking the exit status, we query the job's usage on lines 46-56. We use JobInfo.getResourceUsage() to get a Map of the resources consumed and print out the results.

An alternative to Session.wait() when working with multiple jobs, such as jobs submitted by Session.runBulkJobs() or multiple calls to Session.runJob() is Session.synchronize(). Session.synchronize() waits for a List of jobs to finish. To use Session.synchronize(), we could replace lines 23-56 with the following:

Example 3.1

23: List ids = session.runBulkJobs(jt, 1, 30, 2);
24: Iterator i = ids.iterator();
25:
26: while (i.hasNext()) {
27:    System.out.println("Your job has been submitted with id " + i.next());
28: }
29:
30: session.deleteJobTemplate(jt);
31: session.synchronize(Collections.singletonList(Session.JOB_IDS_SESSION_ALL),
32:       Session.TIMEOUT_WAIT_FOREVER, true);
33:
34: System.out.println("All jobs have finished.");

Example 3.1

Lines 23-28 now call Session.runBulkJobs() so that we have several jobs for which to wait. On line 31, instead of calling Session.wait(), we call Session.synchronize(). Session.synchronize() takes only three parameters. The first is the List of ids for which to wait. If the special id, Session.DRMAA_JOB_IDS_SESSION_ALL, appears in the List, Session.synchronize() will wait for all jobs submitted via DRMAA during this session, i.e. since Session.init() was called. The second parameter is how long to wait for all the jobs in the list to finish. This is the same as the timeout parameter for Session.wait(). The third parameter is whether this call to Session.synchronize() should clean up after the job. After a job completes, it leaves behind accounting information, such as exist status and usage, until either Session.wait() or Session.synchronize() with dispose set to true is called. It is the responsibility of the application to make sure one of these two functions is called for every job. Not doing so creates a memory leak. Note that calling Session.synchronize() with dispose set to true flushes all accounting information for all jobs in the list. If you want to use Session.synchronize() and still recover the accounting information, set dispose to false and call Session.wait() for each job. To do this in Example 3, we would replace lines 23-56 with the following:

Example 3.2

23: int start = 1;
24: int end  = 30;
25: int step = 2;
26:
27: List ids = session.runBulkJobs(jt, start, end, step);
28: Iterator i = ids.iterator();
29:
30: while (i.hasNext()) {
31:    System.out.println("Your job has been submitted with id " + i.next());
32: }
33:
34: session.deleteJobTemplate(jt);
35: session.synchronize(Collections.singletonList(Session.JOB_IDS_SESSION_ALL),
36:       Session.TIMEOUT_WAIT_FOREVER, false);
37:
38: for (int count = start; count < end; count += step) {
39:    JobInfo info = session.wait(Session.JOB_IDS_SESSION_ANY,
40:       Session.TIMEOUT_WAIT_FOREVER);
41:
42:    if (info.wasAborted()) {
43:       System.out.println("Job " + info.getJobId() + " never ran");
44:    } else if (info.hasExited()) {
45:       System.out.println("Job " + info.getJobId() +
46:             " finished regularly with exit status " +
47:             info.getExitStatus());
48:    } else if (info.hasSignaled()) {
49:       System.out.println("Job " + info.getJobId() +
50:             " finished due to signal " +
51:             info.getTerminatingSignal());
52:    } else {
53:       System.out.println("Job " + info.getJobId() +
54:             " finished with unclear conditions");
55:    }
56:
57:    System.out.println("Job Usage:");
58:
59:    Map rmap = info.getResourceUsage();
60:    Iterator r = rmap.keySet().iterator();
61:
62:    while (r.hasNext()) {
63:       String name = (String)r.next();
64:       String value = (String)rmap.get(name);
65:
66:       System.out.println("  " + name + "=" + value);
67:    }
68: }

What's different is that on line 35, we set dispose to false, and then on lines 38-68 we wait once for each job, printing the exit status and usage information as we did in Example 3. We pass Session.DRMAA_JOB_IDS_SESSION_ANY to Session.wait() as the job id because we already know that all the jobs have finished, so we don't really care in what order we process them. In an interactive system where we couldn't guarantee that more jobs wouldn't be submitted between the synchronize and the wait, we would have to store the job ids from the Session.runBulkJobs() in an array and then wait for each job specifically. Otherwise, the Session.wait() could end up waiting for a job submitted after the call to Session.synchronize().

Controling a Job

Now let's look at an example of how to control a job from DRMAA:

Example 4

01: package com.sun.grid.drmaa.howto;
02:
03: import java.util.Collections;
04: import org.ggf.drmaa.DrmaaException;
05: import org.ggf.drmaa.JobTemplate;
06: import org.ggf.drmaa.Session;
07: import org.ggf.drmaa.SessionFactory;
08:
09: public class Howto4 {
10:    public static void main(String[] args) {
11:       SessionFactory factory = SessionFactory.getFactory();
12:       Session session = factory.getSession();
13:
14:       try {
15:          session.init("");
16:          JobTemplate jt = session.createJobTemplate();
17:          jt.setRemoteCommand("sleeper.sh");
18:          jt.setArgs(Collections.singletonList("5"));
19:
20:          String id = session.runJob(jt);
21:
22:          System.out.println("Your job has been submitted with id " + id);
23:
24:          session.control(id, Session.TERMINATE);
25:
26:          System.out.println("Your job has been deleted");
27:
28:          session.deleteJobTemplate(jt);
29:          session.exit();
30:       } catch (DrmaaException e) {
31:          System.out.println("Error: " + e.getMessage());
32:       }
33:    }
34: }

This example is very similar to Example 2 except for lines 24-26. On line 20 we use Session.control() to delete the job we just submitted. Aside from deleting the job, we could have also used Session.control() to suspend, resume, hold, or release it. For more information, see the drmaa_control man page.

Note that Session.control() can be used to control jobs not submitted through DRMAA. Any valid SGE job id could be passed to Session.control() as the id of the job to delete.

Getting Job Status

Here's an example of using DRMAA to query the status of a job:

Example 5

01: package com.sun.grid.drmaa.howto;
02:
03: import java.util.Collections;
04: import org.ggf.drmaa.DrmaaException;
05: import org.ggf.drmaa.JobTemplate;
06: import org.ggf.drmaa.Session;
07: import org.ggf.drmaa.SessionFactory;
08:
09: public class Howto5 {
10:    public static void main(String[] args) {
11:       SessionFactory factory = SessionFactory.getFactory();
12:       Session session = factory.getSession();
13:
14:       try {
15:          session.init("");
16:          JobTemplate jt = session.createJobTemplate();
17:          jt.setRemoteCommand("sleeper.sh");
18:          jt.setArgs(Collections.singletonList("5"));
19:
20:          String id = session.runJob(jt);
21:
22:          System.out.println("Your job has been submitted with id " + id);
23:
24:          try {
25:             Thread.sleep(20 * 1000);
26:          } catch (InterruptedException e) {
27:             // Don't care
28:          }
29:
30:          int status = session.getJobProgramStatus(id);
31:
32:          switch (status) {
33:             case Session.UNDETERMINED:
34:                System.out.println("Job status cannot be determined\n");
35:                break;
36:             case Session.QUEUED_ACTIVE:
37:                System.out.println("Job is queued and active\n");
38:                break;
39:             case Session.SYSTEM_ON_HOLD:
40:                System.out.println("Job is queued and in system hold\n");
41:                break;
42:             case Session.USER_ON_HOLD:
43:                System.out.println("Job is queued and in user hold\n");
44:                break;
45:             case Session.USER_SYSTEM_ON_HOLD:
46:                System.out.println("Job is queued and in user and system hold\n");
47:                break;
48:             case Session.RUNNING:
49:                System.out.println("Job is running\n");
50:                break;
51:             case Session.SYSTEM_SUSPENDED:
52:                System.out.println("Job is system suspended\n");
53:                break;
54:             case Session.USER_SUSPENDED:
55:                System.out.println("Job is user suspended\n");
56:                break;
57:             case Session.USER_SYSTEM_SUSPENDED:
58:                System.out.println("Job is user and system suspended\n");
59:                break;
60:             case Session.DONE:
61:                System.out.println("Job finished normally\n");
62:                break;
63:             case Session.FAILED:
64:                System.out.println("Job finished, but failed\n");
65:                break;
66:          } /* switch */
67:
68:          session.deleteJobTemplate(jt);
69:          session.exit();
70:       } catch (DrmaaException e) {
71:          System.out.println("Error: " + e.getMessage());
72:       }
73:    }
74: }

Again, this example is very similar to Example 2, this time with the exception of lines 24-68. First, after submitting the job, we sleep for 20 seconds to give SGE time to schedule the job. Then, on line 30, we use Session.getJobProgramStatus() to get the status of the job. Lines 32-66 determine what the job status is and report it. This switch statement is a common usage pattern for Session.getJobProgramStatus() and should be encapsulated in a method for ease of use.

Getting DRM Information

Next, let's look at how to query the DRMAA library for information about the DRMS and the DRMAA implementation itself:

Example 6

01: package com.sun.grid.drmaa.howto;
02:
03: import org.ggf.drmaa.DrmaaException;
04: import org.ggf.drmaa.Session;
05: import org.ggf.drmaa.SessionFactory;
06: import org.ggf.drmaa.Version;
07:
08: public class Howto6 {
09:    public static void main(String[] args) {
10:       SessionFactory factory = SessionFactory.getFactory();
11:       Session session = factory.getSession();
12:
13:       try {
14:          System.out.println("Supported contact strings: \"" +
15:                session.getContact() + "\"");
16:          System.out.println("Supported DRM systems: \"" +
17:                session.getDrmSystem() + "\"");
18:          System.out.println("Supported DRMAA implementations: \"" +
19:                session.getDrmaaImplementation() + "\"");
20:
21:          session.init("");
22:
23:          System.out.println("Using contact strings: \"" +
24:                session.getContact() + "\"");
25:          System.out.println("Using DRM systems: \"" +
26:                session.getDrmSystem() + "\"");
27:          System.out.println("Using DRMAA implementations: \"" +
28:                session.getDrmaaImplementation() + "\"");
29:
30:          Version version = session.getVersion();
31:
32:          System.out.println("Using DRMAA version " + version.toString());
33:
34:          session.exit();
35:       } catch (DrmaaException e) {
36:          System.out.println("Error: " + e.getMessage());
37:       }
38:    }
39: }

On line 14, we print the contact string list. This is the list of contact strings that will be understood by this DRMAA instance. Normally one of these strings is used to select to which DRM this DRMAA instance should be bound. In the Grid Engine 6.0u2 implementation, the contact string list is empty because there is only ever one possible DRM to which to bind.

On line 16, we print the list of supported DRM systems. For the Grid Engine 6.0u2 implementation, this will always be Grid Engine 6.0u2.

On line 18, we print the list of supported DRMAA implementations. For the Grid Engine 6.0u2 implementation, this will always be Grid Engine 6.0u2.

On line 21, we call Session.init(). After Session.init() has been called, the Session.getContact(), Session.getDrmSystem(), and Session.getDrmaaImplementation() calls change.

On line 23, we call Session.getContact() again, this time to get the contact string that was used to bind to a DRMS in Session.init(). For the Grid Engine 6.0u2 implementation, this will always be an empty string.

On line 25, we call Session.getDrmSystem() again, this time to get the name of the DRMS to which DRMAA is bound. For the Grid Engine 6.0u2 implementation, this will always be Grid Engine 6.0u2.

On line 27, we call Session.getDrmaaImplementation() again, this time to get the name of the DRMAA implementation to which the application is bound. For the Grid Engine 6.0u2 implementation, this will always be Grid Engine 6.0u2.

On line 30, we get the version number of the DRMAA Java language binding specification supported by this DRMAA implementation. For the Grid Engine 6.0u2 implementation this is currently version 0.5.

Finally, on line 34, we end the session with drmaa_exit().

Using the New Utility Classes

The JobTemplate class uses two new classes to represent some of its property types. They are FileTransferMode and PartialTimestamp. The PartialTimestampFormat is used to print a PartialTimestamp. The Session interface uses the Version class to represent version information.

FileTransferMode

The FileTransferMode class is a simple container which holds the state of three boolean properties: errorStream, inputStream, and outputStream. Each property indicated whether the given stream should be used for file transfers. When a given property is true, the corresponding path property is taken to represent the source or destination for the file transfer. For example, if the errorStream property of the active FileTransferMode object is true, the errorPath property of the JobTemplate object is treated as the destination to which to copy the resulting error output.

PartialTimestamp

The PartialTimestamp class is used to represent incomplete time specifications, e.g. "1:00" as opposed to "2005/01/05 01:00". Such partial time specifications will be resolved to the soonest time which is still in the future. For example, before 1:00, "1:00" will be resolved to 1:00 on that same day. After 1:00, it will be resolved to 1:00 on the next day. The PartialTimestamp class is a subclass of java.util.Calendar and behaves as much like java.util.GregorianCalendar as possible, with the exception of allowing incomplete time specifications.

The PartialTimestamp class has one other new feature not present in the java.util.Calendar class, field modifiers. Since it may be desirable to appy a modifier to a field which is not yet set, the PartialTimestamp class allows for field modifiers to be kept separately from field values. If a modifier is applied to a set field, that modifier is ultimately added to the field's value. If a modifier is applied to an unset field, the modifier is added to the field's value at the time it is resolved. For example, if a PartialTimestamp object has HOUR_OF_DAY=1 and MINUTE=0 and a +1 modifier to the DAY_OF_MONTH field, and the PartialTimestamp object is resolved at 0:55 on a Tuesday, the result will be 1:00 on the following Wednesday. If the same PartialTimestamp object were resolved at 1:01 on a Tuesday, the result would be 1:00 on the following Thursday.

For a PartialTimestamp object, there are primary fields and secondary fields. The primary fields are actually used by DRMAA and the PartialTimestampFormat class. The secondary fields are used only to calculate the unset primary fields. The primary fields are CENTURY, YEAR, MONTH, DAY_OF_MONTH/DATE, HOUR_OF_DAY, MINUTE, SECOND, and ZONE_OFFSET. The secondary fields are DAY_OF_WEEK, DAY_OF_YEAR, DAY_OF_WEEK_IN_MONTH, WEEK_OF_MONTH, and WEEK_OF_YEAR. The MILLISECOND field is neither primary nor secondary because it outside of the scope of the DRMAA time stamp format specification, and hence is largely ignored.

PartialTimestampFormat

To convert a PartialTimestamp object to or from a String, one should use a PartialTimestampFormat object. The parse() method converts a String into a PartialTimestamp object, and the format() method convers a PartialTimestamp object into a String. In order for the PartialTimestampFormat class to properly do the conversions, the no field of a lower order than the highest order set field may be unset. For example, if the DAY_OF_WEEK field is set, the HOUR_OF_DAY and MINUTE fields must also be set. (These two field are actually required in all cases.) If the MONTH field is set, the DAY_OF_MONTH, HOUR_OF_DAY, and MINUTE fields must be set. If the CENTURY field is set, the YEAR, MONTH, DAY_OF_MONTH, HOUR_OF_DAY, and MINUTE fields must be set. If the MILLISECOND field is set, the HOUR_OF_DAY, MINUTE, and SECOND fields must be set. The SECOND and ZONE_OFFSET fields are always optional. The same applies for strings to be parsed.

Since the PartialTimestamp class is somewhat complicated, let's look at an example:

Example 7

01: package com.sun.grid.drmaa.howto;
02:
03: import org.ggf.drmaa.PartialTimestamp;
04: import org.ggf.drmaa.PartialTimestampFormat;
05:
06: public class Howto7 {
07:    public static void main(String[] args) {
08:       try {
09:          PartialTimestamp date = new PartialTimestamp();
10:          PartialTimestampFormat format = new PartialTimestampFormat();
11:
12:          date.set(PartialTimestamp.HOUR_OF_DAY, 13);
13:          date.set(PartialTimestamp.MINUTE, 0);
14:
15:          System.out.println(format.format(date));
16:
17:          date.setModifier(PartialTimestamp.DAY_OF_MONTH, 1);
18:
19:          System.out.println(format.format(date));
20:          System.out.println(date.getTime().toString());
21:
22:          date.setTimeInMillis(System.currentTimeMillis());
23:
24:          System.out.println(format.format(date));
25:
26:          date = format.parse("01/05 14:07:29");
27:
28:          System.out.println(format.format(date));
29:       } catch (java.text.ParseException e) {
30:          System.out.println("Error: " + e.getMessage());
31:       }
32:    }
33: }

Since the PartialTimestamp class is useable outside of a DRMAA session, this example does not create a SessionFactory or Session instance.

On line 9, we create a PartialTimestamp with no fields set. Other constructors could be used to create a PartialTimestamp object with various fields already set. See the JavaDocs for more details.

On line 10, we create a PartialTimestampFormat object.

On lines 12 and 13, we set the two required PartialTimestamp fields.

On line 15, we use the PartialTimestampFormat object to convert the PartialTimestamp to a String and print it out. This will print "13:00".

On line 17, we set a modifier for the DAY_OF_MONTH field. This means that when the PartialTimestamp is resolved to a concrete time, that time will be one day later than the earliest time which is still in the future.

On line 19, we use the PartialTimestampFormat object to convert the PartialTimestamp to a String and print it out again. This will also print "13:00" because modifiers are ignored by the PartialTimestampFormat class.

On line 20, we resolve the PartialTimestamp to a concrete Date object and print the value of that object. If this code is executed at 3:49:25pm on Tuesday, January 4th 2005, the result will be "Thu Jan 06 13:00:00 MET 2005". (The timezone is MET because that's where I am.)

On line 22, we set the value of the time to the current time in milliseconds. Setting the time directly, either in milliseconds or as a Date object, results in all fields being set.

On line 24, we use the PartialTimestampFormat object to convert the PartialTimestamp to a String and print it out. Because all the fields have been set, the result is "2005/01/05 15:49:25 +01:00". (Again, the timezone setting is because I am in GMT+1.)

On lines 26-28, we use the PartialTimestampFormat object to convert the String to a PartialTimestamp and then back to a String to be printed it out. The result is the same string, "01/05 14:07:29"

Version

The Version class is used by the Session instance to return the version of DRMAA supported by the implementation. The major and minor fields of the Version class represent the major and minor version numbers, respectively. For example, DRMAA 1.0 is represented by a Version object with major=1 and minor=0.