package xapi.util; import xapi.platform.JrePlatform; import static xapi.util.X_Namespace.PROPERTY_DEBUG; import static xapi.util.X_Namespace.PROPERTY_MULTITHREADED; import static xapi.util.X_Namespace.PROPERTY_TEST; import static xapi.util.X_Namespace.PROPERTY_USE_X_INJECT; import java.io.File; import java.io.IOException; /** * This class is magic; there are four copies of this class; * do not add anything to them unless you download all xapi source, * and do a full text search on "X_Runtime\s+{" * * This is the public class exposed to a jre. * Each method compiles down to a runtime constant, * or as close a possible, * to encourage compiler inlining. * * Then, we super-source a copy for normal gwt builds, which reports false * for isRuntimeInjection, and GWT.isScript() for isGwtProd(). * * For gwt users who inherit xapi.X_Inject, we use magic method injection * to replace the super-sourced copy of {@link #isRuntimeInjection()} to return * true or false, depending on the configuration present in gwt module xml. * * Finally, our xapi-debug module will override X_Runtime in the classloader, * to return true for isDebug(). * * @author "James X. Nelson (james@wetheinter.net)" * */ @JrePlatform public class X_Runtime { private X_Runtime() {} // using static final fields to encourage inlining after clinit private static final boolean inject; private static final boolean parallel; private static final boolean debug; private static final boolean test; private static final boolean gwt; private static final String fileSeparator; static { debug = "true".equals(System.getProperty(PROPERTY_DEBUG, "true")); test = "true".equals(System.getProperty(PROPERTY_TEST, "false")); fileSeparator = System.getProperty("file.separator", "false"); boolean success = "true".equals(System.getProperty(PROPERTY_USE_X_INJECT, "true")); try { // Check if user has disabled with a system property. if (success) { // Make sure the X_Inject class is loadable Class.forName("xapi.inject.X_Inject"); } } catch (Throwable e) { String error = "Class xapi.inject.X_Inject is not loadable." + (debug?"\nEnsure your module inherits xapi-core-inject, or set system property " + PROPERTY_USE_X_INJECT+" to \"false\"." : ""); if (debug || success) System.err.println(error); // If the user explicitly set use X inject to true, we bail early, // with a more useful error message. Not setting the property will // merely print the error message, and continue w/out injection. if ("true".equals(System.getProperty(PROPERTY_USE_X_INJECT))) throw new AssertionError(error); success = false; } inject = success; try { // Make sure X_Process is loadable, and bail with a usable error message. Class.forName("xapi.process.X_Process"); success = !"".equals(System.getProperty(PROPERTY_MULTITHREADED,"1")); } catch (Throwable e) { success = !"".equals(System.getProperty(PROPERTY_MULTITHREADED,"")); String message = "Class xapi.process.X_Process is not loadable." + (debug?"\nEnsure your module inherits xapi-core-process, or set system property " + PROPERTY_MULTITHREADED+" to empty string \"\".":""); if (debug || success) System.err.println(message); if (success) { //if system property was set // throw now, with better error message. throw new AssertionError(message); } success = false; } parallel = success; // our last variable, isGwt, requires special handling; // although this class should be super-sourced in all gwt compiles, // we must take care not to break on any platforms that don't have gwt-user // on the classpath (android comes to mind). success = false; try { //This will bomb in gwt if our emulation layer isn't on user classpath Class<?> cls = Class.forName("com.google.gwt.core.shared.GWT"); if (cls == null) { //Only gwt might return null //in cases where a user has our emulation layer on classpath, //but didn't explicitly inherit wetheinter.net.gwt.Reflect; //Class.forName will return null instead of throw ClassNotFoundException success = true; //For users who do inherit wetheinter.net.gwt.Reflect, //Class.forName is a magic-method, which will return any class //accessible to the module being compiled } else { try { java.lang.reflect.Method isClient = cls.getMethod("isClient"); //normally only gwt dev or a jre that happens to have GWT on classpath //will actually get to call this method. Gwt production might, //if you send the GWT.class literal through X_Gwt.magicClass() method. success = (Boolean)isClient.invoke(null); }catch (NoSuchMethodError e) { //This is what our emulated class will throw if a class is not enhanced success = true; } catch (IllegalArgumentException e) { assert false : e;//should never get here } catch (IllegalAccessException e) { //eat this silently; a java enviro with a security manager is not gwt } catch (java.lang.reflect.InvocationTargetException e) { assert false : e;//should never get here, but don't punish prod } } } catch (VerifyError e) { // only ever thrown by java; can happen when gwt w/ java 8 is on classpath } catch (UnsupportedClassVersionError e) { // only ever thrown by java; can happen when gwt w/ java 8 is on classpath }catch(NoSuchMethodException e) { //What an unpatched GWT java.lang.Class will throw success = true; }catch(NoClassDefFoundError e) { //A jre that does not have GWT on classpath }catch(ClassNotFoundException e) { //A jre that does not have GWT on classpath } gwt = success; } /** * Overridden in super-source to return GWT.isScript() * @return - true if running in compiled javascript. */ public static boolean isJavaScript() { return false; } /** * Overridden in super-source to return false. * @return - true in all jre runtimes, including gwt-dev */ public static boolean isJava() { return true; } /** * @return - true only if xapi-flash-api is inherited. */ public static boolean isActionScript() { return false; } /** * @return Convenience method for whether this is a gwt environment; * hardcoded to true in super-source; optimized to return true in gwt dev. */ public static boolean isGwt() { return gwt; } /** * In jres, this returns true if xapi.inject.X_Inject is on classpath, * and the user has not set system property * {@link X_Namespace#PROPERTY_USE_X_INJECT} explicitly to "false". * * Overridden in super-source to return false; * overridden again with magic-method-injection to return true. * * The default debug module will check X_Properties on every call. * * @return true if runtime injection is enabled. */ public static boolean isRuntimeInjection() { // defaults to true; can be inlined after clinit return inject; } /** * @return true is xapi.debug property is set to true. */ public static boolean isDebug() { //set xapi.debug = true to enable debugging. return debug; } public static boolean isTest() { return test; } /** * @return true unless {@link X_Namespace#PROPERTY_MULTITHREADED} is set to 0 */ public static boolean isMultithreaded() { // default to true; can be inlined after clinit return parallel; } public static String getWorkingDirectory() { String pwd = System.getenv("PWD"); if (pwd == null) try { pwd = new File(".").getCanonicalPath(); } catch (IOException ignored) {} return pwd == null ? "." : pwd; } public static String fileSeparator() { return fileSeparator; } public static char fileSeparatorChar() { return fileSeparator.charAt(0); } }