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 }