001/*
002 * Copyright (C) 2012 eXo Platform SAS.
003 *
004 * This is free software; you can redistribute it and/or modify it
005 * under the terms of the GNU Lesser General Public License as
006 * published by the Free Software Foundation; either version 2.1 of
007 * the License, or (at your option) any later version.
008 *
009 * This software is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *
014 * You should have received a copy of the GNU Lesser General Public
015 * License along with this software; if not, write to the Free
016 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
017 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
018 */
019package org.crsh.standalone;
020
021import org.crsh.cli.Required;
022import org.crsh.cli.Usage;
023import org.crsh.cli.descriptor.CommandDescriptor;
024import org.crsh.cli.Argument;
025import org.crsh.cli.Command;
026import org.crsh.cli.Option;
027import org.crsh.cli.impl.lang.CommandFactory;
028import org.crsh.cli.impl.invocation.InvocationMatch;
029import org.crsh.cli.impl.invocation.InvocationMatcher;
030import org.crsh.cli.impl.lang.Instance;
031import org.crsh.cli.impl.lang.Util;
032import org.crsh.shell.Shell;
033import org.crsh.shell.ShellFactory;
034import org.crsh.shell.impl.remoting.RemoteClient;
035import org.crsh.util.Utils;
036import org.crsh.vfs.FS;
037import org.crsh.vfs.spi.file.FileMountFactory;
038import org.crsh.vfs.spi.url.ClassPathMountFactory;
039
040import java.lang.instrument.Instrumentation;
041import java.util.Collections;
042import java.util.List;
043import java.util.Map;
044import java.util.Properties;
045import java.util.logging.Level;
046import java.util.logging.Logger;
047
048public class Agent {
049
050  /** . */
051  private static Logger log = Logger.getLogger(Agent.class.getName());
052
053  public static void agentmain(final String agentArgs, final Instrumentation inst) throws Exception {
054    log.log(Level.INFO, "CRaSH agent loaded");
055
056    //
057    Thread t = new Thread() {
058      @Override
059      public void run() {
060        try {
061          CommandDescriptor<Instance<Agent>> c = CommandFactory.DEFAULT.create(Agent.class);
062          InvocationMatcher<Instance<Agent>> matcher = c.matcher();
063          InvocationMatch<Instance<Agent>> match = matcher.parse(agentArgs);
064          match.invoke(Util.wrap(new Agent(inst)));
065        } catch (Exception e) {
066          e.printStackTrace();
067        }
068      }
069    };
070
071    //
072    t.start();
073    log.log(Level.INFO, "Spawned CRaSH thread " + t.getId() + " for further processing");
074  }
075
076  /** . */
077  private final Instrumentation instrumentation;
078
079  public Agent(Instrumentation instrumentation) {
080    this.instrumentation = instrumentation;
081  }
082
083  @Command
084  public void main(
085      @Required
086      @Option(names={"c","cmd"})
087      @Usage("the command path")
088      String cmd,
089      @Required
090      @Option(names={"conf"})
091      @Usage("the conf path")
092      String conf,
093      @Option(names={"p","property"})
094      @Usage("set a property of the form a=b")
095      List<String> properties,
096      @Argument(name = "port")
097      Integer port) throws Exception {
098
099    //
100    FileMountFactory fileDriver = new FileMountFactory(Utils.getCurrentDirectory());
101    ClassPathMountFactory classpathDriver = new ClassPathMountFactory(Thread.currentThread().getContextClassLoader());
102
103    //
104    FS cmdFS = new FS.Builder().register("file", fileDriver).register("classpath", classpathDriver).mount(cmd).build();
105    FS confFS = new FS.Builder().register("file", fileDriver).register("classpath", classpathDriver).mount(conf).build();
106    Bootstrap bootstrap = new Bootstrap(Thread.currentThread().getContextClassLoader(), confFS, cmdFS);
107
108    //
109    if (properties != null) {
110      Properties config = new Properties();
111      for (String property : properties) {
112        int index = property.indexOf('=');
113        if (index == -1) {
114          config.setProperty(property, "");
115        } else {
116          config.setProperty(property.substring(0, index), property.substring(index + 1));
117        }
118      }
119      bootstrap.setConfig(config);
120    }
121
122    // Set the instrumentation available as an attribute
123    Map<String, Object> attributes = Collections.<String, Object>singletonMap("instrumentation", instrumentation);
124    bootstrap.setAttributes(attributes);
125
126    // Do bootstrap
127    bootstrap.bootstrap();
128
129    //
130    if (port != null) {
131      try {
132        ShellFactory factory = bootstrap.getContext().getPlugin(ShellFactory.class);
133        Shell shell = factory.create(null);
134        RemoteClient client = new RemoteClient(port, shell);
135        log.log(Level.INFO, "Callback back remote on port " + port);
136        client.connect();
137        client.getRunnable().run();
138      }
139      finally {
140        bootstrap.shutdown();
141      }
142    }
143  }
144}