Generating MS Project Plans with MPXJ

Contents

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

Deutsche Version

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

12 comments

Archived from the original WordPress blog. Comments are closed.

  1. Ramesh Morasa
    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.
    1. admin

      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

  2. Ramesh Morasa

    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.

    1. admin

      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.

  3. eddie

    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..

  4. Jonas

    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)

  5. Luis

    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

    1. Jonas

      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.

  6. Luis
    I’m ok with that. Im working with the XML version. :)
    So far, I didn’t find anything, nowhere :(
  7. Luis

    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 )-
    }}}

  8. Swathi

    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

  9. Jonas

    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