001: /**
002: * Sequoia: Database clustering technology.
003: * Copyright (C) 2002-2004 French National Institute For Research In Computer
004: * Science And Control (INRIA).
005: * Contact: sequoia@continuent.org
006: *
007: * Licensed under the Apache License, Version 2.0 (the "License");
008: * you may not use this file except in compliance with the License.
009: * You may obtain a copy of the License at
010: *
011: * http://www.apache.org/licenses/LICENSE-2.0
012: *
013: * Unless required by applicable law or agreed to in writing, software
014: * distributed under the License is distributed on an "AS IS" BASIS,
015: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016: * See the License for the specific language governing permissions and
017: * limitations under the License.
018: *
019: * Initial developer(s): Nicolas Modrzyk.
020: * Contributor(s): Mathieu Peltier.
021: */package org.continuent.sequoia.console.text;
022:
023: import java.io.FileInputStream;
024: import java.io.FileNotFoundException;
025: import java.io.IOException;
026: import java.io.InputStream;
027: import java.net.InetAddress;
028: import java.net.UnknownHostException;
029: import java.util.Properties;
030:
031: import org.apache.commons.cli.CommandLine;
032: import org.apache.commons.cli.CommandLineParser;
033: import org.apache.commons.cli.GnuParser;
034: import org.apache.commons.cli.HelpFormatter;
035: import org.apache.commons.cli.Option;
036: import org.apache.commons.cli.OptionGroup;
037: import org.apache.commons.cli.Options;
038: import org.apache.commons.cli.ParseException;
039: import org.continuent.sequoia.common.i18n.ConsoleTranslate;
040: import org.continuent.sequoia.common.jmx.JmxConstants;
041: import org.continuent.sequoia.common.util.Constants;
042: import org.continuent.sequoia.console.jmx.RmiJmxClient;
043: import org.continuent.sequoia.console.text.module.AbstractConsoleModule;
044:
045: /**
046: * This class defines a ConsoleLauncher
047: *
048: * @author <a href="mailto:Nicolas.Modrzyk@inria.fr">Nicolas Modrzyk </a>
049: * @author <a href="mailto:mathieu.peltier@inrialpes.fr">Mathieu Peltier </a>
050: * @version 1.0
051: */
052: public class ConsoleLauncher {
053: /**
054: * Product name to be displayed at console startup
055: */
056: public static final String PRODUCT_NAME;
057:
058: static {
059: Properties props = new Properties();
060: try {
061: String propertiesFile = System
062: .getProperty(
063: "console.commands", //$NON-NLS-1$
064: AbstractConsoleModule.DEFAULT_COMMAND_PROPERTIES_FILE);
065: props.load(ClassLoader
066: .getSystemResourceAsStream(propertiesFile));
067: PRODUCT_NAME = props.getProperty("product.name"); //$NON-NLS-1$
068: } catch (IOException e) {
069: throw new ExceptionInInitializerError(e.getMessage());
070: }
071: }
072:
073: /**
074: * Launchs the Sequoia console. The available options are: <il>
075: * <li><code>-d</code> or <code>--debug</code>: show stack trace when
076: * error occurs.</li>
077: * <li><code>-f</code> or <code>--file</code>: use a given file as the
078: * source of commands instead of reading commands interactively.</li>
079: * <li><code>-h</code> or <code>--help</code>: displays usage
080: * information.</li>
081: * <li><code>-i</code> or <code>--ip</code>: IP address of the host name
082: * where the JMX Server hosting the controller is running (the default is
083: * '0.0.0.0').</li>
084: * <li><code>-n</code> or <code>--nocolor</code>: do not print colors in
085: * interactive mode for supported systems.</li>
086: * <li><code>-e</code> or <code>--exitonerror</code>: exit on error in
087: * non interactive mode.</li>
088: * <li><code>-p</code> or <code>--port</code>: JMX/RMI Port number of
089: * (the default is
090: * {@link org.continuent.sequoia.common.jmx.JmxConstants#DEFAULT_JMX_RMI_PORT}).
091: * </li>
092: * <li><code>-s</code> or <code>--secret</code>: password for JMX
093: * connection.</li>
094: * <li><code>-u</code> or <code>--username</code>: username for JMX
095: * connection.</li>
096: * <li><code>-m</code> or <code>--multiline</code>: enable multiline
097: * statements in the SQL console (disabled by default for backwards
098: * compatibility)</li>
099: * <li><code>-r</code> or <code>--requestdelimiter</code>: Request
100: * delimiter to use when multiline statement is enabled (<code>;</code> by
101: * default) connection.</li>
102: * <li><code>-v</code> or <code>--version</code>: displays version
103: * information.</li>
104: * </ul>
105: *
106: * @param args command line arguments (see above)
107: * @throws Exception if fails
108: */
109: public static void main(String[] args) throws Exception {
110: // Create options object
111: Options options = createOptions();
112:
113: // Parse command line
114: CommandLineParser parser = new GnuParser();
115: CommandLine commandLine = null;
116: try {
117: commandLine = parser.parse(options, args);
118: } catch (ParseException e) {
119: System.err.println("Syntax error (" + e + ")");
120: printUsage(options);
121: System.exit(1);
122: }
123:
124: // Non-recognized options
125: int n = commandLine.getArgs().length;
126: for (int i = 0; i < n; i++) {
127: System.err.println("Syntax error (unrecognized option: "
128: + commandLine.getArgs()[i] + ")");
129: printUsage(options);
130: System.exit(1);
131: }
132:
133: // Handle --help option
134: if (commandLine.hasOption('h')) {
135: if (commandLine.getOptions().length > 1)
136: System.err.println("Syntax error");
137:
138: printUsage(options);
139: System.exit(1);
140: }
141:
142: // Handle --version option
143: if (commandLine.hasOption('v')) {
144: if (commandLine.getOptions().length > 1) {
145: System.err.println("Syntax error");
146: printUsage(options);
147: } else
148: System.out.println(ConsoleTranslate.get(
149: "console.version", new String[] { PRODUCT_NAME,
150: Constants.VERSION }));
151:
152: System.exit(1);
153: }
154:
155: startTextConsole(commandLine);
156: }
157:
158: /**
159: * Starts the text console with the given commandline
160: *
161: * @param commandLine parameters for the text console
162: * @throws Exception if fails
163: */
164: public static void startTextConsole(CommandLine commandLine)
165: throws Exception {
166: // check if we are in interactive mode, and if so, output no traces
167: boolean isInteractive = !commandLine.hasOption('f');
168:
169: // Handle --ip option
170: String ip;
171: try {
172: // localhost
173: ip = InetAddress.getByName(null).getHostName();
174: } catch (UnknownHostException e1) {
175: // not IPv6 compliant, but "null" is never unknown anyway
176: ip = "127.0.0.1";
177: }
178: if (commandLine.hasOption('i')) {
179: String tmp = commandLine.getOptionValue('i');
180: if (tmp != null) {
181: ip = tmp;
182: }
183: }
184:
185: boolean exitOnError = commandLine.hasOption('e');
186:
187: // Handle --debug option
188: boolean debug = commandLine.hasOption('d');
189: // Handle --silent option
190: boolean silent = commandLine.hasOption('l');
191:
192: // Launch the console (handle --file option)
193: Console console;
194: InputStream in = null;
195: if (commandLine.hasOption('f')) {
196: String filename = commandLine.getOptionValue('f');
197: if ("-".equals(filename)) {
198: in = System.in;
199: } else {
200: try {
201: in = new FileInputStream(filename);
202: } catch (FileNotFoundException e) {
203: System.err.println("Failed to open file '"
204: + filename + "' (" + e + ")");
205: System.exit(1);
206: }
207: }
208: } else {
209: System.out.println(ConsoleTranslate.get(
210: "console.interactive.mode", PRODUCT_NAME)); //$NON-NLS-1$
211: in = System.in;
212: }
213:
214: RmiJmxClient jmxClient = null;
215: boolean sqlClientOnly = commandLine.hasOption('q');
216: if (!sqlClientOnly) {
217: jmxClient = getJmxClient(commandLine, ip, exitOnError);
218: }
219: console = new Console(jmxClient, in, isInteractive, debug,
220: silent, exitOnError, sqlClientOnly);
221: console.setPrintColor(!commandLine.hasOption('n'));
222: console.enableMultilineStatements(commandLine.hasOption('m'));
223: if (commandLine.hasOption('r')) {
224: console
225: .setRequestDelimiter(commandLine
226: .getOptionValue('r'));
227: }
228: console.handlePrompt();
229: System.exit(0);
230: }
231:
232: static RmiJmxClient getJmxClient(CommandLine commandLine,
233: String ip, boolean exitOnError) throws IOException {
234: // Handle --port option
235: int port;
236: if (commandLine.hasOption('p')) {
237: String s = commandLine.getOptionValue('p');
238: if (s == null) {
239: port = JmxConstants.DEFAULT_JMX_RMI_PORT;
240: } else
241: try {
242: port = Integer.parseInt(s);
243: } catch (NumberFormatException e) {
244: System.err.println("Bad port number (" + e
245: + "), using default "
246: + JmxConstants.DEFAULT_JMX_RMI_PORT
247: + " port number");
248: port = JmxConstants.DEFAULT_JMX_RMI_PORT;
249: }
250: } else {
251: port = JmxConstants.DEFAULT_JMX_RMI_PORT;
252: }
253: // Handle --secret and --username options
254: RmiJmxClient jmxClient = null;
255: if (commandLine.hasOption('u') && commandLine.hasOption('s')) {
256: String username = commandLine.getOptionValue('u');
257: String password = commandLine.getOptionValue('s');
258: jmxClient = new RmiJmxClient("" + port, ip, username,
259: password);
260: } else {
261: try {
262: jmxClient = new RmiJmxClient("" + port, ip, null);
263: } catch (Exception e) {
264: System.err
265: .println("Cannot connect to the administration port of the controller. Is a controller running at "
266: + ip + ":" + port + " ?");
267: // SEQUOIA-714: exit the console if the flag ''--exitonerror' is
268: // set
269: if (exitOnError) {
270: System.exit(1);
271: }
272: // SEQUOIA-708: let the console start if the user wants to use
273: // the SQL
274: // console.
275: }
276: }
277:
278: return jmxClient;
279: }
280:
281: /**
282: * Creates <code>Options</code> object that contains all available options
283: * that can be used launching Sequoia console.
284: *
285: * @return an <code>Options</code> instance
286: */
287: private static Options createOptions() {
288: Options options = new Options();
289: OptionGroup group = new OptionGroup();
290:
291: // help, verbose, text only console and file options (mutually exclusive
292: // options)
293: group.addOption(new Option("h", "help", false,
294: "Displays usage information."));
295: group.addOption(new Option("t", "text", false,
296: "Ignored - only for previous version compatibility."));
297: group.addOption(new Option("v", "version", false,
298: "Displays version information."));
299: group
300: .addOption(new Option(
301: "f",
302: "file",
303: true,
304: "Use a given file as the source of commands instead of reading commands interactively."));
305: options.addOptionGroup(group);
306:
307: /**
308: * Controller JMX ip option, defined in
309: * {@link org.continuent.sequoia.controller.core.ControllerConstants.DEFAULT_IP}
310: */
311: String defaultIp = "0.0.0.0";
312: // should probably better be: InetAddress.anyLocalAddress().getHostAddress()
313: options.addOption(new Option("i", "ip", true,
314: "The JMX server of the controller binds to this address (the default is '"
315: + defaultIp + "')."));
316:
317: // controller port option
318: options.addOption(new Option("p", "port", true,
319: "JMX/RMI port number of (the default is "
320: + JmxConstants.DEFAULT_JMX_RMI_PORT + ")."));
321:
322: // JMX options
323: options.addOption(new Option("u", "username", true,
324: "Username for JMX connection."));
325: options.addOption(new Option("s", "secret", true,
326: "Password for JMX connection."));
327:
328: options.addOption(new Option("d", "debug", false,
329: "Show stack trace when error occurs."));
330: options.addOption(new Option("l", "silent", false,
331: "Show only most meaningful messages."));
332:
333: options
334: .addOption(new Option("n", "nocolor", false,
335: "Do not print colors in interactive mode for supported systems."));
336:
337: options.addOption(new Option("e", "exitonerror", false,
338: "Stop on error in non interactive mode."));
339:
340: options
341: .addOption(new Option("r", "requestdelimiter", true,
342: "Request delimiter for multiline statements in the SQL console."));
343:
344: options.addOption(new Option("m", "multiline", false,
345: "Enable multiline statements in the SQL console."));
346:
347: options.addOption(new Option("q", "sqlclient", false,
348: "Launch SQL client console."));
349:
350: return options;
351: }
352:
353: /**
354: * Displays usage message.
355: *
356: * @param options available command line options
357: */
358: private static void printUsage(Options options) {
359: String header = ConsoleTranslate.get("console.launches",
360: PRODUCT_NAME)
361: + System.getProperty("line.separator") + "Options:";
362:
363: (new HelpFormatter()).printHelp(80,
364: "console(.sh|.bat) [options]", header, options, "");
365: }
366:
367: }
|