Generating MS Project Plans with MPXJ

Lately needed to create a project plan for a company who was doing some migrations. In this migrations a lot of different systems (about 200) and ressources (about 200 people) where involved. There were about 30 tasks which all had a different duration and different people involved depending on the system for which they were performed. First we thought about doing it the hard way: preparing a template for one system, copy&pasting it 200 times, then adjusting the values as needed. But due to frequent changes in the plan soon this turned out as unmanageable work effort.
So I was looking for some solution to automatically generate the plan.

I found this great java library, which makes it easy to read and write project plans: http://mpxj.sourceforge.net/

Download

First download the necessary files. The mpxj library is mandatory, the poi library you need to read mpp-Project-Files.

Setting up eclipse

  1. Create a new Java Project
  2. Add the mpxj and poi libraries to the buildpath as external archives

Create a basic project plan

Here is the example code to generate a basic project plan with tasks and subtasks and dependencies between the tasks

import java.util.Calendar;
import java.util.Calendar;
 
import net.sf.mpxj.Duration;
import net.sf.mpxj.ProjectFile;
import net.sf.mpxj.RelationType;
import net.sf.mpxj.Resource;
import net.sf.mpxj.Task;
import net.sf.mpxj.TimeUnit;
import net.sf.mpxj.mpx.MPXWriter;
 
public class MpxjExample {
 
	static Task pre = null;
 
	public static void main(String[] args) throws Exception {
 
		// writes the project-data-structure into a mpx-XML-File
		MPXWriter mpxwriter = new MPXWriter();
 
		// base data-structure for project files
		ProjectFile projectfile = new ProjectFile();
 
		// filling the project with some dummy data
		// here you can easily use your real data extracted
		// from a database or a csv-file
		int personcount = 1;
		for (int i = 1; i <= 10; i++) {
 
			// the same task as in ms project
			Task task = projectfile.addTask();
			task.setName("Example Task " + i);
 
			Task presub = null;
			// add some subtasks
			for (int j = 1; j <= 10; j++) {
 
				Task subtask = task.addTask();
				subtask.setName("Sub Task " + j);
 
				// set the subtasks duration, every subtask will take 4 hours in
				// the example
				subtask.setDuration(Duration.getInstance(4, TimeUnit.HOURS));
 
				// add resources to the subtask
				// in this example we will add only one resource to every task
				// 1. add the resource to the general projectfile
				Resource res = projectfile.addResource();
				res.setName("Person " + personcount);
				personcount++;
 
				// associate the resource with the current task
				subtask.addResourceAssignment(res);
 
				// concatenate the subtasks, so that one subtask is performed after
				// another on the timeline
				// the first task has no predecessor
				if (j == 1) {
					presub = subtask;
				} else {
					subtask.addPredecessor(presub, RelationType.FINISH_START, Duration
							.getInstance(0, TimeUnit.HOURS));
					presub = subtask;
				}
			}
 
			// concatenate the tasks, so that one main task is performed after
			// another on the timeline
			// the first task has no predecessor
			if (i == 1) {
				//set the startdate of the project
				Calendar rightNow = Calendar.getInstance();
				rightNow.set(2012, 1, 1);
				task.setStart(rightNow.getTime());
				pre = task;
			} else {
				task.addPredecessor(pre, RelationType.FINISH_START, Duration
						.getInstance(0, TimeUnit.HOURS));
				pre = task;
			}
		}
 
		//writing the project file
		mpxwriter.write(projectfile, "exampleproject.mpx");
 
	}
}

Opening the file in MS Project

At my tries there popped up an error message, but if you click to ignore the warnings the file builds up normally and then you can save it under a normal mpp-file. With the mpp-file saved by MS Project no error messages pop up.

Sometimes MS Project crashed when opening the mpx-file, but next time it worked. I think there is some problem with MS Project parsing files which have not set all values. But trying it another time for me always worked.

Adding several resources to a task

I don’t know if this is the right way to do, but for me it worked. As long as you proceed this way. If i did it otherwise, the total project times did not fit in the end.

  1. you need to know (in the code) how many resources will be added to the task
  2. add the time the task takes and divide it through the count of resources added (the more people work on the task in parallel the faster it is finished)
  3. add the resources

Here is the example code

import java.util.Calendar;
import java.util.Calendar;
 
import net.sf.mpxj.Duration;
import net.sf.mpxj.ProjectFile;
import net.sf.mpxj.RelationType;
import net.sf.mpxj.Resource;
import net.sf.mpxj.Task;
import net.sf.mpxj.TimeUnit;
import net.sf.mpxj.mpx.MPXWriter;
 
public class MpxjExample2 {
 
	static Task pre = null;
	static final int respertask = 3;
 
	public static void main(String[] args) throws Exception {
 
		// writes the project-data-structure into a mpx-XML-File
		MPXWriter mpxwriter = new MPXWriter();
 
		// base data-structure for project files
		ProjectFile projectfile = new ProjectFile();
 
		// filling the project with some dummy data
		// here you can easily use your real data extracted
		// from a database or a csv-file
		int personcount = 1;
		for (int i = 1; i <= 10; i++) {
 
			// the same task as in ms project
			Task task = projectfile.addTask();
			task.setName("Example Task " + i);
 
			Task presub = null;
			// add some subtasks
			for (int j = 1; j <= 10; j++) {
 
				Task subtask = task.addTask();
				subtask.setName("Sub Task " + j);
 
				//this time we add three resources per task
				subtask.setDuration(Duration.getInstance(((float)4/(float)respertask), TimeUnit.HOURS));
				for(int k = 1; k <= respertask; k++) {
 
 
				// add resources to the subtask
				// 1. add the resource to the general projectfile
				Resource res = projectfile.addResource();
				res.setName("Person " + personcount);
				personcount++;
 
				// associate the resource with the current task
				subtask.addResourceAssignment(res);
				}
				// set the subtasks duration, every subtask will take 4 hours in
				// the example
 
 
 
 
				// concatenate the subtasks, so that one subtask is performed after
				// another on the timeline
				// the first task has no predecessor
				if (j == 1) {
					presub = subtask;
				} else {
					subtask.addPredecessor(presub, RelationType.FINISH_START, Duration
							.getInstance(0, TimeUnit.HOURS));
					presub = subtask;
				}
			}
 
			// concatenate the tasks, so that one main task is performed after
			// another on the timeline
			// the first task has no predecessor
			if (i == 1) {
				//set the startdate of the project
				Calendar rightNow = Calendar.getInstance();
				rightNow.set(2012, 1, 1);
				task.setStart(rightNow.getTime());
				pre = task;
			} else {
				task.addPredecessor(pre, RelationType.FINISH_START, Duration
						.getInstance(0, TimeUnit.HOURS));
				pre = task;
			}
		}
 
		//writing the project file
		mpxwriter.write(projectfile, "exampleproject2.mpx");
 
	}
}

Example Java Classes

MpxjExample.java
MpxjExample2.java

pixelstats trackingpixel

Comments (12)

Ramesh MorasaApril 19th, 2013 at %I:%M %p

Hi,
Thank you for your code. Could you please provide me the sample code in Dotnet and How to overcome the below issue?
I am assigning task id to Task like task.setID(Convert.ToInt16(1)) in dotnet where am getting error like task.setID(java.lang.integer) has some invalid arguments.

adminApril 19th, 2013 at %I:%M %p

Hi Ramesh,

sorry I’m not familiar with the Dotnet version, but i think in Dotnet you dont have to use the setters and getters, but can use directly the Dotnet accesors, like this:
task.ID = Convert.ToInt16(1)

Have a look at this article:
http://mpxj.sourceforge.net/howto-dotnet.html


Jonas

Ramesh MorasaApril 24th, 2013 at %I:%M %p

Hi Jonas,

ProjectFile project = new ProjectFile();

I want add a calendar to the project with following requirement.

Working Days: Monday to Friday
Non working Days: Saturday and Sunday

Working Intervals in a day :

8:00 12:00
13:00 18:00

Exception days (Holidays) : 01 May 2013 , 08 May 2013

Exception days working intervals:
On specific exception date i want to work between so and so time interval like for example
as i mentioned above for date “08 May 2013” i want to work between 9:00 to 12:00 only

How do i implement this?

It would be helpful to me if you provide the sample code.

adminApril 24th, 2013 at %I:%M %p

Hi Ramesh,

I think you can do this with the ProjectCalendar Object using the function “addCalendarException”

http://mpxj.sourceforge.net/apidocs/net/sf/mpxj/ProjectCalendar.html#addCalendarException%28java.util.Date,%20java.util.Date%29

I assume you can just add two exceptions: one for 8th May 00:00 (or 00:01) – 9:00 and one from 12:00 – 23:59 (or 00:00, you have to try)

I’m quite busy at the moment, so unfortunately I don’t have time to provide you with sample code.

eddieSeptember 20th, 2013 at %I:%M %p

Hi Jonas,

I am creating a mpx project with this API, but I would like to insert a customized field, like text10 for example. and after if possible I would like to rename it to “Department” for example.

Any idea on how I can do this? I tryed with TaskField but had no success..

JonasSeptember 20th, 2013 at %I:%M %p

Hi Eddie,

it is quite some time ago I did this project, so I don’t have any example projects ready to test. I found this in the API-Doc, perhaps it is what you were looking for:

http://mpxj.sourceforge.net/apidocs/net/sf/mpxj/Task.html#setFieldByAlias(java.lang.String, java.lang.Object)

LuisNovember 4th, 2013 at %I:%M %p

Hi Jonas,
I’m in the same situation as eddie, and I also reached the api reference you indicate, but doesn’t work if the project (the original XML) does not define the extended attribute with the alias.
So what I wanted to do is as easy as two step process:
{

// Check whether the alias exist and if not it creates it
ensureFieldExistsInProject( “alias”, task.getParentFile() );

// Adds task
task.setFieldByAlias( “alias”, “content” );

}

The problem is that I don’t find how to create the the extended field -when it doesn’t exist already-.
Well, I found something using the MSPDIWriter, but that means that is not a general solution, but only for this file format and I suspect there must be a way to define it at ProjectFile level.

Have you ever done anything like that? Do you have any idea?

Thanks a lot,
Luis

JonasNovember 4th, 2013 at %I:%M %p

sorry, I didn’t try something like this, but perhaps someone else knows howto do it and posts here.

One problem I encountered during my fiddling with the library is, that you can only write the XML-File-Format, but not the binary Project-Format. But it its possible to write out the plan as XML reimport it into MS Project and then save as “normal” Binary-Format. It was not the nice way, but for my needs it was enough. I basically used the library to generate some kind of skeleton which then was edited manually. I always had the feeling, that the MPXJ-Library meets some needs, but is a bit buggy and too unflexible (output only in the XML-Format) for nice production use. However it was the only lib I found available on the net.

LuisNovember 5th, 2013 at %I:%M %p

I’m ok with that. Im working with the XML version. 🙂
So far, I didn’t find anything, nowhere 🙁

LuisNovember 8th, 2013 at %I:%M %p

Actually it was evident. I th9ink I just messed it up when testing and then I overpassed the solution. I leave it here in case is useful for others.

{{{
/***
*
* @param strFieldAlias
* @throws Exception
*/
static void ensureFieldExistsInProject( String strFieldAlias, ProjectFile projectfile ) throws Exception
{
if( projectfile==null)
throw new Exception(); // TODO specify which exception
if( strFieldAlias==null || strFieldAlias.isEmpty() )
throw new Exception(); // TODO spefify which expection

// What to do if the field doesnt exists?
TaskField taskfield = projectfile.getAliasTaskField(strFieldAlias);
if( taskfield!=null ) {
// it already exists
return;
}

projectfile.setTaskFieldAlias(CONSTRAINTS_TASKFIELD, strFieldAlias); // “TEXT10” or whatever available attribute in CONSTRAINTS_TASKFIELD

}//- ensureFieldExistsInProject( strFieldAlias, projectfile )-
}}}

SwathiJanuary 23rd, 2014 at %I:%M %p

Hi I am trying to set the enddate of the task. tried all the below but unable to set. Can any help me on this.
Task projectTask = file.addTask();
projectTask.Name = task.Name;
projectTask.ActualStart = projectTask.EarlyStart = df.parse(startDate.ConvertToAndIgnoreException());
//projectTask.Set(TaskField.START, df.parse(startDate.ConvertToAndIgnoreException()));
if (endDate.HasValue)
{
//projectTask.Set(TaskField.FINISH, df.parse(endDate.Value.ConvertToAndIgnoreException()));
//projectTask.Finish = df.parse(string.Format(“{0:dd/MM/yyyy}”, endDate.Value));
//projectTask.Duration = Duration.getInstance((startDate – endDate.Value).TotalDays +1, TimeUnit.DAYS);
projectTask.ActualFinish = projectTask.EarlyFinish = df.parse(endDate.Value.ConvertToAndIgnoreException());
}

Thanks in Advance,
Swathi

JonasJanuary 23rd, 2014 at %I:%M %p

It’s been quite a while I used MPXJ, but I think back then I also had problems with this. I think I solved it by setting a startdate and the proper duration (you can calculate it using the start and enddate).

Looks like in this blog post something similar is suggested:
http://stackoverflow.com/questions/16807627/ms-project-shows-incorrect-start-finish-dates-after-importing-mspdi-created-by

And here another related post (not exactly for this problem, but perhaps also relevant for your work)
http://stackoverflow.com/questions/11135907/will-mpxj-api-calculate-enddate-automatically-before-writing-to-mpx-file

Leave a comment

Your comment

Time limit is exhausted. Please reload the CAPTCHA.