/** * */ package de.saumya.mojo.ruby.script; import java.io.File; import java.io.IOException; import java.io.PrintStream; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.MalformedURLException; import java.util.List; import java.util.Map; import org.codehaus.classworlds.ClassRealm; import org.codehaus.classworlds.DuplicateRealmException; import org.codehaus.classworlds.NoSuchRealmException; import de.saumya.mojo.ruby.Logger; class EmbeddedLauncher extends AbstractLauncher { private static final Class<?>[] No_ARG_TYPES = new Class[0]; private static final Object[] NO_ARGS = new Object[0]; private final ClassRealm classRealm; private final ScriptFactory factory; private final Logger logger; public EmbeddedLauncher(final Logger logger, final ScriptFactory factory) throws ScriptException { this.logger = logger; this.factory = factory; if (factory.classRealm != null) { this.classRealm = cloneClassRealm(factory.jrubyJar, factory.classpathElements, factory.classRealm); } else { this.classRealm = null; } } private ClassRealm cloneClassRealm(final File jrubyJar, final List<String> classpathElements, final ClassRealm classRealm) throws ScriptException { // TODO how to reuse the plugin realm ? // for (final String classpath : classpathElements) { // if (classpath.equals(jrubyJar.getAbsolutePath())) { // return null; // } // } ClassRealm newClassRealm; try { ClassRealm jruby; try { jruby = classRealm.getWorld().getRealm("jruby"); } catch (final NoSuchRealmException e) { jruby = classRealm.getWorld().newRealm("jruby"); if(jrubyJar != null){ jruby.addConstituent(jrubyJar.toURI().toURL()); } } try { jruby.getWorld().disposeRealm("pom"); } catch (final NoSuchRealmException e) { // ignored } newClassRealm = jruby.createChildRealm("pom"); for (final String classpath : classpathElements) { if (!classpath.contains("jruby-complete") || factory.jrubyJar == null) { newClassRealm.addConstituent(new File(classpath).toURI() .toURL()); } } } catch (final DuplicateRealmException e) { throw new ScriptException("error in naming realms", e); } catch (final MalformedURLException e) { throw new ScriptException("hmm. found some malformed URL", e); } return newClassRealm; } @Override protected void doExecute(final File launchDirectory, final List<String> args, final File outputFile) throws ScriptException, IOException { doExecute(launchDirectory, outputFile, args, true); } private void doExecute(final File launchDirectory, final File outputFile, final List<String> args, final boolean warn) throws ScriptException, IOException { final String currentDir; if (launchDirectory != null) { currentDir = System.getProperty("user.dir"); logger.debug("launch directory: " + launchDirectory.getAbsolutePath()); System.setProperty("user.dir", launchDirectory.getAbsolutePath()); } else { currentDir = null; } args.addAll(0, this.factory.switches.list); if (warn) { if (this.factory.jvmArgs.list.size() > 0) { this.logger.warn("have to ignore jvm arguments and properties in the current setup"); } if (this.factory.environment().size() > 0) { this.logger.warn("have to ignore environment settings in the current setup"); } } final PrintStream output = System.out; ClassLoader current = null; try { if (outputFile != null) { final PrintStream writer = new PrintStream(outputFile); System.setOut(writer); this.logger.debug("output file: " + outputFile); } this.logger.debug("args: " + args); if (this.classRealm != null) { current = Thread.currentThread().getContextClassLoader(); Thread.currentThread() .setContextClassLoader(this.classRealm.getClassLoader()); } // use reflection to avoid having jruby as plugin dependency final Class<?> clazz = Thread.currentThread() .getContextClassLoader() .loadClass("org.jruby.Main"); final Object main = clazz.newInstance(); final Method m = clazz.getMethod("run", String[].class); final long start = System.currentTimeMillis(); final Object result = m.invoke(main, (Object) args.toArray(new String[args.size()])); final long end = System.currentTimeMillis(); this.logger.debug("time " + (end - start)); final int status; if (result instanceof Integer) { // jruby before version 1.5 status = ((Integer) result); } else { // jruby from version 1.5 onwards // TODO better error handling like error messages and . . . // TODO see org.jruby.Main final Method statusMethod = result.getClass() .getMethod("getStatus", No_ARG_TYPES); status = (Integer) statusMethod.invoke(result, NO_ARGS); } if (status != 0) { throw new ScriptException("some error in script " + args + ": " + status); } } catch (final InstantiationException e) { throw new ScriptException(e); } catch (final IllegalAccessException e) { throw new ScriptException(e); } catch (final ClassNotFoundException e) { throw new ScriptException(e); } catch (final NoSuchMethodException e) { throw new ScriptException(e); } catch (final InvocationTargetException e) { throw new ScriptException(e); } finally { if (current != null) { Thread.currentThread().setContextClassLoader(current); } System.setOut(output); if (currentDir != null) { System.setProperty("user.dir", currentDir); } if (this.classRealm != null) { try { this.classRealm.getWorld().disposeRealm("pom"); // jrubyClassRealm.setParent(null); } catch (final NoSuchRealmException e) { // ignore } } } } public void executeScript(final File launchDirectory, final String script, final List<String> args, final File outputFile) throws ScriptException, IOException { final StringBuilder buf = new StringBuilder(); for (final Map.Entry<String, String> entry : this.factory.environment().entrySet()) { if (entry.getValue() != null) { buf.append("ENV['") .append(entry.getKey()) .append("']='") .append(entry.getValue()) .append("';"); } } buf.append(script); args.add(0, "-e"); args.add(1, buf.toString()); args.add(2, "--"); doExecute(launchDirectory, outputFile, args, false); } }