Jump to: navigation, search

MoDisco/MDT Migration

Overview

MoDisco moved from Modeling/GMT to Modeling/MDT (see this bug). As part of this migration, we should have renamed all Java packages from org.eclipse.gmt.modisco.* to org.eclipse.modisco.*.

Unfortunately, this means breaking every existing API, which is in contradiction with the API policy that was established for MoDisco. Among other things, this policy states that API cannot be broken without notifying adopters at least one year before that.

This means for MoDisco that we cannot just rename the Java packages, because we still have to support the API that existed in the org.eclipse.gmt.modisco.* namespace.

To reconcile these two contradicting requirements, we explored several solutions, which are presented below.

Possible solutions

duplicate

We could, before releasing a new version of MoDisco with the renamed packages, duplicate all the previously renamed plug-ins, and rename all packages (through an automated refactoring) from org.eclipse.modisco.* back to org.eclipse.gmt.modisco.*. And then distribute this as a "backward compatibility SDK".

The problem with this solution is for adopters that rely on plug-ins with the old namespace, but that want to start developing plug-ins for the latest version of MoDisco. This would be an all-or-nothing solution: either migrate everything to the latest version of MoDisco, or develop new projects with the old version.

delegate

Another solution would be to create a "backward compatibility SDK" (with GMT packages) that delegates to the latest version of MoDisco. The idea looks straightforward: every call to an API from the old version delegates to the corresponding API in the latest version.

This could be automated through a refactoring that would create proxies (in the GMT namespace) for each MoDisco API, that delegate to the corresponding methods with the same name, but in the org.eclipse.modisco namespace.

This would look like this:

New API (org.eclipse.modisco namespace) Proxy implementation (org.eclipse.gmt.modisco namespace)
public class A {
  public A() {
    // do some initialization
  }
 
  public void doStuff() {
    // do some stuff
  }
}
public class A {
  private org.eclipse.modisco.A proxy;
 
  public org.eclipse.modisco.A getProxy() {
    return proxy;
  }
 
  public A() {
    // guard to avoid creating 2 proxies 
    // (Java adds super call automatically)
    if (!(this.getClass() == A.class))
      return;
    proxy = new org.eclipse.modisco.A();
  }
 
  public void doStuff() {
    getProxy().doStuff();
  }
}


A proxy knows its delegate, but a delegate doesn't know its proxy. But we need this information to be able to find the proxy that was associated with an element that is returned from a method. So, we have to keep this information somewhere. For example in a HashMap:

New API (org.eclipse.modisco namespace) Proxy implementation (org.eclipse.gmt.modisco namespace)
public class C {
  private int value;
 
  public C() {
    this(123);
  }
 
  public C(int value) {
    this.value = value;
  }
 
  public int getValue() {
    return value;
  }
 
  public void setValue(int value) {
    this.value = value;
  }
}
public class C {
  private org.eclipse.modisco.C proxy;
 
  public org.eclipse.modisco.C getProxy() {
    return proxy;
  }
 
  public C() {
    proxy = new org.eclipse.modisco.C();
    proxies.put(proxy, this);
  }
 
  public C(int value) {
    proxy = new org.eclipse.modisco.C(value);
    proxies.put(proxy, this);
  }
 
  public C(org.eclipse.modisco.C element) {
    this.proxy = element;
  }
 
  public int getValue() {
    return getProxy().getValue();
  }
 
  public void setValue(int value) {
    getProxy().setValue(value);
  }
 
  private static Map<Object, Object> proxies = 
      new HashMap<Object, Object>();
 
  public static C resolveProxy(
        org.eclipse.modisco.C element) {
    C proxy = (C) proxies.get(element);
    if (proxy == null) {
      proxy = new C(element);
      proxies.put(proxy, element);
    }
    return proxy;
  }
}


So far, so good. But in practice, we discovered that this is not always so simple, and there are many roadblocks that prevent this solution from working 100%. Moreover, even getting it to work only for common cases that can work is far from trivial, and requires a fair amount of work.

Difficulties