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 }