001: /*
002: * This is free software, licensed under the Gnu Public License (GPL)
003: * get a copy from <http://www.gnu.org/licenses/gpl.html>
004: *
005: * author: Henner Zeller <H.Zeller@acm.org>
006: */
007: package henplus.commands;
008:
009: import henplus.AbstractCommand;
010: import henplus.CommandDispatcher;
011: import henplus.HenPlus;
012: import henplus.SQLSession;
013: import henplus.SessionManager;
014: import henplus.io.ConfigurationContainer;
015: import henplus.view.Column;
016: import henplus.view.ColumnMetaData;
017: import henplus.view.TableRenderer;
018: import henplus.view.util.SortedMatchIterator;
019:
020: import java.io.BufferedReader;
021: import java.io.IOException;
022: import java.io.InputStream;
023: import java.sql.SQLException;
024: import java.io.InputStreamReader;
025: import java.io.OutputStream;
026: import java.io.OutputStreamWriter;
027: import java.io.PrintWriter;
028: import java.util.Iterator;
029: import java.util.Map;
030: import org.apache.commons.cli.CommandLine;
031: import org.apache.commons.cli.Option;
032: import org.apache.commons.cli.Options;
033:
034: import java.util.SortedMap;
035: import java.util.StringTokenizer;
036: import java.util.TreeMap;
037:
038: /**
039: * document me.
040: */
041: public class ConnectCommand extends AbstractCommand {
042:
043: private static String CONNECTION_CONFIG = "connections";
044: private final ConfigurationContainer _config;
045: private final SessionManager _sessionManager;
046: private final SortedMap _knownUrls;
047: private final HenPlus _henplus;
048:
049: private final static ColumnMetaData[] SESS_META;
050:
051: static {
052: SESS_META = new ColumnMetaData[5];
053: SESS_META[0] = new ColumnMetaData("session");
054: SESS_META[1] = new ColumnMetaData("user");
055: SESS_META[2] = new ColumnMetaData("jdbc url");
056: SESS_META[3] = new ColumnMetaData("uptime");
057:
058: SESS_META[4] = new ColumnMetaData("#stmts",
059: ColumnMetaData.ALIGN_RIGHT);
060: }
061:
062: /**
063: * the current session we are in.
064: */
065: private String currentSessionName = null;
066:
067: /**
068: * returns the command-strings this command can handle.
069: */
070: public String[] getCommandList() {
071: return new String[] { "connect", "disconnect",
072: "rename-session", "switch", "sessions" };
073: }
074:
075: public ConnectCommand(HenPlus henplus, SessionManager sessionManager) {
076: _henplus = henplus;
077: _sessionManager = sessionManager;
078: _knownUrls = new TreeMap();
079: _config = henplus
080: .createConfigurationContainer(CONNECTION_CONFIG);
081: _config.read(new ConfigurationContainer.ReadAction() {
082: public void readConfiguration(InputStream inStream)
083: throws Exception {
084: if (inStream == null)
085: return;
086: BufferedReader in = new BufferedReader(
087: new InputStreamReader(inStream, "UTF-8"));
088: String urlLine;
089: while ((urlLine = in.readLine()) != null) {
090: StringTokenizer tok = new StringTokenizer(urlLine);
091: String url;
092: String alias;
093: int tokNum = tok.countTokens();
094: if (tokNum == 1) {
095: url = tok.nextToken();
096: alias = url;
097: } else if (tokNum == 2) {
098: url = tok.nextToken();
099: alias = tok.nextToken();
100: } else {
101: continue;
102: }
103: _knownUrls.put(alias, url);
104: }
105: }
106: });
107: }
108:
109: public void handleCommandline(CommandLine line) {
110: String url = null;
111: String password = null;
112: String username = null;
113: String[] argv = line.getArgs();
114: if (argv.length > 0) {
115:
116: url = argv[0];
117: username = (argv.length > 1) ? argv[1] : null;
118: password = (argv.length > 2) ? argv[2] : null;
119:
120: }
121: if (line.hasOption("U")) {
122: username = line.getOptionValue("U");
123: }
124: if (line.hasOption("P")) {
125: password = line.getOptionValue("P");
126: }
127: if (line.hasOption("J")) {
128: url = line.getOptionValue("J");
129: }
130: if (url != null) {
131: try {
132: connect(url, username, password);
133: } catch (Exception e) {
134: //e.printStackTrace();
135: HenPlus.msg().println(e.getMessage());
136: }
137: }
138:
139: }
140:
141: /**
142: * @param url
143: * @param username
144: * @param password
145: * @throws ClassNotFoundException
146: * @throws SQLException
147: * @throws IOException
148: */
149: private void connect(String url, String username, String password)
150: throws ClassNotFoundException, SQLException, IOException {
151: SQLSession session;
152: session = new SQLSession(url, username, password);
153: currentSessionName = createSessionName(session, null);
154: _sessionManager.addSession(currentSessionName, session);
155: _knownUrls.put(url, url);
156: _henplus.setPrompt(currentSessionName + "> ");
157: _sessionManager.setCurrentSession(session);
158: }
159:
160: public void registerOptions(Options r) {
161: Option option = new Option("J", "url", true,
162: "JDBC URL to connect to");
163: option.setArgName("jdbc:...");
164: r.addOption(option);
165: Option option2 = new Option("U", "username", true,
166: "Username to connect with");
167: option2.setArgName("username");
168: r.addOption(option2);
169: Option option3 = new Option("P", "password", true,
170: "Password to connect with");
171: option3.setArgName("password");
172:
173: r.addOption(option3);
174: }
175:
176: /**
177: * create a session name from an URL.
178: */
179: private String createSessionName(SQLSession session, String name) {
180: String userName = null;
181: String dbName = null;
182: String hostname = null;
183: String url = session.getURL();
184:
185: if (name == null || name.length() == 0) {
186: StringBuffer result = new StringBuffer();
187: userName = session.getUsername();
188: StringTokenizer st = new StringTokenizer(url, ":");
189: while (st.hasMoreElements()) {
190: String val = (String) st.nextElement();
191: if (val.toUpperCase().equals("JDBC"))
192: continue;
193: dbName = val;
194: break;
195: }
196: int pos;
197: if ((pos = url.indexOf('@')) >= 0) {
198: st = new StringTokenizer(url.substring(pos + 1), ":/");
199: try {
200: hostname = (String) st.nextElement();
201: } catch (Exception e) { /* ignore */
202: }
203: } else if ((pos = url.indexOf('/')) >= 0) {
204: st = new StringTokenizer(url.substring(pos + 1), ":/");
205: while (st.hasMoreElements()) {
206: String val = (String) st.nextElement();
207: if (val.length() == 0)
208: continue;
209: hostname = val;
210: break;
211: }
212: }
213: if (userName != null)
214: result.append(userName + "@");
215: if (dbName != null)
216: result.append(dbName);
217: if (dbName != null && hostname != null)
218: result.append(":");
219: if (hostname != null)
220: result.append(hostname);
221: name = result.toString();
222: }
223: String key = name;
224: int count = 0;
225: while (_sessionManager.sessionNameExists(key)) {
226: ++count;
227: key = name + "#" + count;
228: }
229: return key;
230: }
231:
232: public void shutdown() {
233: _sessionManager.closeAll();
234:
235: _config.write(new ConfigurationContainer.WriteAction() {
236: public void writeConfiguration(OutputStream out)
237: throws Exception {
238: PrintWriter writer = new PrintWriter(
239: new OutputStreamWriter(out, "UTF-8"));
240: Iterator urlIter = _knownUrls.entrySet().iterator();
241: while (urlIter.hasNext()) {
242: Map.Entry entry = (Map.Entry) urlIter.next();
243: String alias = (String) entry.getKey();
244: String url = (String) entry.getValue();
245: if (alias.equals(url)) {
246: writer.println(url);
247: } else {
248: writer.print(url);
249: writer.print(" ");
250: writer.println(alias);
251: }
252: }
253: writer.close();
254: }
255: });
256: }
257:
258: /**
259: * we can connect, even if we don't have a running connection.
260: */
261: public boolean requiresValidSession(String cmd) {
262: if ("connect".equals(cmd)) {
263: return false;
264: }
265: return true;
266: }
267:
268: /**
269: * complete session names. But not the session we are currently in, since
270: * we don't want to switch to our own session, right ?
271: */
272: public Iterator complete(CommandDispatcher disp,
273: String partialCommand, final String lastWord) {
274: if (partialCommand.startsWith("connect")) {
275: if (argumentCount(partialCommand) > ("".equals(lastWord) ? 1
276: : 2)) {
277: return null;
278: }
279: return new SortedMatchIterator(lastWord, _knownUrls);
280: }
281:
282: else if (partialCommand.startsWith("switch")) {
283: if (argumentCount(partialCommand) > ("".equals(lastWord) ? 1
284: : 2)) {
285: return null;
286: }
287: return new SortedMatchIterator(lastWord, _sessionManager
288: .getSessionNames()) {
289: protected boolean exclude(String sessionName) {
290: return sessionName.equals(currentSessionName);
291: }
292: };
293: }
294: return null;
295: }
296:
297: /**
298: * execute the command given.
299: */
300: public int execute(SQLSession currentSession, String cmd,
301: String param) {
302: SQLSession session = null;
303:
304: StringTokenizer st = new StringTokenizer(param);
305: int argc = st.countTokens();
306:
307: if ("sessions".equals(cmd)) {
308: showSessions();
309: return SUCCESS;
310: }
311:
312: else if ("connect".equals(cmd)) {
313: if (argc < 1 || argc > 2) {
314: return SYNTAX_ERROR;
315: }
316: String url = (String) st.nextElement();
317: String alias = (argc == 2) ? st.nextToken() : null;
318: if (alias == null) {
319: /*
320: * we only got one parameter. So the that single parameter
321: * might have been an alias. let's see..
322: */
323: if (_knownUrls.containsKey(url)) {
324: String possibleAlias = url;
325: url = (String) _knownUrls.get(url);
326: if (!possibleAlias.equals(url)) {
327: alias = possibleAlias;
328: }
329: }
330: }
331: try {
332: session = new SQLSession(url, null, null);
333: _knownUrls.put(url, url);
334: if (alias != null) {
335: _knownUrls.put(alias, url);
336: }
337: currentSessionName = createSessionName(session, alias);
338: _sessionManager.addSession(currentSessionName, session);
339: _sessionManager.setCurrentSession(session);
340: } catch (Exception e) {
341: HenPlus.msg().println(e.toString());
342: return EXEC_FAILED;
343: }
344: }
345:
346: else if ("switch".equals(cmd)) {
347: String sessionName = null;
348: if (argc != 1 && _sessionManager.getSessionCount() != 2) {
349: return SYNTAX_ERROR;
350: }
351: if (argc == 0 && _sessionManager.getSessionCount() == 2) {
352: Iterator i = _sessionManager.getSessionNames()
353: .iterator();
354: while (i.hasNext()) {
355: sessionName = (String) i.next();
356: if (!sessionName.equals(currentSessionName)) {
357: break;
358: }
359: }
360: } else {
361: sessionName = (String) st.nextElement();
362: }
363: session = _sessionManager.getSessionByName(sessionName);
364: if (session == null) {
365: HenPlus.msg().println(
366: "'" + sessionName + "': no such session");
367: return EXEC_FAILED;
368: }
369: currentSessionName = sessionName;
370: }
371:
372: else if ("rename-session".equals(cmd)) {
373: String sessionName = null;
374: if (argc != 1) {
375: return SYNTAX_ERROR;
376: }
377: sessionName = (String) st.nextElement();
378: if (sessionName.length() < 1) {
379: return SYNTAX_ERROR;
380: }
381:
382: /* // moved to sessionmanager.renameSession
383: *
384: if (_sessionManager.sessionNameExists(sessionName)) {
385: HenPlus.err().println("A session with that name already exists");
386: return EXEC_FAILED;
387: }
388:
389: session = _sessionManager.removeSessionWithName(currentSessionName);
390: if (session == null) {
391: return EXEC_FAILED;
392: }
393: _sessionManager.addSession(sessionName, session);
394: */
395: int renamed = _sessionManager.renameSession(
396: currentSessionName, sessionName);
397: if (renamed == EXEC_FAILED)
398: return EXEC_FAILED;
399:
400: currentSessionName = sessionName;
401: session = _sessionManager.getCurrentSession();
402: }
403:
404: else if ("disconnect".equals(cmd)) {
405: currentSessionName = null;
406: if (argc != 0) {
407: return SYNTAX_ERROR;
408: }
409: _sessionManager.closeCurrentSession();
410: HenPlus.msg().println("session closed.");
411:
412: if (_sessionManager.hasSessions()) {
413: currentSessionName = _sessionManager
414: .getFirstSessionName();
415: session = _sessionManager
416: .getSessionByName(currentSessionName);
417: }
418: }
419:
420: if (currentSessionName != null) {
421: _henplus.setPrompt(currentSessionName + "> ");
422: } else {
423: _henplus.setDefaultPrompt();
424: }
425: _henplus.setCurrentSession(session);
426:
427: return SUCCESS;
428: }
429:
430: private void showSessions() {
431: HenPlus.msg().println("current session is marked with '*'");
432: for (int i = 0; i < SESS_META.length; ++i) {
433: SESS_META[i].resetWidth();
434: }
435: TableRenderer table = new TableRenderer(SESS_META, HenPlus
436: .out());
437: Iterator it = _sessionManager.getSessionNames().iterator();
438: while (it.hasNext()) {
439: String sessName = (String) it.next();
440: SQLSession session = _sessionManager
441: .getSessionByName(sessName);
442: String prepend = sessName.equals(currentSessionName) ? " * "
443: : " ";
444: Column[] row = new Column[5];
445: row[0] = new Column(prepend + sessName);
446: row[1] = new Column(session.getUsername());
447: row[2] = new Column(session.getURL());
448: row[3] = new Column(TimeRenderer.renderTime(session
449: .getUptime()));
450: row[4] = new Column(session.getStatementCount());
451: table.addRow(row);
452: }
453: table.closeTable();
454: }
455:
456: /**
457: * return a descriptive string.
458: */
459: public String getShortDescription() {
460: return "manage sessions";
461: }
462:
463: public String getSynopsis(String cmd) {
464: if ("connect".equals(cmd)) {
465: return cmd + " <jdbc-url> [session-name]";
466: } else if ("switch".equals(cmd)) {
467: return cmd + " <session-name>";
468: } else if ("rename-session".equals(cmd)) {
469: return cmd + " <new-session-name>";
470: }
471: return cmd; // disconnect
472: }
473:
474: public String getLongDescription(String cmd) {
475: String dsc = null;
476: if ("connect".equals(cmd)) {
477: dsc = "\tconnects to the url with the optional session name.\n"
478: + "\tIf no session name is given, a session name is chosen.\n"
479: + "\tIf a session name is given, this is stored as an alias\n"
480: + "\tfor the URL as well, so later you might connect with\n"
481: + "\tthat alias conveniently instead:\n"
482: + "\t\tconnect jdbc:oracle:thin:foo/bar@localhost:BLUE myAlias\n"
483: + "\tallows to later connect simply with\n"
484: + "\t\tconnect myAlias\n"
485: + "\tOf course, all URLs and aliases are stored in your \n"
486: + "\t~/.henplus configuration. All connects and aliases \n"
487: + "\tare provided in the TAB-completion of this command.";
488: } else if ("disconnect".equals(cmd)) {
489: dsc = "\tdisconnect current session. You can leave a session as\n"
490: + "\twell if you just type CTRL-D";
491: } else if ("switch".equals(cmd)) {
492: dsc = "\tswitch to session with the given session name.";
493: } else if ("sessions".equals(cmd)) {
494: dsc = "\tlist all active sessions.";
495: } else if ("rename-session".equals(cmd)) {
496: dsc = "\trename current session. This influences the prompt.";
497: }
498: return dsc;
499: }
500: }
501:
502: /*
503: * Local variables:
504: * c-basic-offset: 4
505: * compile-command: "ant -emacs -find build.xml"
506: * End:
507: */
|