1 package org.springframework.roo.shell;
2
3 import java.io.BufferedReader;
4 import java.io.File;
5 import java.io.FileInputStream;
6 import java.io.FileNotFoundException;
7 import java.io.IOException;
8 import java.io.InputStream;
9 import java.io.InputStreamReader;
10 import java.net.URL;
11 import java.text.DateFormat;
12 import java.util.Date;
13 import java.util.Properties;
14 import java.util.Set;
15 import java.util.SortedSet;
16 import java.util.TreeSet;
17 import java.util.jar.Manifest;
18 import java.util.logging.Logger;
19
20 import org.apache.felix.scr.annotations.Component;
21 import org.apache.felix.scr.annotations.Reference;
22 import org.osgi.service.component.ComponentContext;
23 import org.springframework.roo.shell.event.AbstractShellStatusPublisher;
24 import org.springframework.roo.shell.event.ShellStatus;
25 import org.springframework.roo.support.logging.HandlerUtils;
26 import org.springframework.roo.support.util.Assert;
27 import org.springframework.roo.support.util.TemplateUtils;
28
29
30
31
32
33
34
35 @Component(componentAbstract=true)
36 public abstract class AbstractShell extends AbstractShellStatusPublisher implements Shell {
37
38 @Reference private ExecutionStrategy executionStrategy;
39 @Reference private Parser parser;
40
41 protected final Logger logger = HandlerUtils.getLogger(getClass());
42 protected boolean inBlockComment = false;
43 protected ExitShellRequest exitShellRequest = null;
44 public static String shellPrompt = "roo> ";
45 public static String completionKeys = "TAB";
46
47 @CliCommand(value={"script"}, help="Parses the specified resource file and executes its commands")
48 public void script(@CliOption(key={"","file"}, help="The file to locate and execute", mandatory=true) File resource,
49 @CliOption(key="lineNumbers", mandatory=false, specifiedDefaultValue="true", unspecifiedDefaultValue="false", help="Display line numbers when executing the script") boolean lineNumbers) {
50 Assert.notNull(resource, "Resource to parser is required");
51 long started = new Date().getTime();
52 InputStream inputStream = null;
53 try {
54 inputStream = new FileInputStream(resource);
55 } catch (FileNotFoundException tryTheClassLoaderInstead) {}
56
57 if (inputStream == null) {
58
59 Set<URL> urls = TemplateUtils.findUrls(getContext().getBundleContext(), "/" + resource.getName());
60
61 Assert.notNull(urls, "Unable to process classpath bundles to locate the script");
62
63 Assert.notEmpty(urls, "Resource '" + resource + "' not found on disk or in classpath");
64 Assert.isTrue(urls.size() == 1, "More than one '" + resource + "' was found in the classpath; unable to continue");
65 try {
66 inputStream = urls.iterator().next().openStream();
67 } catch (IOException e) {
68 throw new IllegalStateException(e);
69 }
70 }
71
72 try {
73 BufferedReader in = new BufferedReader(new InputStreamReader(inputStream));
74 String line;
75 int i = 0;
76 while ((line = in.readLine()) != null) {
77 i++;
78 if (lineNumbers) {
79 logger.fine("Line " + i + ": " + line);
80 } else {
81 logger.fine(line);
82 }
83 if (!"".equals(line.trim())) {
84 boolean success = executeScriptLine(line);
85 if (!success) {
86
87 throw new IllegalStateException("Script execution aborted");
88 }
89 }
90 }
91 in.close();
92 } catch (IOException e) {
93 throw new IllegalStateException(e);
94 }
95 logger.fine("Milliseconds required: " + (new Date().getTime() - started));
96 }
97
98
99
100
101
102
103 protected boolean executeScriptLine(String line) {
104 return executeCommand(line);
105 }
106
107 public boolean executeCommand(String line) {
108
109 setShellStatus(ShellStatus.PARSING);
110
111 long lastWaitMessage = System.currentTimeMillis();
112 while (executionStrategy == null || !executionStrategy.isReadyForCommands()) {
113
114 try {
115 Thread.sleep(500);
116 } catch (InterruptedException ignore) {}
117 if (System.currentTimeMillis() > (lastWaitMessage + (3000*5))) {
118 lastWaitMessage = System.currentTimeMillis();
119 logger.finest("Waiting for process manager to become available - " + new Date().toString());
120 }
121 }
122
123 try {
124
125 if (!inBlockComment && line.contains("/*")) {
126 blockCommentBegin();
127 String lhs = line.substring(0, line.lastIndexOf("/*"));
128 if (line.contains("*/")) {
129 line = lhs + line.substring(line.lastIndexOf("*/")+2);
130 blockCommentFinish();
131 } else {
132 line = lhs;
133 }
134 }
135 if (inBlockComment) {
136 if (!line.contains("*/")) {
137 return true;
138 }
139 blockCommentFinish();
140 line = line.substring(line.lastIndexOf("*/")+2);
141 }
142
143
144 if (!inBlockComment && line.trim().startsWith("//")) {
145 line = line.substring(0, line.indexOf("//"));
146 }
147
148 line = line.replace('\t', ' ');
149 if ("".equals(line.trim())) {
150 setShellStatus(ShellStatus.EXECUTION_COMPLETE);
151 return true;
152 }
153 ParseResult parseResult = parser.parse(line);
154 if (parseResult == null) {
155 return false;
156 } else {
157 setShellStatus(ShellStatus.EXECUTING);
158 Object result = executionStrategy.execute(parseResult);
159 setShellStatus(ShellStatus.EXECUTION_RESULT_PROCESSING);
160 if (result != null) {
161 if (result instanceof ExitShellRequest) {
162 exitShellRequest = (ExitShellRequest) result;
163 } else if (result instanceof Iterable<?>) {
164 for (Object o : (Iterable<?>)result) {
165 logger.info(o.toString());
166 }
167 } else {
168 logger.info(result.toString());
169 }
170 }
171 }
172 } catch (RuntimeException ex) {
173 setShellStatus(ShellStatus.EXECUTION_RESULT_PROCESSING);
174
175
176
177 try {
178 logCommandIfRequired(line, false);
179 } catch (Exception ignoreIt) {}
180 return false;
181 } finally {
182 setShellStatus(ShellStatus.EXECUTION_COMPLETE);
183 }
184 logCommandIfRequired(line, true);
185 return true;
186 }
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205 protected void logCommandIfRequired(String line, boolean successful) {
206 if (line.startsWith("script")) {
207 logCommandToOutput((successful ? "// " : "// [failed] ") + line);
208 } else {
209 logCommandToOutput((successful ? "" : "// [failed] ") + line);
210 }
211 }
212
213
214
215
216
217
218
219
220
221
222
223 protected void logCommandToOutput(String processedLine) {
224
225 }
226
227
228
229
230
231
232
233
234 public void setPromptPath(String path) {
235 if ("".equals(path) || path == null) {
236 shellPrompt = "roo> ";
237 } else {
238 shellPrompt = path + " roo> ";
239 }
240 }
241
242 public ExitShellRequest getExitShellRequest() {
243 return exitShellRequest;
244 }
245
246 @CliCommand(value={"//", ";"}, help="Inline comment markers (start of line only)")
247 public void inlineComment() {}
248
249 @CliCommand(value={"/*"}, help="Start of block comment")
250 public void blockCommentBegin() {
251 Assert.isTrue(!inBlockComment, "Cannot open a new block comment when one already active");
252 inBlockComment = true;
253 }
254
255 @CliCommand(value={"*/"}, help="End of block comment")
256 public void blockCommentFinish() {
257 Assert.isTrue(inBlockComment, "Cannot close a block comment when it has not been opened");
258 inBlockComment = false;
259 }
260
261 @CliCommand(value={"system properties"}, help="Shows the shell's properties")
262 public String props() {
263 Properties properties = System.getProperties();
264 SortedSet<String> data = new TreeSet<String>();
265 for (Object property : properties.keySet()) {
266 Object value = properties.get(property);
267 data.add(property + " = " + value);
268 }
269
270 StringBuilder sb = new StringBuilder();
271 for (String line : data) {
272 sb.append(line).append(System.getProperty("line.separator"));
273 }
274 return sb.toString();
275 }
276
277 @CliCommand(value={"date"}, help="Displays the local date and time")
278 public String date() {
279 return DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL).format(new Date());
280 }
281
282 @CliCommand(value={"version"}, help="Displays shell version")
283 public String version(@CliOption(key="", help="Special version flags") String extra) {
284 StringBuilder sb = new StringBuilder();
285
286 if ("jaime".equals(extra)) {
287 sb.append(" /\\ /l").append(System.getProperty("line.separator"));
288 sb.append(" ((.Y(!").append(System.getProperty("line.separator"));
289 sb.append(" \\ |/").append(System.getProperty("line.separator"));
290 sb.append(" / 6~6,").append(System.getProperty("line.separator"));
291 sb.append(" \\ _ +-.").append(System.getProperty("line.separator"));
292 sb.append(" \\`-=--^-' \\").append(System.getProperty("line.separator"));
293 sb.append(" \\ \\ |\\--------------------------+").append(System.getProperty("line.separator"));
294 sb.append(" _/ \\ | Thanks for loading Roo! |").append(System.getProperty("line.separator"));
295 sb.append(" ( . Y +---------------------------+").append(System.getProperty("line.separator"));
296 sb.append(" /\"\\ `---^--v---.").append(System.getProperty("line.separator"));
297 sb.append(" / _ `---\"T~~\\/~\\/").append(System.getProperty("line.separator"));
298 sb.append(" / \" ~\\. !").append(System.getProperty("line.separator"));
299 sb.append(" _ Y Y.~~~ /'").append(System.getProperty("line.separator"));
300 sb.append(" Y^| | | Roo 7").append(System.getProperty("line.separator"));
301 sb.append(" | l | / . /'").append(System.getProperty("line.separator"));
302 sb.append(" | `L | Y .^/ ~T").append(System.getProperty("line.separator"));
303 sb.append(" | l ! | |/ | | ____ ____ ____").append(System.getProperty("line.separator"));
304 sb.append(" | .`\\/' | Y | ! / __ \\/ __ \\/ __ \\").append(System.getProperty("line.separator"));
305 sb.append(" l \"~ j l j L______ / /_/ / / / / / / /").append(System.getProperty("line.separator"));
306 sb.append(" \\,____{ __\"\" ~ __ ,\\_,\\_ / _, _/ /_/ / /_/ /").append(System.getProperty("line.separator"));
307 sb.append(" ~~~~~~~~~~~~~~~~~~~~~~~~~~~ /_/ |_|\\____/\\____/").append(" ").append(versionInfo()).append(System.getProperty("line.separator"));
308 return sb.toString();
309 }
310
311 sb.append(" ____ ____ ____ ").append(System.getProperty("line.separator"));
312 sb.append(" / __ \\/ __ \\/ __ \\ ").append(System.getProperty("line.separator"));
313 sb.append(" / /_/ / / / / / / / ").append(System.getProperty("line.separator"));
314 sb.append(" / _, _/ /_/ / /_/ / ").append(System.getProperty("line.separator"));
315 sb.append("/_/ |_|\\____/\\____/ ").append(" ").append(versionInfo()).append(System.getProperty("line.separator"));
316 sb.append(System.getProperty("line.separator"));
317
318 return sb.toString();
319 }
320
321 public static String versionInfo() {
322
323 String bundleVersion = null;
324 String gitCommitHash = null;
325 try {
326 String classContainer = AbstractShell.class.getProtectionDomain().getCodeSource().getLocation().toString();
327 if (classContainer.endsWith(".jar")) {
328
329 URL manifestUrl = new URL("jar:" + classContainer + "!/META-INF/MANIFEST.MF");
330 Manifest manifest = new Manifest(manifestUrl.openStream());
331 bundleVersion = manifest.getMainAttributes().getValue("Bundle-Version");
332 gitCommitHash = manifest.getMainAttributes().getValue("Git-Commit-Hash");
333 }
334 } catch (Exception ignoreAndMoveOn) {}
335
336 StringBuilder sb = new StringBuilder();
337
338 if (bundleVersion != null) {
339 sb.append(bundleVersion);
340 }
341
342 if (gitCommitHash != null) {
343 if (sb.length() > 0) {
344 sb.append(" ");
345 }
346 sb.append("[rev ");
347 sb.append(gitCommitHash.substring(0,7));
348 sb.append("]");
349 }
350
351 if (sb.length() == 0) {
352 sb.append("UNKNOWN VERSION");
353 }
354
355 return sb.toString();
356 }
357
358 public String getShellPrompt() {
359 return shellPrompt;
360 }
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382 public File getHome() {
383 String rooHome = getHomeAsString();
384 File f = new File(rooHome);
385 Assert.isTrue(!f.exists() || (f.exists() && f.isDirectory()), "Path '" + f.getAbsolutePath() + "' must be a directory, or it must not exist");
386 if (!f.exists()) {
387 f.mkdirs();
388 }
389 Assert.isTrue(f.exists() && f.isDirectory(), "Path '" + f.getAbsolutePath() + "' is not a directory; please specify roo.home system property correctly");
390 return f;
391 }
392
393 protected abstract String getHomeAsString();
394
395 protected abstract ComponentContext getContext();
396
397 protected final Parser getParser() {
398 return parser;
399 }
400 }