Multi-tier Migration

You are viewing an old version (v. 13) of this page.
The latest version is v. 22, last edited on Feb 11, 2010 (view differences | )
<< View previous version | view page history | view next version >>

Migrate super class and sub class separately

Continue with version "1" of the Task class defined in Getting Started chapter:

package com.pmease.commons.xmt.bean;

import java.util.List;
import org.dom4j.Element;
import com.pmease.commons.xmt.VersionedDocument;

public class Task {
	enum Priority {HIGH, MEDIUM, LOW}
	
	public Priority priority;

	@SuppressWarnings("unused")
	private void migrate1(VersionedDocument dom, List<String> versions) {
		Element element = dom.getRootElement().element("prioritized");
		element.setName("priority");
		if (element.getText().equals("true"))
			element.setText("HIGH");
		else
			element.setText("LOW");
	}
}

Now we have class CompileTask subclassing from Task class as below:

package example;

import java.util.List;

public class CompileTask extends Task {
        public List<String> srcFiles;
}

Instance of CompileTask can be serialized to XML with XMT as below:

package example;

import java.util.ArrayList;

import com.pmease.commons.xmt.VersionedDocument;

public class Test {
	public static void main(String args[]) {
		CompileTask task = new CompileTask();
		task.priority = Task.Priority.HIGH;
		task.srcFiles = new ArrayList<String>();
		task.srcFiles.add("Class1.java");
		task.srcFiles.add("Class2.java");
		String xml = VersionedDocument.fromBean(task).toXML();
                writeXmlToFileOrDatabase(xml);
	}

        private static void writeXmlToFileOrDatabase(String xml) {
                // write XML to file or database here
        }
}

The resulting XML will be:

<example.CompileTask version="1.0">
  <prioritized>true</prioritized>
  <srcFiles>
    <string>Class1.java</string>
    <string>Class2.java</string>
  </srcFiles>
</example.CompileTask>

Pay attention to version attribute of the root element: XMT examines the class hierarchy (except for class java.lang.Object) to get current version of each class, and concatenates them with period. Since there are no migrate methods defined in class Task and CompileTask, current version of both classes are of "0", and the resulting version of the hierarchy (or composite version) will be "0.0".
When deserializing the compile task object from XML, XMT splits this composite version to get XML version for each class in the hierarchy, and repeats the process described in Getting Started chapter for each of these classes. So if class Task is evolved to take numeric priority value described in Getting Started chapter, we simply add migrate methods in Task class, while keep CompileTask class intacted. If we continue to evolve class CompileTask to include a compile option field, and make sure that compile tasks of old version automatically take the default compile option of "-debug", we can then define the migrate method in CompileTask like below, and keep class Task intacted:

package example;

import java.util.List;

public class CompileTask extends Task {
        public List<String> srcFiles;
        
        public String options = "-debug";
        
	@SuppressWarnings("unused")
	private void migrate1(VersionedDocument dom, List<String> versions) {
		dom.getRootElement().addElement("options").setText("-debug");
	}
}

This separation of concerns has the benefit of not requiring you to define migrate methods in various sub classes if super class is evolved, and vice versa.

Address class hierarchy change problem

Till now, we remains the class hierarchy when we evolve class Task and CompileTask. Now let's assume that class Task is removed and class CompileTask needs to take care of the priority field:

package example;

import java.util.List;
import com.pmease.commons.xmt.VersionedDocument;
import com.pmease.commons.xmt.MigrationHelper;

public class CompileTask {
        public int priority;

        public List<String> srcFiles;
        
        public String options = "-debug";
        
	@SuppressWarnings("unused")
	private void migrate1(VersionedDocument dom, List<String> versions) {
		dom.getRootElement().addElement("options").setText("-debug");
	}
}

However deserialization from below XML will not work:

<example.CompileTask version="0.0">
  <prioritized>true</prioritized>
  <srcFiles>
    <string>Class1.java</string>
    <string>Class2.java</string>
  </srcFiles>
</example.CompileTask>

There are two obstacles:

  1. There is no migration logic in class CompileTask to migrate priority field which is handled previously by class Task.
  2. The version recorded in XML is "0.0" and XMT expects for two classes in the hierarchy for migration. However there is only one class now.

To solve this issue, we create a class TaskMigrator just to hold the migration logic previously exists in Task class and call it in method migrate2 like below:

package example;

import java.util.List;
import com.pmease.commons.xmt.VersionedDocument;
import com.pmease.commons.xmt.MigrationHelper;

public class CompileTask {
        public int priority;

        public List<String> srcFiles;
        
        public String options = "-debug";
        
	@SuppressWarnings("unused")
	private void migrate1(VersionedDocument dom, List<String> versions) {
		dom.getRootElement().addElement("options").setText("-debug");
	}

	@SuppressWarnings("unused")
	private void migrate2(VersionedDocument dom, List<String> versions) {
                String taskVersion = versions.remove(0);
                MigrationHelper.migrate(taskVersion, TaskMigrator.class, dom);
	}
        
        private static class TaskMigrator {

	        @SuppressWarnings("unused")
	        private void migrate1(VersionedDocument dom, List<String> versions) {
		        Element element = dom.getRootElement().element("prioritized");
		        element.setName("priority");
		        if (element.getText().equals("true"))
			        element.setText("HIGH");
		        else
			        element.setText("LOW");
	        }

                @SuppressWarnings("unused")
                private void migrate2(VersionedDocument dom, List<String> versions) {
        	        Element element = dom.getRootElement().element("priority");
                	if (element.getText().equals("HIGH"))
		                element.setText("10");
                	else if (element.getText().equals("MEDIUM"))
		                element.setText("5");
                        else 
                                element.setText("1");
                }
        }
}

With this modification, XMT will then follow below procedure to migrate from the aforementioned XML:

  1. It splits version "0.0" recorded in XML as a list, with the first element being version of CompileTask and second element being version of Task.
  2. It
Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.