How To: Use the CPM Schedulers
MPXJ provides two Critical Path Method (CPM) scheduler implementations:
MicrosoftScheduler
and PrimaveraScheduler
. You can use these to schedule
a new project you have created or an existing project you have updated.
These schedulers will use the Critical Path Method algorithm to calculate
the Early Start, Early Finish, Late Start and Late Finish dates for all of the
tasks in the schedule, and from those it will set the Start and Finish dates for
each task.
The two CPM implementations aim to reproduce the results obtained when scheduling a project in either Microsoft Project or Primavera P6. Although both of these applications implement CPM, they both take slightly different approaches, and so the results obtained from each scheduler will be different.
These schedulers have been implemented by observing and attempting to replicate the behavior of Microsoft Project and Primavera P6. In most cases they will produce results which match the original applications, but this is not guaranteed. If you do come across differences, bug reports with sample data are welcome - particularly if you can explain why the original application is scheduling the project the way it is!
Both schedulers implement the Scheduler
interface, which exposes a single
schedule
method. This method takes a ProjectFile
instance, which is the
project to be scheduled, and a start date, which is the date from which the
project should be scheduled.
NOTE: neither the
MicrosoftScheduler
or thePrimaveraScheduler
perform resource leveling. Also, they both assume that you have either already determined the duration of each task, or have added resource assignments to the tasks representing the required amount of work for that task.
The following sections provide some more detailed examples of using both of these schedulers with new and existing projects.
New Project With MicrosoftScheduler
The MicrosoftScheduler
is intended to schedule projects in a way that is
closely aligned to how Microsoft Project would schedule the same project.
NOTE: the
MicrosoftScheduler
only supports scheduling a project from the start date. Scheduling projects from the finish date is not supported.
The MicrosoftScheduler
determines that tasks are either "task dependent",
or "resource dependent", and will schedule them accordingly.
A task dependent task will not have any labor (work) resources assigned to it,
and will already have its Duration, Actual Duration and Remaining duration
attributes set. If the task has been progressed then the Actual Start (and
Actual Finish if applicable) will be set.
A resource dependent task will have one or more labor (work) resource assignments, each with their Work, Actual Work and Remaining Work attributes set. If the task has been progressed then the Actual Start (and Actual Finish if applicable) will be set.
NOTE: The
MicrosoftScheduler
will not correctly schedule split tasks, recurring tasks, or summary tasks which have constraints applied to them.
The following sections provide sample code illustrating how the
MicrosoftScheduler
can be used to schedule a newly created project. All of
this sample code is available in the
MPXJ Java Samples repository.
Task Dependent Tasks
Planned Project
To simplify the code in the following examples, we'll be using the method shown below to create new tasks. This avoids repeating code unnecessarily for each task we create.
private Task createTask(ChildTaskContainer parent, String name, Duration duration)
{
Task task = parent.addTask();
task.setName(name);
task.setDuration(duration);
task.setActualDuration(Duration.getInstance(0, duration.getUnits()));
task.setRemainingDuration(duration);
return task;
}
The method shown above takes as arguments the parent of the new task (either the project file or a summary task) the name of the new task, and it's duration. The method then manages populating the Duration, Actual Duration and Remaining Duration attributes.
ProjectFile file = new ProjectFile();
ProjectCalendar calendar = file.addDefaultBaseCalendar();
file.setDefaultCalendar(calendar);
Task summary1 = file.addTask();
summary1.setName("Summary 1");
Task task1 = createTask(summary1, "Task 1", Duration.getInstance(4, TimeUnit.DAYS));
Task task2 = createTask(summary1, "Task 2", Duration.getInstance(2, TimeUnit.DAYS));
Task task3 = createTask(summary1, "Task 3", Duration.getInstance(5, TimeUnit.DAYS));
task3.addPredecessor(new Relation.Builder().predecessorTask(task1));
task3.addPredecessor(new Relation.Builder().predecessorTask(task2));
Task summary2 = file.addTask();
summary2.setName("Summary 2");
Task task4 = createTask(summary2, "Task 4", Duration.getInstance(2, TimeUnit.DAYS));
Task task5 = createTask(summary2, "Task 5", Duration.getInstance(2, TimeUnit.DAYS));
Task task6 = createTask(summary2, "Task 6", Duration.getInstance(2, TimeUnit.DAYS));
task6.addPredecessor(new Relation.Builder().predecessorTask(task4));
task6.addPredecessor(new Relation.Builder().predecessorTask(task5)
.lag(Duration.getInstance(1, TimeUnit.DAYS)));
Task milestone1 = createTask(file, "Milestone 1", Duration.getInstance(0, TimeUnit.DAYS));
milestone1.addPredecessor(new Relation.Builder().predecessorTask(task3));
milestone1.addPredecessor(new Relation.Builder().predecessorTask(task6));
We'll be working with the sample code above, which creates a new project, adds a default calendar, creates six tasks with two summary tasks and one milestone. The tasks are linked together by some simple predecessor-successor relationships, one of which has some lag defined.
To schedule the file, we just need to invoke the scheduler as illustrated below:
new MicrosoftScheduler().schedule(file, LocalDateTime.of(2025, 4, 11, 8, 0));
We're passing in the ProjectFile
instance we've just created, along
with the date from which we want to schedule the project. At this point the
Start, Finish, Early Start, Early Finish, Late Start, and Late Finish
attributes will be populated.
We could inspect the results of using the scheduler by calling a printTasks
method similar to the one implemented below:
private void printTasks(ProjectFile file)
{
System.out.println(writeTaskHeaders());
file.getTasks().forEach(t -> System.out.println(writeTaskData(t)));
}
private String writeTaskHeaders()
{
return TASK_COLUMNS.stream().map(TaskField::toString)
.collect(Collectors.joining("\t"));
}
private String writeTaskData(Task task)
{
return TASK_COLUMNS.stream().map(c -> writeValue(task.get(c)))
.collect(Collectors.joining("\t"));
}
private String writeValue(Object value)
{
return value instanceof LocalDateTime ?
DATE_TIME_FORMAT.format((LocalDateTime)value) :
String.valueOf(value);
}
private static final List<TaskField> TASK_COLUMNS = Arrays.asList(
TaskField.ID,
TaskField.UNIQUE_ID,
TaskField.NAME,
TaskField.DURATION,
TaskField.START,
TaskField.FINISH,
TaskField.EARLY_START,
TaskField.EARLY_FINISH,
TaskField.LATE_START,
TaskField.LATE_FINISH,
TaskField.TOTAL_SLACK,
TaskField.CRITICAL);
private static final DateTimeFormatter DATE_TIME_FORMAT =
DateTimeFormatter.ofPattern("dd/MM/yy HH:mm");
We can also then save an MSPDI file and open it in Microsoft Project to confirm that the project has been scheduled as we expect:
new UniversalProjectWriter(FileFormat.MSPDI).write(file, "scheduled.xml");
Progressed Project
Building on the sample code above, we'll now update some of the tasks to indicate that they have been progressed, and have actual durations. To update the tasks we'll use the method shown below to set the Actual Start, Actual Duration and Remaining Duration attributes.
private void progressTask(Task task, LocalDateTime actualStart, double percentComplete)
{
double durationValue = task.getDuration().getDuration();
TimeUnit durationUnits = task.getDuration().getUnits();
task.setActualStart(actualStart);
task.setPercentageComplete(percentComplete);
task.setActualDuration(Duration.getInstance((percentComplete * durationValue) / 100.0, durationUnits));
task.setRemainingDuration(Duration.getInstance(((100.0 - percentComplete) * durationValue) / 100.0, durationUnits));
}
We can now progress the first two tasks in our sample, and schedule the resulting project:
progressTask(task1, LocalDateTime.of(2025, 4, 11, 8, 0), 25.0);
progressTask(task2, LocalDateTime.of(2025, 4, 11, 8, 0), 50.0);
new MicrosoftScheduler().schedule(file, LocalDateTime.of(2025, 4, 11, 8, 0));
Resource Dependent Tasks
Planned Project
To illustrate how MicrosoftScheduler
operates with resource dependent tasks,
we'll take a look at some more sample code. Similar to our task dependent
example above we'll use a method to make our sample code less repetitive:
private ResourceAssignment createResourceAssignment(Task task, Resource resource, Duration work)
{
ResourceAssignment assignment = task.addResourceAssignment(resource);
assignment.setWork(work);
assignment.setActualWork(Duration.getInstance(0, work.getUnits()));
assignment.setRemainingWork(work);
return assignment;
}
This method adds a resource assignment to a task and sets the attributes
required by MicrosoftScheduler
: Work, Actual Work and Remaining Work. Our new
sample code creates a project with the same structure as the sample we looked
at previously. The main difference is that we'll create two resources, and use
the createResourceAssignment
method to add work using these resources to two
tasks.
ProjectFile file = new ProjectFile();
ProjectCalendar calendar = file.addDefaultBaseCalendar();
file.setDefaultCalendar(calendar);
Resource resource1 = file.addResource();
resource1.setName("Resource 1");
ProjectCalendar calendar1 = file.addDefaultDerivedCalendar();
resource1.setCalendar(calendar1);
calendar1.setParent(calendar);
calendar1.setName("Resource 1");
calendar1.addCalendarException(LocalDate.of(2025, 4, 14));
Resource resource2 = file.addResource();
resource2.setName("Resource 2");
ProjectCalendar calendar2 = file.addDefaultDerivedCalendar();
resource2.setCalendar(calendar2);
calendar2.setParent(calendar);
calendar2.setName("Resource 2");
Task summary1 = file.addTask();
summary1.setName("Summary 1");
Task task1 = summary1.addTask();
task1.setName("Task 1");
createResourceAssignment(task1, resource1, Duration.getInstance(32, TimeUnit.HOURS));
Task task2 = summary1.addTask();
task2.setName("Task 2");
createResourceAssignment(task2, resource2, Duration.getInstance(16, TimeUnit.HOURS));
Task task3 = createTask(summary1, "Task 3", Duration.getInstance(5, TimeUnit.DAYS));
task3.addPredecessor(new Relation.Builder().predecessorTask(task1));
task3.addPredecessor(new Relation.Builder().predecessorTask(task2));
Task summary2 = file.addTask();
summary2.setName("Summary 2");
Task task4 = createTask(summary2, "Task 4", Duration.getInstance(2, TimeUnit.DAYS));
Task task5 = createTask(summary2, "Task 5", Duration.getInstance(2, TimeUnit.DAYS));
Task task6 = createTask(summary2, "Task 6", Duration.getInstance(2, TimeUnit.DAYS));
task6.addPredecessor(new Relation.Builder().predecessorTask(task4));
task6.addPredecessor(new Relation.Builder().predecessorTask(task5).lag(Duration.getInstance(1, TimeUnit.DAYS)));
Task milestone1 = createTask(file, "Milestone 1", Duration.getInstance(0, TimeUnit.DAYS));
milestone1.addPredecessor(new Relation.Builder().predecessorTask(task3));
milestone1.addPredecessor(new Relation.Builder().predecessorTask(task6));
new MicrosoftScheduler().schedule(file, LocalDateTime.of(2025, 4, 11, 8, 0));
As we did in our previous sample code, once we've created the project we
schedule it using MicrosoftScheduler
.
Progressed Project
Our final example illustrates how we'd update resource assignments to record actual work on our project. To do this we'll use another simple method to avoid repeating the same code when we update several resource assignments:
private void progressAssignment(ResourceAssignment assignment, double percentComplete)
{
double workValue = assignment.getWork().getDuration();
TimeUnit workUnits = assignment.getWork().getUnits();
assignment.setPercentageWorkComplete(percentComplete);
assignment.setActualWork(Duration.getInstance((percentComplete * workValue) / 100.0, workUnits));
assignment.setRemainingWork(Duration.getInstance(((100.0 - percentComplete) * workValue) / 100.0, workUnits));
}
The main purpose of this method is to update the Actual Work and Remaining Work attributes of a resource assignment, given a percent complete value.
Using our previous sample code, we'll add the following lines to add Actual Start dates to the tasks we're updating, then we'll adjust the resource assignments to have actual work:
task1.setActualStart(LocalDateTime.of(2025, 4, 11, 8, 0));
progressAssignment(assignment1, 25.0);
task2.setActualStart(LocalDateTime.of(2025, 4, 11, 8, 0));
progressAssignment(assignment2, 50.0);
new MicrosoftScheduler().schedule(file, LocalDateTime.of(2025, 4, 11, 8, 0));
Once we've updated the resource assignments, we call the scheduler to update the schedule to include the actual work.
New Project With PrimaveraScheduler
The PrimaveraScheduler
is intended to schedule projects in a way that is
closely aligned to how Primavera P6 would schedule the same project.
NOTE: the
PrimaveraScheduler
currently only supports P6's "default" scheduling options (the options configured when the Tools->Schedule->Options->Default menu item is selected).
In the previous section we saw how MicrosoftScheduler
differentiated between
tasks based on whether they just had duration attributes defined, or whether
they had resource assignments with work. Rather than taking this approach,
Primavera P6 uses an explicit Activity Type attribute on each task to determine
how it behaves (the value of this attribute in these cases will be either Task
Dependent or Resource Dependent). This attribute is available through the use
of getter and setter methods on instances of the Task
class.
Note that Task Dependent activities can have resource assignments with work, but these do not affect how the activity is scheduled: only the duration of these activities is used.
P6 requires that milestones have a specific Activity Type: either Start Milestone or Finish Milestone.
There are also Level of Effort activities (which represent an "outline" of an
amount of effort required between two points in the schedule, determined by the
activity's predecessors and successors) and WBS Summary activities, which are
used to "roll up" details from all activities in the hierarchy beneath a parent
WBS entry. All of these activities types are supported and can be scheduled by
the PrimaveraScheduler
class.
For Task Dependent activities, the PrimaveraScheduler
expects that the
Duration, Actual Duration and Remaining Duration attributes are provided. For
Resource Dependent activities, the activity must have labor (work) resource
assignments with their Work, Actual Work, and Remaining Work attributes set. In
both cases, where the activity has been progressed, the activity should have the
Actual Start, and if applicable, the Actual Finish attribute populated.
The Data Date is also important to the PrimaveraScheduler
. The Data Date is
known as the Status Date my MPXJ and can be found in the ProjectProperties
.
The example below illustrates the Data Date being set for a project:
file.getProjectProperties().setStatusDate(LocalDateTime.of(2025, 4, 11, 17, 0));
If your project does not have a value set for the Status Date attribute, the
PrimaveraScheduler
will assume that the Data Date is the same as the start
date for the project your are scheduling.
The following sections provide sample code illustrating how the
PrimaveraScheduler
can be used to schedule a newly created project. All of
this sample code is available in the
MPXJ Java Samples repository.
Task Dependent Activities
Planned Project
To simplify the code in the following examples, we'll be using the method shown below to create new activities. This avoids repeating code unnecessarily for each activity we create.
private Task createActivity(ChildTaskContainer parent, ActivityType type,
String name, Duration duration)
{
Task task = parent.addTask();
task.setActivityType(type);
task.setName(name);
task.setDuration(duration);
task.setActualDuration(Duration.getInstance(0, duration.getUnits()));
task.setRemainingDuration(duration);
return task;
}
The method shown above takes as arguments the parent of the new activity (either the project file or a WBS entry), the activity type, the name of the new activity, and its duration. The method then manages populating the Duration, Actual Duration and Remaining Duration attributes.
ProjectFile file = new ProjectFile();
ProjectCalendar calendar = file.addDefaultBaseCalendar();
file.setDefaultCalendar(calendar);
Task summary1 = file.addTask();
summary1.setName("WBS 1");
Task task1 = createActivity(summary1, ActivityType.TASK_DEPENDENT, "Activity 1", Duration.getInstance(4, TimeUnit.DAYS));
Task task2 = createActivity(summary1, ActivityType.TASK_DEPENDENT,"Activity 2", Duration.getInstance(2, TimeUnit.DAYS));
Task task3 = createActivity(summary1, ActivityType.TASK_DEPENDENT,"Activity 3", Duration.getInstance(5, TimeUnit.DAYS));
task3.addPredecessor(new Relation.Builder().predecessorTask(task1));
task3.addPredecessor(new Relation.Builder().predecessorTask(task2));
Task summary2 = file.addTask();
summary2.setName("WBS 2");
Task task4 = createActivity(summary2, ActivityType.TASK_DEPENDENT, "Activity 4", Duration.getInstance(2, TimeUnit.DAYS));
Task task5 = createActivity(summary2, ActivityType.TASK_DEPENDENT,"Activity 5", Duration.getInstance(2, TimeUnit.DAYS));
Task task6 = createActivity(summary2, ActivityType.TASK_DEPENDENT,"Activity 6", Duration.getInstance(2, TimeUnit.DAYS));
task6.addPredecessor(new Relation.Builder().predecessorTask(task4));
task6.addPredecessor(new Relation.Builder().predecessorTask(task5).lag(Duration.getInstance(1, TimeUnit.DAYS)));
Task milestone1 = createActivity(file, ActivityType.FINISH_MILESTONE,"Milestone 1", Duration.getInstance(0, TimeUnit.DAYS));
milestone1.addPredecessor(new Relation.Builder().predecessorTask(task3));
milestone1.addPredecessor(new Relation.Builder().predecessorTask(task6));
We'll be working with the sample code above, which creates a new project, adds a default calendar, creates six activities with two WBS entries and one milestone. The activities are linked together by some simple predecessor-successor relationships, one of which has some lag defined.
To schedule the file, we just need to invoke the scheduler as illustrated below:
new PrimaveraScheduler().schedule(file, LocalDateTime.of(2025, 4, 11, 8, 0));
We're passing in the ProjectFile
instance we've just created, along
with the date from which we want to schedule the project. At this point the
Start, Finish, Early Start, Early Finish, Late Start, and Late Finish
activity and WBS attributes will be populated.
We could inspect the results of using the scheduler by calling the printTasks
method we saw in a previous section, and we could also save a PMXML file and
open it in Primavera P6 to confirm that the project has been scheduled as we
expect:
new UniversalProjectWriter(FileFormat.PMXML).write(file, "scheduled.xml");
NOTE: when you import the resulting project into P6, you will have to schedule it first using P6's default scheduling options in order to populate the Early Start, Early Finish, Late Start, and Late Finish dates.
Progressed Project
Building on the sample code above, we'll now update some of the activities to indicate that they have been progressed, and have actual duration. To update the activities we'll use the method shown below to set the Actual Start Actual Duration and Remaining Duration attributes:
private void progressActivity(Task task, LocalDateTime actualStart, double percentComplete)
{
double durationValue = task.getDuration().getDuration();
TimeUnit durationUnits = task.getDuration().getUnits();
task.setActualStart(actualStart);
task.setPercentageComplete(percentComplete);
task.setActualDuration(Duration.getInstance((percentComplete * durationValue) / 100.0, durationUnits));
task.setRemainingDuration(Duration.getInstance(((100.0 - percentComplete) * durationValue) / 100.0, durationUnits));
}
We can now progress the first two activities in our sample, and schedule the resulting project:
progressActivity(task1, LocalDateTime.of(2025, 4, 11, 8, 0), 25.0);
progressActivity(task2, LocalDateTime.of(2025, 4, 11, 8, 0), 50.0);
file.getProjectProperties().setStatusDate(LocalDateTime.of(2025, 4, 11, 17, 0));
new PrimaveraScheduler().schedule(file, LocalDateTime.of(2025, 4, 11, 8, 0));
Note that in the example above we've set the Data Date to be consistent with the progress we've applied to the activities.
Resource Dependent Activities
Planned Project
To illustrate how PrimaveraScheduler
operates with resource dependent tasks,
we'll take a look at some more sample code. Similar to our task dependent
example above we'll use a method to make our sample code less repetitive:
private ResourceAssignment createResourceAssignment(Task activity, Resource resource, Duration work)
{
ResourceAssignment assignment = activity.addResourceAssignment(resource);
assignment.setWork(work);
assignment.setActualWork(Duration.getInstance(0, work.getUnits()));
assignment.setRemainingWork(work);
return assignment;
}
This method adds a resource assignment to an activity and sets the attributes
required by PrimaveraScheduler
: Work, Actual Work and Remaining Work. Our new
sample code creates a project with the same structure as the sample we looked
at previously. The main difference is that we'll create two resources, and use
the createResourceAssignment
method to add work using these resources to two
tasks.
ProjectFile file = new ProjectFile();
ProjectCalendar calendar = file.addDefaultBaseCalendar();
file.setDefaultCalendar(calendar);
Resource resource1 = file.addResource();
resource1.setName("Resource 1");
ProjectCalendar calendar1 = file.addDefaultDerivedCalendar();
resource1.setCalendar(calendar1);
calendar1.setParent(calendar);
calendar1.setName("Resource 1");
calendar1.addCalendarException(LocalDate.of(2025, 4, 14));
Resource resource2 = file.addResource();
resource2.setName("Resource 2");
ProjectCalendar calendar2 = file.addDefaultDerivedCalendar();
resource2.setCalendar(calendar2);
calendar2.setParent(calendar);
calendar2.setName("Resource 2");
Task summary1 = file.addTask();
summary1.setName("WBS 1");
Task task1 = summary1.addTask();
task1.setActivityType(ActivityType.RESOURCE_DEPENDENT);
task1.setName("Activity 1");
createResourceAssignment(task1, resource1, Duration.getInstance(32, TimeUnit.HOURS));
Task task2 = summary1.addTask();
task2.setActivityType(ActivityType.RESOURCE_DEPENDENT);
task2.setName("Activity 2");
createResourceAssignment(task2, resource2, Duration.getInstance(16, TimeUnit.HOURS));
Task task3 = createActivity(summary1, ActivityType.TASK_DEPENDENT,"Activity 3", Duration.getInstance(5, TimeUnit.DAYS));
task3.addPredecessor(new Relation.Builder().predecessorTask(task1));
task3.addPredecessor(new Relation.Builder().predecessorTask(task2));
Task summary2 = file.addTask();
summary2.setName("WBS 2");
Task task4 = createActivity(summary2, ActivityType.TASK_DEPENDENT,"Activity 4", Duration.getInstance(2, TimeUnit.DAYS));
Task task5 = createActivity(summary2, ActivityType.TASK_DEPENDENT,"Activity 5", Duration.getInstance(2, TimeUnit.DAYS));
Task task6 = createActivity(summary2, ActivityType.TASK_DEPENDENT,"Activity 6", Duration.getInstance(2, TimeUnit.DAYS));
task6.addPredecessor(new Relation.Builder().predecessorTask(task4));
task6.addPredecessor(new Relation.Builder().predecessorTask(task5).lag(Duration.getInstance(1, TimeUnit.DAYS)));
Task milestone1 = createActivity(file, ActivityType.FINISH_MILESTONE,"Milestone 1", Duration.getInstance(0, TimeUnit.DAYS));
milestone1.addPredecessor(new Relation.Builder().predecessorTask(task3));
milestone1.addPredecessor(new Relation.Builder().predecessorTask(task6));
new PrimaveraScheduler().schedule(file, LocalDateTime.of(2025, 4, 11, 8, 0));
As we did in our previous sample code, once we've created the project we
schedule it using PrimaveraScheduler
.
Progressed Project
Our final example illustrates how we'd update resource assignments to record actual work on our project. To do this we'll use another simple method to avoid repeating the same code when we update several resource assignments:
private void progressAssignment(ResourceAssignment assignment, double percentComplete)
{
double workValue = assignment.getWork().getDuration();
TimeUnit workUnits = assignment.getWork().getUnits();
assignment.setPercentageWorkComplete(percentComplete);
assignment.setActualWork(Duration.getInstance((percentComplete * workValue) / 100.0, workUnits));
assignment.setRemainingWork(Duration.getInstance(((100.0 - percentComplete) * workValue) / 100.0, workUnits));
}
The main purpose of this method is to update the Actual Work and Remaining Work attributes of a resource assignment, given a percent complete value.
Using our previous sample code, we'll add the following lines to add Actual Start dates to the tasks we're updating, adjust the resource assignments to have actual work, and set the Data Date:
task1.setActualStart(LocalDateTime.of(2025, 4, 11, 8, 0));
progressAssignment(assignment1, 25.0);
task2.setActualStart(LocalDateTime.of(2025, 4, 11, 8, 0));
progressAssignment(assignment2, 50.0);
file.getProjectProperties().setStatusDate(LocalDateTime.of(2025, 4, 11, 17, 0));
new PrimaveraScheduler().schedule(file, LocalDateTime.of(2025, 4, 11, 8, 0));
Once we've updated the resource assignments, we call the scheduler to update the schedule to include the actual work.
Update An Existing Project
One final illustration is how MPXJ's schedulers can be used to update an existing project. In this example we have loaded an existing schedule from an MPP file, and have updated the duration of the second task from 3 days to 5 days.
Note that we are passing to the scheduler the start date of the project, as read from the project properties. This is to ensure that we replicate the date from which the project was originally scheduled.
Task task = file.getTaskByID(2);
task.setDuration(Duration.getInstance(5, TimeUnit.DAYS));
new MicrosoftScheduler().schedule(file, file.getProjectProperties().getStartDate());
new UniversalProjectWriter(FileFormat.MSPDI).write(file, "updated.xml");
When the project is scheduled, the scheduler will ensure that the finish date of
the updated task is adjusted accordingly, along with the start and finish dates
of any subsequent successor tasks. Although this sample is based on a Microsoft
Project file, exactly the same approach could be used with a P6 project using
the PrimaveraScheduler
.