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