Adding example, however only a sketch, of possible runtime check between alternative versions
1.1 --- a/samples/composition/build.xml Sat Jun 14 09:58:11 2008 +0200
1.2 +++ b/samples/composition/build.xml Sat Jun 14 09:58:13 2008 +0200
1.3 @@ -21,6 +21,9 @@
1.4 <antcall target="-build-one">
1.5 <param name="version" value="api2.0-enum"/>
1.6 </antcall>
1.7 + <antcall target="-build-one">
1.8 + <param name="version" value="api2.0-runtime"/>
1.9 + </antcall>
1.10
1.11 <antcall target="-build-one">
1.12 <param name="version" value="test"/>
1.13 @@ -49,6 +52,10 @@
1.14 <antcall target="-run-one">
1.15 <param name="version" value="api2.0-enum"/>
1.16 </antcall>
1.17 + <echo level="info" message="Running the Implementation against Version 2.0 with runtime check in the API. This should succeed."/>
1.18 + <antcall target="-run-one">
1.19 + <param name="version" value="api2.0-runtime"/>
1.20 + </antcall>
1.21 </target>
1.22
1.23 <!-- support methods -->
2.1 --- a/samples/composition/nbproject/project.xml Sat Jun 14 09:58:11 2008 +0200
2.2 +++ b/samples/composition/nbproject/project.xml Sat Jun 14 09:58:13 2008 +0200
2.3 @@ -46,6 +46,12 @@
2.4 <location>src-api2.0-enum</location>
2.5 <encoding>UTF-8</encoding>
2.6 </source-folder>
2.7 + <source-folder>
2.8 + <label>src-api2.0-runtime</label>
2.9 + <type>java</type>
2.10 + <location>src-api2.0-runtime</location>
2.11 + <encoding>UTF-8</encoding>
2.12 + </source-folder>
2.13 </folders>
2.14 <ide-actions>
2.15 <action name="build">
2.16 @@ -88,6 +94,10 @@
2.17 <location>src-api2.0-enum</location>
2.18 </source-folder>
2.19 <source-folder style="packages">
2.20 + <label>API Version 2.0, with runtime inspection</label>
2.21 + <location>src-api2.0-runtime</location>
2.22 + </source-folder>
2.23 + <source-folder style="packages">
2.24 <label>Usage of the API</label>
2.25 <location>src-test</location>
2.26 </source-folder>
2.27 @@ -126,6 +136,10 @@
2.28 <source-level>1.5</source-level>
2.29 </compilation-unit>
2.30 <compilation-unit>
2.31 + <package-root>src-api2.0-runtime</package-root>
2.32 + <source-level>1.5</source-level>
2.33 + </compilation-unit>
2.34 + <compilation-unit>
2.35 <package-root>src-test</package-root>
2.36 <classpath mode="compile">src-api1.0:../libs/dist/junit-4.4.jar</classpath>
2.37 <source-level>1.5</source-level>
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
3.2 +++ b/samples/composition/src-api2.0-runtime/api/Arithmetica.java Sat Jun 14 09:58:13 2008 +0200
3.3 @@ -0,0 +1,64 @@
3.4 +package api;
3.5 +
3.6 +import org.apidesign.runtime.check.RuntimeCheck;
3.7 +
3.8 +/** Class to simplify arithmetical operations, improved version to compute
3.9 + * the sum for ranges, but only if the virtual machine is configured to
3.10 + * run in incompatible mode.
3.11 + *
3.12 + * @author Jaroslav Tulach <jtulach@netbeans.org>
3.13 + * @version 2.0
3.14 + */
3.15 +public class Arithmetica {
3.16 + public int sumTwo(int one, int second) {
3.17 + return one + second;
3.18 + }
3.19 +
3.20 + public int sumAll(int... numbers) {
3.21 + int sum = numbers[0];
3.22 + for (int i = 1; i < numbers.length; i++) {
3.23 + sum = sumTwo(sum, numbers[i]);
3.24 + }
3.25 + return sum;
3.26 + }
3.27 +
3.28 + public int sumRange(int from, int to) {
3.29 + if (calledByV2AwareLoader()) {
3.30 + return sumRange2(from, to);
3.31 + } else {
3.32 + return sumRange1(from, to);
3.33 + }
3.34 + }
3.35 +
3.36 + private int sumRange1(int from, int to) {
3.37 + int len = to - from;
3.38 + int[] array = new int[len + 1];
3.39 + for (int i = 0; i <= len; i++) {
3.40 + array[i] = from + i;
3.41 + }
3.42 + return sumAll(array);
3.43 + }
3.44 +
3.45 + private int sumRange2(int from, int to) {
3.46 + return (from + to) * (to - from + 1) / 2;
3.47 + }
3.48 +
3.49 + private static boolean calledByV2AwareLoader() {
3.50 + // BEGIN: design.composition.arith2.6.runtime
3.51 + StackTraceElement[] arr = Thread.currentThread().getStackTrace();
3.52 + ClassLoader myLoader = Arithmetica.class.getClassLoader();
3.53 + for (int i = 0; i < arr.length; i++) {
3.54 + ClassLoader caller = arr[i].getClass().getClassLoader();
3.55 + if (myLoader == caller) {
3.56 + continue;
3.57 + }
3.58 + if (RuntimeCheck.requiresAtLeast("2.6", "api.Arithmetica", caller)) {
3.59 + return true;
3.60 + }
3.61 + return false;
3.62 + }
3.63 + return true;
3.64 + // END: design.composition.arith2.6.runtime
3.65 + }
3.66 +
3.67 +}
4.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
4.2 +++ b/samples/composition/src-api2.0-runtime/org/apidesign/runtime/check/RuntimeCheck.java Sat Jun 14 09:58:13 2008 +0200
4.3 @@ -0,0 +1,31 @@
4.4 +package org.apidesign.runtime.check;
4.5 +
4.6 +import java.util.Map;
4.7 +import java.util.Set;
4.8 +
4.9 +public final class RuntimeCheck {
4.10 + private RuntimeCheck() {
4.11 + }
4.12 +
4.13 + public static boolean requiresAtLeast(String minVersion, String apiName, ClassLoader caller) {
4.14 + if (caller instanceof AwareLoader) {
4.15 + String realVersion = ((AwareLoader)caller).requestedVersion(apiName);
4.16 + if (realVersion != null) {
4.17 + double minV = Double.parseDouble(minVersion);
4.18 + double realV = Double.parseDouble(realVersion);
4.19 + return minV <= realV;
4.20 + }
4.21 + }
4.22 + return false;
4.23 + }
4.24 +
4.25 + public static interface AwareLoader {
4.26 + public String requestedVersion(String apiName);
4.27 + }
4.28 +
4.29 +
4.30 +
4.31 + public static ClassLoader create(ClassLoader wrap, Map<String,String> requiredVersion) {
4.32 + return new VersionAwareClassLoader(wrap, requiredVersion);
4.33 + }
4.34 +}
5.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
5.2 +++ b/samples/composition/src-api2.0-runtime/org/apidesign/runtime/check/VersionAwareClassLoader.java Sat Jun 14 09:58:13 2008 +0200
5.3 @@ -0,0 +1,43 @@
5.4 +package org.apidesign.runtime.check;
5.5 +
5.6 +import java.io.IOException;
5.7 +import java.io.InputStream;
5.8 +import java.util.Map;
5.9 +import java.util.Set;
5.10 +import java.util.logging.Level;
5.11 +import java.util.logging.Logger;
5.12 +
5.13 +final class VersionAwareClassLoader extends ClassLoader
5.14 +implements RuntimeCheck.AwareLoader {
5.15 +
5.16 + private final Map<String,String> requestedVersions;
5.17 +
5.18 + public VersionAwareClassLoader(ClassLoader parent, Map<String,String> requestedVersions) {
5.19 + super(parent);
5.20 + this.requestedVersions = requestedVersions;
5.21 + }
5.22 +
5.23 + @Override
5.24 + protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
5.25 + if (name.endsWith("Test") && name.startsWith("api.")) {
5.26 + try {
5.27 + InputStream is = getResourceAsStream(name.replace('.', '/').concat(".class"));
5.28 + byte[] arr = new byte[is.available()];
5.29 + int read = is.read(arr);
5.30 + assert read >= 0;
5.31 + is.close();
5.32 + return defineClass(name, arr, 0, read);
5.33 + } catch (IOException ex) {
5.34 + throw new ClassNotFoundException("Cannot load " + name, ex);
5.35 + }
5.36 + }
5.37 +
5.38 + return super.loadClass(name, resolve);
5.39 + }
5.40 +
5.41 + public String requestedVersion(String apiName) {
5.42 + return requestedVersions.get(apiName);
5.43 + }
5.44 +
5.45 +
5.46 +}