001    /*
002     * Copyright (C) 2010 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     */
019    
020    package org.crsh.standalone;
021    
022    import com.sun.tools.attach.VirtualMachine;
023    import org.crsh.cmdline.ClassDescriptor;
024    import org.crsh.cmdline.CommandFactory;
025    import org.crsh.cmdline.IntrospectionException;
026    import org.crsh.cmdline.annotations.Argument;
027    import org.crsh.cmdline.annotations.Command;
028    import org.crsh.cmdline.annotations.Option;
029    import org.crsh.cmdline.annotations.Usage;
030    import org.crsh.cmdline.matcher.CommandMatch;
031    import org.crsh.cmdline.matcher.InvocationContext;
032    import org.crsh.cmdline.matcher.Matcher;
033    import org.crsh.term.BaseTerm;
034    import org.crsh.term.Term;
035    import org.crsh.term.processor.Processor;
036    import org.crsh.term.spi.jline.JLineIO;
037    import org.crsh.term.spi.net.TermIOServer;
038    import org.slf4j.Logger;
039    import org.slf4j.LoggerFactory;
040    
041    import java.io.File;
042    import java.net.*;
043    import java.util.List;
044    import java.util.Properties;
045    
046    /**
047     * @author <a href="mailto:julien.viet@exoplatform.com">Julien Viet</a>
048     * @version $Revision$
049     */
050    public class CRaSH {
051    
052      /** . */
053      private static Logger log = LoggerFactory.getLogger(CRaSH.class);
054    
055      /** . */
056      private final ClassDescriptor<CRaSH> descriptor;
057    
058      public CRaSH() throws IntrospectionException {
059        this.descriptor = CommandFactory.create(CRaSH.class);
060      }
061    
062      @Command
063      public void main(
064        @Option(names = {"h","help"})
065        @Usage("display standalone mode help")
066        Boolean help,
067        @Option(names={"j","jar"})
068        @Usage("specify a file system path of a jar added to the class path")
069        List<String> jars,
070        @Option(names={"c","cmd"})
071        @Usage("specify a file system path of a dir added to the command path")
072        List<String> cmds,
073        @Option(names={"conf"})
074        @Usage("specify a file system path of a dir added to the configuration path")
075        List<String> confs,
076        @Option(names={"p","property"})
077        @Usage("specify a configuration property of the form a=b")
078        List<String> properties,
079        @Argument(name = "pid")
080        @Usage("the optional JVM process id to attach to")
081        Integer pid) throws Exception {
082    
083        //
084        if (Boolean.TRUE.equals(help)) {
085          descriptor.printUsage(System.out);
086        } else if (pid != null) {
087    
088          // Standalone
089          URL url = CRaSH.class.getProtectionDomain().getCodeSource().getLocation();
090          java.io.File f = new java.io.File(url.toURI());
091          log.info("Attaching to remote process " + pid);
092          VirtualMachine vm = VirtualMachine.attach("" + pid);
093    
094          //
095          TermIOServer server = new TermIOServer(new JLineIO(), 0);
096          int port = server.bind();
097          log.info("Callback server set on port " + port);
098    
099          // Build the options
100          StringBuilder sb = new StringBuilder();
101    
102          // Rewrite canonical path
103          if (cmds != null) {
104            for (String cmd : cmds) {
105              File cmdPath = new File(cmd);
106              if (cmdPath.exists()) {
107                sb.append("--cmd ").append(cmdPath.getCanonicalPath()).append(' ');
108              }
109            }
110          }
111    
112          // Rewrite canonical path
113          if (confs != null) {
114            for (String conf : confs) {
115              File confPath = new File(conf);
116              if (confPath.exists()) {
117                sb.append("--conf ").append(confPath.getCanonicalPath()).append(' ');
118              }
119            }
120          }
121    
122          // Rewrite canonical path
123          if (jars != null) {
124            for (String jar : jars) {
125              File jarPath = new File(jar);
126              if (jarPath.exists()) {
127                sb.append("--jar ").append(jarPath.getCanonicalPath()).append(' ');
128              }
129            }
130          }
131    
132          // Propagate canonical config
133          if (properties != null) {
134            for (String property : properties) {
135              sb.append("--property ").append(property).append(' ');
136            }
137          }
138    
139          // Append callback port
140          sb.append(port);
141    
142          //
143          String options = sb.toString();
144          log.info("Loading agent with command " + options);
145          vm.loadAgent(f.getCanonicalPath(), options);
146    
147          //
148          try {
149            server.accept();
150            while (server.execute()) {
151              //
152            }
153          } finally {
154            vm.detach();
155          }
156        } else {
157          final Bootstrap bootstrap = new Bootstrap(Thread.currentThread().getContextClassLoader());
158    
159          //
160          if (cmds != null) {
161            for (String cmd : cmds) {
162              File cmdPath = new File(cmd);
163              bootstrap.addCmdPath(cmdPath);
164            }
165          }
166    
167          //
168          if (confs != null) {
169            for (String conf : confs) {
170              File confPath = new File(conf);
171              bootstrap.addCmdPath(confPath);
172            }
173          }
174    
175          //
176          if (jars != null) {
177            for (String jar : jars) {
178              File jarPath = new File(jar);
179              bootstrap.addJarPath(jarPath);
180            }
181          }
182    
183          //
184          if (properties != null) {
185            Properties config = new Properties();
186            for (String property : properties) {
187              int index = property.indexOf('=');
188              if (index == -1) {
189                config.setProperty(property, "");
190              } else {
191                config.setProperty(property.substring(0, index), property.substring(index + 1));
192              }
193            }
194            bootstrap.setConfig(config);
195          }
196    
197          // Register shutdown hook
198          Runtime.getRuntime().addShutdownHook(new Thread() {
199            @Override
200            public void run() {
201              // Should trigger some kind of run interruption
202            }
203          });
204    
205          // Do bootstrap
206          bootstrap.bootstrap();
207    
208          // Start crash for this command line
209          Term term = new BaseTerm(new JLineIO());
210          org.crsh.shell.impl.CRaSH crash = new org.crsh.shell.impl.CRaSH(bootstrap.getContext());
211          Processor processor = new Processor(term, crash.createSession());
212    
213          //
214          try {
215            processor.run();
216          }
217          finally {
218            bootstrap.shutdown();
219          }
220        }
221      }
222    
223      public static void main(String[] args) throws Exception {
224    
225        StringBuilder line = new StringBuilder();
226        for (int i = 0;i < args.length;i++) {
227          if (i  > 0) {
228            line.append(' ');
229          }
230          line.append(args[i]);
231        }
232    
233        //
234        CRaSH main = new CRaSH();
235        Matcher<CRaSH> matcher = Matcher.createMatcher("main", main.descriptor);
236        CommandMatch<CRaSH, ?, ?> match = matcher.match(line.toString());
237        match.invoke(new InvocationContext(), new CRaSH());
238      }
239    }