001    /*
002     *  This file is part of the Jikes RVM project (http://jikesrvm.org).
003     *
004     *  This file is licensed to You under the Eclipse Public License (EPL);
005     *  You may not use this file except in compliance with the License. You
006     *  may obtain a copy of the License at
007     *
008     *      http://www.opensource.org/licenses/eclipse-1.0.php
009     *
010     *  See the COPYRIGHT.txt file distributed with this work for information
011     *  regarding copyright ownership.
012     */
013    package org.jikesrvm.scheduler;
014    
015    import java.lang.instrument.Instrumentation;
016    import java.lang.reflect.InvocationTargetException;
017    import java.lang.reflect.Method;
018    import java.util.jar.JarFile;
019    import java.util.jar.Manifest;
020    
021    import org.jikesrvm.Callbacks;
022    import org.jikesrvm.CommandLineArgs;
023    import org.jikesrvm.VM;
024    import org.jikesrvm.classloader.Atom;
025    import org.jikesrvm.classloader.RVMClass;
026    import org.jikesrvm.classloader.RVMClassLoader;
027    import org.jikesrvm.classloader.RVMMethod;
028    import org.jikesrvm.classloader.TypeReference;
029    import org.jikesrvm.runtime.Reflection;
030    import org.vmmagic.pragma.Entrypoint;
031    
032    /**
033     * Thread in which user's "main" program runs.
034     */
035    public final class MainThread extends Thread {
036      private final String[] args;
037      private final String[] agents;
038      private RVMMethod mainMethod;
039      protected boolean launched = false;
040    
041      private static final boolean dbg = false;
042    
043      /**
044       * Create "main" thread.
045       * @param args {@code args[0]}: name of class containing "main" method;
046       *  {@code args[1..N]}: parameters to pass to "main" method
047       */
048      public MainThread(String[] args) {
049        super("MainThread");
050        setDaemon(false); // NB otherwise we inherit the boot threads daemon status
051        this.agents = CommandLineArgs.getJavaAgentArgs();
052        this.args = args;
053        if (dbg) {
054          VM.sysWriteln("MainThread(args.length == ", args.length, "): constructor done");
055        }
056      }
057    
058      private void runAgents(ClassLoader cl) {
059        if (agents.length > 0) {
060          Instrumentation instrumenter = null;
061          if (VM.BuildForGnuClasspath) {
062            try {
063              instrumenter = (Instrumentation)Class.forName("gnu.java.lang.JikesRVMSupport")
064                .getMethod("createInstrumentation").invoke(null);
065              java.lang.JikesRVMSupport.initializeInstrumentation(instrumenter);
066            } catch (Exception _) {
067            }
068          }
069          for (String agent : agents) {
070            /*
071             * Parse agent string according to the form
072             * given in the java.lang.instrumentation package
073             * documentation:
074             * jarpath[=options]
075             *
076             * (The -javaagent: part of the agent options has
077             *  already been stripped)
078             */
079            int equalsIndex = agent.indexOf('=');
080            String agentJar;
081            String agentOptions;
082            if (equalsIndex != -1) {
083              agentJar = agent.substring(0, equalsIndex);
084              agentOptions = agent.substring(equalsIndex + 1);
085            } else {
086              agentJar = agent;
087              agentOptions = "";
088            }
089            runAgent(instrumenter, cl, agentJar, agentOptions);
090          }
091        }
092      }
093    
094      private static void runAgent(Instrumentation instrumenter, ClassLoader cl, String agentJar, String agentOptions) {
095        Manifest mf = null;
096        try {
097          JarFile jf = new JarFile(agentJar);
098          mf = jf.getManifest();
099        } catch (Exception e) {
100          VM.sysWriteln("vm: IO Exception opening JAR file ", agentJar, ": ", e.getMessage());
101          VM.sysExit(VM.EXIT_STATUS_BOGUS_COMMAND_LINE_ARG);
102        }
103        if (mf == null) {
104          VM.sysWriteln("The jar file is missing the manifest: ", agentJar);
105          VM.sysExit(VM.EXIT_STATUS_BOGUS_COMMAND_LINE_ARG);
106        }
107        String agentClassName = mf.getMainAttributes().getValue("Premain-Class");
108        if (agentClassName == null) {
109          VM.sysWriteln("The jar file is missing the Premain-Class manifest entry for the agent class: ", agentJar);
110          VM.sysExit(VM.EXIT_STATUS_BOGUS_COMMAND_LINE_ARG);
111        }
112        //TODO: By this stage all agent jars and classes they reference via their manifest
113        try {
114          Class<?> agentClass = cl.loadClass(agentClassName);
115          Method agentPremainMethod = agentClass.getMethod("premain", new Class<?>[]{String.class, Instrumentation.class});
116          agentPremainMethod.invoke(null, new Object[]{agentOptions, instrumenter});
117        } catch (InvocationTargetException e) {
118          // According to the spec, exceptions from premain() can be ignored
119        } catch (Throwable e) {
120          VM.sysWriteln("Failed to run the agent's premain: " + e.getMessage());
121          e.printStackTrace();
122          System.exit(0);
123        }
124      }
125    
126      RVMMethod getMainMethod() {
127        return mainMethod;
128      }
129    
130      /**
131       * Run "main" thread.
132       * <p>
133       * This code could be made a little shorter by relying on Reflection
134       * to do the classloading and compilation.  We intentionally do it here
135       * to give us a chance to provide error messages that are specific to
136       * not being able to find the main class the user wants to run.
137       * This may be a little silly, since it results in code duplication
138       * just to provide debug messages in a place where very little is actually
139       * likely to go wrong, but there you have it....
140       */
141      @Override
142      @Entrypoint
143      public void run() {
144        launched = true;
145    
146        if (dbg) VM.sysWriteln("MainThread.run() starting ");
147    
148        // Set up application class loader
149        ClassLoader cl = RVMClassLoader.getApplicationClassLoader();
150        setContextClassLoader(cl);
151    
152        runAgents(cl);
153    
154        if (dbg) VM.sysWrite("[MainThread.run() loading class to run... ");
155        // find method to run
156        // load class specified by args[0]
157        RVMClass cls = null;
158        try {
159          Atom mainAtom = Atom.findOrCreateUnicodeAtom(args[0].replace('.', '/'));
160          TypeReference mainClass = TypeReference.findOrCreate(cl, mainAtom.descriptorFromClassName());
161          cls = mainClass.resolve().asClass();
162          cls.resolve();
163          cls.instantiate();
164          cls.initialize();
165        } catch (NoClassDefFoundError e) {
166          if (dbg) VM.sysWrite("failed.]");
167          // no such class
168          VM.sysWrite(e + "\n");
169          return;
170        }
171        if (dbg) VM.sysWriteln("loaded.]");
172    
173        // find "main" method
174        //
175        mainMethod = cls.findMainMethod();
176        if (mainMethod == null) {
177          // no such method
178          VM.sysWrite(cls + " doesn't have a \"public static void main(String[])\" method to execute\n");
179          return;
180        }
181    
182        if (dbg) VM.sysWrite("[MainThread.run() making arg list... ");
183        // create "main" argument list
184        //
185        String[] mainArgs = new String[args.length - 1];
186        for (int i = 0, n = mainArgs.length; i < n; ++i) {
187          mainArgs[i] = args[i + 1];
188        }
189        if (dbg) VM.sysWriteln("made.]");
190    
191        if (dbg) VM.sysWrite("[MainThread.run() compiling main(String[])... ");
192        mainMethod.compile();
193        if (dbg) VM.sysWriteln("compiled.]");
194    
195        // Notify other clients that the startup is complete.
196        //
197        Callbacks.notifyStartup();
198    
199        if (dbg) VM.sysWriteln("[MainThread.run() invoking \"main\" method... ");
200        // invoke "main" method with argument list
201        Reflection.invoke(mainMethod, null, null, new Object[]{mainArgs}, true);
202        if (dbg) VM.sysWriteln("  MainThread.run(): \"main\" method completed.]");
203      }
204    }