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 }