1
/*___INFO__MARK_BEGIN__*/
2
/*************************************************************************
4
* The Contents of this file are made available subject to the terms of
5
* the Sun Industry Standards Source License Version 1.2
7
* Sun Microsystems Inc., March, 2001
10
* Sun Industry Standards Source License Version 1.2
11
* =================================================
12
* The contents of this file are subject to the Sun Industry Standards
13
* Source License Version 1.2 (the "License"); You may not use this file
14
* except in compliance with the License. You may obtain a copy of the
15
* License at http://gridengine.sunsource.net/Gridengine_SISSL_license.html
17
* Software provided under this License is provided on an "AS IS" basis,
18
* WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
19
* WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
20
* MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
21
* See the License for the specific provisions governing your rights and
22
* obligations concerning the Software.
24
* The Initial Developer of the Original Code is: Sun Microsystems, Inc.
26
* Copyright: 2001 by Sun Microsystems, Inc.
28
* All Rights Reserved.
30
************************************************************************/
32
/*___INFO__MARK_END__*/
33
package com.sun.grid.jgdi.util.shell;
35
import com.sun.grid.jgdi.JGDIException;
36
import java.io.PrintWriter;
37
import java.lang.annotation.Annotation;
38
import java.lang.reflect.Method;
39
import java.text.MessageFormat;
40
import java.util.ArrayList;
41
import java.util.Arrays;
42
import java.util.HashMap;
43
import java.util.List;
45
import java.util.MissingResourceException;
46
import java.util.ResourceBundle;
53
public abstract class AnnotatedCommand extends AbstractCommand {
55
/* Map holding all commands and their respective optionDesriptorMap */
56
private static Map<Class<? extends AbstractCommand>, Map<String, OptionDescriptor>> commandOptionMap = null;
57
/* Map holding all options of selected command and methods that should be invoked for it */
58
private Map<String, OptionDescriptor> optionDescriptorMap = null;
59
/* List of all command options */
60
private List<OptionInfo> optionList = null;
61
/* Map holding OptionInfo (after paring the args) to the option string */
62
private Map<String, OptionInfo> optionInfoMap = null;
64
/* ResourceBundle containging all error messags */
65
private static final ResourceBundle errorMessages = ResourceBundle.getBundle("com.sun.grid.jgdi.util.shell.ErrorMessagesResources");
67
/* Extra argument list for extending commands that support it. Other have to explicitly check and throw error if not-null */
68
private List<List<String>> extraArgs = null;
71
* Initialize the option map optionDescriptorMap if not yet created.
72
* Map is created by scanning all OptionMethod annotated functions.
73
* NOTE: Only options in the map will be recognized as implemented
75
public static void initOptionDescriptorMap(Class<? extends AbstractCommand> cls, PrintWriter out, PrintWriter err) throws Exception {
76
if (commandOptionMap == null) {
77
commandOptionMap = new HashMap<Class<? extends AbstractCommand>, Map<String, OptionDescriptor>>(30);
79
if (!commandOptionMap.containsKey(cls)) {
80
Map<String, OptionDescriptor> optionDescriptorMap = new HashMap<String, OptionDescriptor>(140);
82
for (Method m : cls.getMethods()) {
83
for (Annotation a : m.getDeclaredAnnotations()) {
84
if (a instanceof OptionAnnotation) {
85
OptionAnnotation o = (OptionAnnotation) a;
86
if (optionDescriptorMap.containsKey(o.value())) {
87
OptionDescriptor od = optionDescriptorMap.get(o.value());
88
if (!od.getMethod().getName().equals(m.getName())) {
89
err.println("WARNING: Attempt to override " +od.getMethod().getDeclaringClass().getName()+"."+od.getMethod().getName() + " by " + cls.getName() + "." + m.getName() + "() for option \""+o.value()+"\"\n");
92
//Add method to the optionDescriptorMap
93
optionDescriptorMap.put(o.value(), new OptionDescriptor(o.value(), o.defaultStringArg(), o.min(), o.extra(), m, out, err));
98
commandOptionMap.put(cls, optionDescriptorMap);
103
* Finds option info based on option name.
104
* Use to do a lookahead to see if specific option was in the argument list.
105
* @param option String
106
* @return OptionInfo associated with the option or null does not exist
108
public OptionInfo getOptionInfo(String option) {
109
if (optionInfoMap.containsKey(option)) {
110
return optionInfoMap.get(option);
117
* @return Map<String, OptionDescriptor>
119
public Map<String, OptionDescriptor> getOptionDescriptorMap() {
120
if (this.optionDescriptorMap == null) {
121
optionDescriptorMap = commandOptionMap.get(this.getClass());
123
return optionDescriptorMap;
127
* Gets the extra arguments for the command. Only certail commands actually need it: qrsub, qsub, qalter
128
* @return List<String>
130
protected List<String> getExtraArguments() {
131
List<String> out = new ArrayList<String>();
132
for (List<String> temp : extraArgs) {
133
for (String arg : temp) {
141
* Gets the original extra arguments (List of List of Stirng) for the command. Only certail commands actually need it: qrsub, qsub, qalter
142
* @return List<List<String>>
144
protected List<List<String>> getOriginalExtraArguments() {
149
* Check if there are extra arguments. If command should not support it (not a qrsub, qsub, qalter)
152
protected boolean hasExtraArguments() {
153
return extraArgs == null || extraArgs.size()==0;
157
protected void parseOptions(String[] args) throws Exception {
158
optionList = new ArrayList<OptionInfo>();
159
optionInfoMap = new HashMap<String, OptionInfo>();
160
List<List<String>> argList = tokenizeArgs(args);
161
while (!argList.isEmpty()) {
162
//Get option info, token are divided by known option
163
//to option and reladet arguments
164
//The arguments are tested for declared multiplicity
165
OptionInfo info = getOptionInfo(getOptionDescriptorMap(), argList);
166
//If we got extra argument list, we skip it (info is null)
168
optionList.add(info);
169
optionInfoMap.put(info.getOd().getOption(), info);
175
* Parse all arguments and invoke appropriate methods
176
* It calls annotated functions for every recognized option.
177
* @param args command line options
178
* @throws java.lang.Exception an exception during the option call
179
* or some runnable exception, when the options are wrong
181
protected void parseAndInvokeOptions(String[] args) throws Exception {
187
* Invoke appropriate methods
188
* It calls annotated functions for every recognized option.
189
* @throws java.lang.Exception an exception during the option call
190
* or some runnable exception, when the options are wrong
192
protected void invokeOptions() throws Exception {
193
for (OptionInfo oi : optionList) {
194
oi.invokeOption(this);
199
* Parse the ergument array to Array list of separated tokens
200
* The tokens are then divided by known options
201
* @param args command line option
202
* @return List of tokens
204
protected List<List<String>> tokenizeArgs(String[] args) {
205
List<List<String>> argList = new ArrayList<List<String>>();
206
List<String> tempList = new ArrayList<String>();
207
//Expand args to list of args, 'arg1,arg2 arg3' -> List(List(arg1,arg2),List(arg3)) so we can do magic later on
208
for (String arg : args) {
209
String[] subElems = arg.split("[,]");
210
for (String subElem : subElems) {
211
subElem = subElem.trim();
212
if (subElem.length() > 0) {
213
tempList.add(subElem);
216
argList.add(new ArrayList<String>(tempList));
223
* Lists all known (registered) options for the command
225
@OptionAnnotation(value = "-list", min=0)
226
public void listOptions(final OptionInfo oi) throws JGDIException {
227
String str = new String();
228
Set<String> set = oi.getMap().keySet();
229
String[] options = oi.getMap().keySet().toArray(new String[set.size()]);
230
Arrays.sort(options);
231
for (String option : options) {
234
// To avoid the continue of the command
235
throw new AbortException();
239
* Gets option info. Returns correct Option object and all its arguments
240
* Default behavior: 1) Read all mandatory args. Error if not complete
241
* 2) Read max optional args until end or next option found
242
* Arguments have to be already expanded
243
* @param optMap {@link Map} holding all options for current command.
244
* @param args argument list
245
* @return return the option info structure
247
//TODO LP: Discuss this enhancement. We now accept "arg1,arg2 arg3,arg4" as 4 valid args
248
private OptionInfo getOptionInfo(final Map<String, OptionDescriptor> optMap, List<List<String>> args) throws JGDIException {
249
//Check we have a map set
250
if (optMap.isEmpty()) {
251
throw new UnsupportedOperationException("Cannot get OptionInfo. Option map is empty!");
254
List<List<String>> extraArgs = null;
255
List<String> tempArg = args.get(0);
256
String option = tempArg.get(0);
258
boolean extraArgsValid = this.getClass().getAnnotation(CommandAnnotation.class).hasExtraArgs();
260
//We have unknown option or extra arguments (qconf -ddd x qalter 45)
261
if (!optMap.containsKey(option)) {
262
extraArgs = new ArrayList<List<String>>();
263
//Read all extra argument to the end of the of all args.
264
while (!args.isEmpty()) {
265
extraArgs.add(args.remove(0));
266
//If some is recognized as valid option. First extra arg is InvalidArgument
267
if (!extraArgsValid || optMap.containsKey(option)) {
268
msg = getErrorMessage("InvalidArgument", extraArgs.get(0).get(0));
269
int exitCode = getCustomExitCode("InvalidArgument", extraArgs.get(0).get(0));
271
throw new JGDIException(msg, exitCode);
274
//We now store the extraArgs to this command.
275
this.extraArgs = extraArgs;
276
return null; //null says now you have the extra args so save them
279
//We take out first arg list
280
tempArg = args.remove(0);
281
//And remove the recognized option
283
//And we put it back if not empty
284
if (tempArg.size()>0) {
285
args.add(0, tempArg);
287
OptionDescriptor od = optMap.get(option);
288
List<List<String>> argList = new ArrayList<List<String>>();
289
List<String> tempList = new ArrayList<String>();
292
if (!od.isWithoutArgs()) {
295
//Try to get all mandatory args
296
while (i < od.getMandatoryArgCount() && args.size() > 0) {
297
tempArg = args.remove(0);
299
while (tempArg.size() > 0 && i < od.getMandatoryArgCount()) {
300
arg = tempArg.remove(0);
304
if (tempList.size() > 0) {
305
argList.add(new ArrayList<String>(tempList));
308
//We check we completed the subList (comma separated list) otherwise we mark it for continue
309
boolean appendToLastList = false;
310
if (tempArg.size() > 0) {
311
appendToLastList = true;
312
args.add(0, tempArg);
315
//Check we have all mandatory args
316
if (i != od.getMandatoryArgCount()) {
317
final String msgType = (i == 0) ? "NoArgument" : "LessArguments";
318
msg = getErrorMessage(msgType, option, argList);
319
int exitCode = getCustomExitCode(msgType, option);
320
throw new JGDIException(msg, exitCode);
323
//Try to get as many optional args as possible
325
boolean exit = false;
327
while (!exit && (i < od.getOptionalArgCount() && args.size() > 0)) {
328
tempArg = args.remove(0);
330
if (appendToLastList) {
331
appendToLastList = false;
332
tempList = argList.remove(argList.size() - 1);
334
for (int j = 0; j<tempArg.size(); j++) {
335
//We have comma separated list with more elems than required - Error
336
if (i >= od.getOptionalArgCount()) {
337
final String msgType = "MoreArguments";
338
msg = getErrorMessage(msgType, option, argList);
339
int exitCode = getCustomExitCode(msgType, option);
340
throw new JGDIException(msg, exitCode);
342
argVal = tempArg.get(j);
344
if (optMap.containsKey(argVal)) {
346
//Next recognized option must be the first in the sub list
348
args.add(0, tempArg);
351
//This is an error since option is found in the list
352
//qconf -sq a,b c,d,-sh <= -sh is known option so whole command is wrong
353
//qconf -sq a,g -sh would be accepted
354
msg = getErrorMessage("InvalidArgument", option, argVal);
355
int exitCode = getCustomExitCode("InvalidArgument", option);
356
throw new JGDIException(msg, exitCode);
359
tempList.add(argVal);
362
//We should always add only complete sub lists.
363
argList.add(new ArrayList<String>(tempList));
367
//TODO LP: This should probably never happen - remove?
368
//Check if we have more args than expected
369
if (argList.size() > od.getMaxArgCount()) {
370
msg = "Expected only " + od.getMaxArgCount() + " arguments for " + option + " option. Got " + argList.size() + ".";
371
throw new IllegalArgumentException(msg);
374
if (argList.size() == 0 && od.getMandatoryArgCount() == 0 && od.getOptionalArgCount() != 0 && od.getDefaultArg().length()> 0) {
375
tempList = new ArrayList<String>();
376
tempList.add(od.getDefaultArg());
377
argList.add(tempList);
380
boolean hasNoArgs = (od.getMandatoryArgCount() == 0) && (od.getOptionalArgCount() == 0);
383
return new OptionInfo(od, optMap);
386
//TODO: Check we have correct args
387
return new OptionInfo(od, argList, optMap);
390
/** Hepler method to get the right error message */
391
String getErrorMessage(String msgType, String optionString) {
392
return getErrorMessage(msgType, optionString, new ArrayList<List<String>>());
395
/** Hepler method to get the right error message */
396
String getErrorMessage(String msgType, String optionString, String arg) {
397
List<List<String>> argList = new ArrayList<List<String>>();
398
List<String> temp = new ArrayList<String>();
401
return getErrorMessage(msgType, optionString, argList);
404
/** Hepler method to get the right error message */
405
String getErrorMessage(String msgType, String optionString, List<List<String>> args) {
406
String[] tmp = this.getClass().getName().split("[.]");
407
String cmdName = tmp[tmp.length-1];
408
return getErrorMessage(msgType, cmdName, optionString, args, getUsage());
411
public static String getDefaultErrorMessage(String cmdName, String msgType, String arg) {
412
List<List<String>> argList = new ArrayList<List<String>>();
413
List<String> temp = new ArrayList<String>();
416
return getErrorMessage(msgType, cmdName, "", argList, "");
419
/** Hepler method to get the right error message */
420
private static String getErrorMessage(String msgType, String cmdName, String optionString, List<List<String>> args, String usage) {
421
String cmdString = cmdName.split("Command")[0].toLowerCase();
422
String prefix = msgType+"."+cmdName+".";
423
String argString = "";
424
if (args != null && args.size() > 0) {
425
for (List<String> temp : args) {
426
for (String arg : temp) {
427
argString += arg + " ";
430
argString = argString.substring(0, argString.length()-1);
432
//TODO LP: Remove the New generic message string
433
String msg = "Missing error messages file!";
434
try { //Try to get option specific message
435
msg = errorMessages.getString(prefix+optionString);
436
} catch (MissingResourceException ex) {
437
try { //Try to get command default message
438
msg = errorMessages.getString(prefix+"default");
439
} catch (MissingResourceException dex) {
440
msg = errorMessages.getString(prefix+"generic");
443
return MessageFormat.format(msg, optionString, argString, "Usage: "+cmdString+" -help", usage);
446
/** Hepler method to get the right exit code for specified error (msgType) */
447
int getCustomExitCode(String msgType, String optionString) {
448
String[] tmp = this.getClass().getName().split("[.]");
449
String cmdName = tmp[tmp.length-1];
450
return getCustomExitCode(msgType, cmdName, optionString);
453
public static int getCustomExitCode(String msgType, String cmdName, String optionString) {
454
String cmdString = cmdName.split("Command")[0].toLowerCase();
455
String prefix = msgType+"."+cmdName+".";
456
String argString = "";
458
try { //Try to get option specific exitCode for this message type
459
exitCode = Integer.valueOf(errorMessages.getString(prefix+optionString+".exitCode"));
460
} catch (MissingResourceException ex) {
461
try { //Try to get command default message specific exitCode
462
exitCode = Integer.valueOf(errorMessages.getString(prefix+"default.exitCode"));
463
} catch (MissingResourceException dex) {}
b'\\ No newline at end of file'