1
2
3
4
5
6
7
8
9
10
11
12 package org.hyphenType.wrapper;
13
14 import static org.hyphenType.datastructure.annotations.ArgumentsObject.DEFAULT_DOUBLE_HYPHEN;
15 import static org.hyphenType.datastructure.annotations.ArgumentsObject.DEFAULT_EQUALS;
16
17 import java.io.ByteArrayOutputStream;
18 import java.io.File;
19 import java.io.FileNotFoundException;
20 import java.io.IOException;
21 import java.io.PrintWriter;
22 import java.lang.reflect.Constructor;
23 import java.lang.reflect.InvocationTargetException;
24 import java.lang.reflect.Method;
25 import java.lang.reflect.Modifier;
26 import java.net.URISyntaxException;
27 import java.util.ArrayList;
28 import java.util.Arrays;
29 import java.util.List;
30 import java.util.StringTokenizer;
31 import java.util.jar.JarFile;
32 import java.util.jar.Manifest;
33
34 import org.hyphenType.datastructure.Options;
35 import org.hyphenType.debug.HTLogger;
36 import org.hyphenType.exceptions.InvalidOptionsInterfaceException;
37 import org.hyphenType.input.UserInput;
38 import org.hyphenType.lexerparser.exceptions.MandatorySimpleArgumentNotFoundException;
39 import org.hyphenType.lexerparser.exceptions.OptionValuesException;
40 import org.hyphenType.optionsextractor.OptionsExtractor;
41 import org.hyphenType.optionsextractor.OptionsExtractorException;
42 import org.hyphenType.unittesting.NonExceptionalExit;
43
44
45
46
47
48
49
50
51
52
53 public class StandAloneAppWrapper {
54
55
56
57
58
59 public static final String KEY = "StandAloneAppWrapper-main-class";
60
61
62
63
64 public static final String ARGUMENT = DEFAULT_DOUBLE_HYPHEN + KEY + DEFAULT_EQUALS;
65
66
67
68
69
70
71
72
73
74 @SuppressWarnings("unchecked")
75 public static void main(final String... arguments) throws Throwable {
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96 String mainClassName = null;
97
98
99
100
101
102
103
104
105 try {
106 JarFile jarFile = new JarFile(new File(StandAloneAppWrapper.class.getProtectionDomain().getCodeSource().getLocation().toURI()));
107 Manifest m = jarFile.getManifest();
108 mainClassName = m.getMainAttributes().getValue(KEY);
109 } catch (FileNotFoundException e) {
110 mainClassName = null;
111 } catch (IOException e) {
112 mainClassName = null;
113 } catch (URISyntaxException e) {
114 mainClassName = null;
115 }
116
117
118
119
120 try {
121 StringTokenizer st = new StringTokenizer(System.getProperty("java.class.path"), System.getProperty("path.separator"));
122 if (st.countTokens() == 1) {
123
124 JarFile jarFile = new JarFile(new File(st.nextToken()));
125 Manifest m = jarFile.getManifest();
126 mainClassName = m.getMainAttributes().getValue(KEY);
127 }
128 } catch (FileNotFoundException e) {
129 mainClassName = null;
130 } catch (IOException e) {
131 mainClassName = null;
132 }
133
134 String[] correctedArguments = arguments;
135
136
137
138
139 if (mainClassInvalid(mainClassName) && correctedArguments.length > 0 && correctedArguments[0].startsWith(ARGUMENT)) {
140 mainClassName = correctedArguments[0].substring(ARGUMENT.length(), correctedArguments[0].length());
141 correctedArguments = Arrays.copyOfRange(correctedArguments, 1, correctedArguments.length);
142 }
143
144
145
146
147 if (mainClassInvalid(mainClassName)) {
148 mainClassName = System.getProperty(KEY);
149 }
150
151
152
153
154 if (mainClassInvalid(mainClassName)) {
155 mainClassName = System.getenv(KEY);
156 }
157
158
159
160
161
162
163
164
165
166
167 if (mainClassInvalid(mainClassName)) {
168 try {
169 StringTokenizer st = new StringTokenizer(System.getProperty("java.class.path"), System.getProperty("path.separator"));
170 if (st.countTokens() == 1) {
171
172 JarFile jarFile = new JarFile(new File(st.nextToken()));
173 Manifest m = jarFile.getManifest();
174 mainClassName = m.getMainAttributes().getValue("Main-Class");
175 }
176 } catch (FileNotFoundException e) {
177 mainClassName = null;
178 } catch (IOException e) {
179 mainClassName = null;
180 }
181 }
182
183
184
185
186 if (mainClassInvalid(mainClassName)) {
187 try {
188 String candidateClassName = System.getProperty("sun.java.command");
189
190
191
192 if (candidateClassName.contains(" ")) {
193 candidateClassName = candidateClassName.substring(0, candidateClassName.indexOf(" "));
194 }
195 if (!StandAloneAppWrapper.class.equals(StandAloneAppWrapper.class.getClassLoader().loadClass(candidateClassName))) {
196 mainClassName = candidateClassName;
197 }
198 } catch (ClassNotFoundException e) {
199 mainClassName = null;
200 }
201 }
202
203 if (mainClassInvalid(mainClassName)) {
204 System.err.println("Error: could not find the main class to execute. Possible methods are (in this order of precedence):\n" + "(1) add the property '" + KEY + "' to the MANIFEST.MF file;\n" + "(2) pass a first argument called " + ARGUMENT + " to the " + StandAloneAppWrapper.class.getName() + " class;\n" + "(3) set the system property " + KEY + " for the JVM (utilizing the parameter -D or programmatically before calling the " + StandAloneAppWrapper.class.getName() + " class);\n" + "(4) set the environment variable " + KEY + " before executing this class;\n" + "(5) make your main class extend or call " + StandAloneAppWrapper.class.getName() + ", put your main class in a jar file, and start the JVM using \"java -jar\"; or\n" + "(6) simply make your main class YourClass extend or call " + StandAloneAppWrapper.class.getName() + " and start the JVM using \"java YourClass\" (works also inside of your favorite IDE).");
205 return;
206 }
207
208 Class clazz;
209 try {
210 clazz = StandAloneAppWrapper.class.getClassLoader().loadClass(mainClassName);
211 } catch (ClassNotFoundException e) {
212 System.err.println("Error: could not load the main class " + mainClassName);
213 return;
214 }
215
216 new StandAloneAppWrapper().invokeMain(clazz, correctedArguments);
217 }
218
219 protected static boolean mainClassInvalid(String mainClassName) {
220 if (mainClassName == null)
221 return true;
222 try {
223 StandAloneAppWrapper.class.getClassLoader().loadClass(mainClassName);
224 } catch (ClassNotFoundException e) {
225 return true;
226 }
227 return false;
228 }
229
230
231
232
233
234
235
236
237
238
239
240
241 public static void main(final Class<?> mainClass, final String... arguments) throws Throwable {
242
243 new StandAloneAppWrapper().invokeMain(mainClass, arguments);
244 }
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263 public final void invokeMain(final Class<?> mainClass, final String[] arguments) throws Throwable {
264 invokeMain(mainClass, arguments, true);
265 }
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280 public final void invokeMain(final Class<?> mainClass, final String[] arguments, final boolean trapThrowable) throws Throwable {
281
282 List<String> messages = new ArrayList<String>();
283
284 for (Method method : mainClass.getMethods()) {
285 if (method.getName().equals("main")) {
286 if (Modifier.isStatic(method.getModifiers())) {
287 if (method.getParameterTypes().length == 2 && method.getParameterTypes()[0].equals(Class.class) && method.getParameterTypes()[1].equals(String[].class)) {
288
289
290 continue;
291 }
292 if (method.getParameterTypes().length == 1 && method.getParameterTypes()[0].equals(String[].class)) {
293
294
295 continue;
296 }
297 }
298 if (method.getParameterTypes().length != 1) {
299 messages.add("Method main in class " + mainClass.getName() + " should have only one argument.");
300 continue;
301 }
302 if (!method.getParameterTypes()[0].isInterface()) {
303 messages.add("The type of the argument of the method main of the class " + mainClass.getName() + " should be an interface.");
304 continue;
305 }
306 if (!Options.class.isAssignableFrom(method.getParameterTypes()[0])) {
307 messages.add(String.format("The parameter of the main method should be of an interface that extends %s.", Options.class.getName()));
308 continue;
309 }
310
311 try {
312 Options<?> options;
313
314 try {
315 options = buildOptionsObject(method.getParameterTypes()[0], arguments);
316 systemOptions = options;
317 } catch (MandatorySimpleArgumentNotFoundException e) {
318 System.err.println(e.getLocalizedMessage());
319 HTLogger.log(e);
320 return;
321 } catch (OptionValuesException e) {
322 System.err.println(e.getLocalizedMessage());
323 HTLogger.log(e);
324 return;
325 }
326
327 Object instance = null;
328
329 if (!Modifier.isStatic(method.getModifiers())) {
330 Constructor<?> constructor;
331 try {
332 constructor = mainClass.getConstructor();
333 } catch (NoSuchMethodException e) {
334 messages.add(String.format("Class %s should have a default (empty) constructor.", mainClass.getName()));
335 continue;
336 }
337 instance = constructor.newInstance();
338 }
339
340 try {
341
342
343
344
345
346 method.invoke(instance, options);
347
348 } catch (InvocationTargetException e) {
349 if(e.getCause() instanceof NonExceptionalExit)
350 throw e.getCause();
351 if(!options.exit(e.getCause())) {
352
353
354
355
356 messages.add(e.getCause().getLocalizedMessage());
357 printExceptionMessage(messages, e);
358 if(!trapThrowable) {
359 throw e.getCause();
360 }
361 }
362 }
363
364 } catch (InvalidOptionsInterfaceException e) {
365 messages.add(String.format("Invalid options interface %s. Check the error trace for details:", method.getParameterTypes()[0].getName()));
366 printExceptionMessage(messages, e);
367 } catch (IllegalArgumentException e) {
368 printExceptionMessage(messages, e);
369 } catch (SecurityException e) {
370 printExceptionMessage(messages, e);
371 } catch (InstantiationException e) {
372 printExceptionMessage(messages, e);
373 } catch (IllegalAccessException e) {
374 printExceptionMessage(messages, e);
375 } catch (InvocationTargetException e) {
376 printExceptionMessage(messages, e);
377 }
378 }
379 }
380
381 if (messages.size() > 0) {
382 System.err.println("There was an error executing the main class. This sort of error refers to the structure of the main class, instead of it's execution. Therefore, this error message should not appear when you distribute this application to the final user. Maybe the following can provide some hints on how to solve the problem:");
383 for (String problem : messages) {
384 System.err.println(problem);
385 }
386 }
387 }
388
389 private void printExceptionMessage(List<String> messages, Exception e) {
390 ByteArrayOutputStream baos = new ByteArrayOutputStream();
391 PrintWriter pw = new PrintWriter(baos);
392 e.printStackTrace(pw);
393 pw.flush();
394 try {
395 baos.flush();
396 } catch (IOException e1) {
397
398 e1.printStackTrace();
399 }
400 messages.add(baos.toString());
401 }
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423 @SuppressWarnings("unchecked")
424 protected Options buildOptionsObject(final Class<?> mainClass, final String[] arguments) throws InvalidOptionsInterfaceException, OptionValuesException, OptionsExtractorException {
425 OptionsExtractor<?> optionsExtractor = new OptionsExtractor(mainClass);
426 Options result = optionsExtractor.options(arguments);
427 return result;
428 }
429
430 private static Options<?> systemOptions;
431
432
433
434
435
436
437
438
439
440
441 @SuppressWarnings("unchecked")
442 public static <E extends Options<?>> E options() {
443 return (E) systemOptions;
444 }
445 }