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