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