/*
* This is free software, licensed under the Gnu Public License (GPL) get a copy from <http://www.gnu.org/licenses/gpl.html> $Id:
* DriverCommand.java,v 1.15 2008-10-19 08:53:25 hzeller Exp $ author: Henner Zeller <H.Zeller@acm.org>
*/
package henplus.commands;
import henplus.AbstractCommand;
import henplus.CommandDispatcher;
import henplus.HenPlus;
import henplus.SQLSession;
import henplus.io.ConfigurationContainer;
import henplus.logging.Logger;
import henplus.view.Column;
import henplus.view.ColumnMetaData;
import henplus.view.TableRenderer;
import henplus.view.util.SortedMatchIterator;
import java.sql.Driver;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.SortedMap;
import java.util.StringTokenizer;
import java.util.TreeMap;
/**
* document me.
*/
public final class DriverCommand extends AbstractCommand {
private static final String[][] KNOWN_DRIVERS = {
{ "Oracle", "oracle.jdbc.driver.OracleDriver", "jdbc:oracle:thin:@localhost:1521:ORCL" },
{ "DB2", "COM.ibm.db2.jdbc.net.DB2Driver", "jdbc:db2://localhost:6789/foobar" },
{ "Postgres", "org.postgresql.Driver", "jdbc:postgresql://localhost/foobar" },
{ "SAP-DB", "com.sap.dbtech.jdbc.DriverSapDB", "jdbc:sapdb://localhost/foobar" },
{ "MySQL", "org.gjt.mm.mysql.Driver", "jdbc:mysql://localhost/foobar" },
{ "Adabas", "de.sag.jdbc.adabasd.ADriver", "jdbc:adabasd://localhost:7200/work" },
{ "Sybase", "com.internetcds.jdbc.tds.Driver", "jdbc:freetds:sybase://servername:port/database" },
{ "MSSQL", "com.internetcds.jdbc.tds.Driver", "jdbc:freetds:sqlserver://servername:port/database" },
{ "HSQLDB", "org.hsqldb.jdbcDriver", "jdbc:hsqldb:db_file_name_prefix" },
{ "SQLite", "org.sqlite.JDBC", "jdbc:sqlite://dirA/dirB/dbfile" } };
private static final String DRIVERS_FILENAME = "drivers";
private static final ColumnMetaData[] DRV_META = {
new ColumnMetaData("for"),
new ColumnMetaData("driver class"),
new ColumnMetaData("Version"),
new ColumnMetaData("sample url"),
};
private static final class DriverDescription {
private final String _className;
private final String _sampleURL;
private String _version; // known after loading.
private boolean _loaded;
public DriverDescription(final String cn, final String surl) {
_className = cn;
_sampleURL = surl;
load();
}
public String getClassName() {
return _className;
}
public String getSampleURL() {
return _sampleURL;
}
public String getVersion() {
return _version;
}
public boolean isLoaded() {
return _loaded;
}
public boolean load() {
try {
Logger.debug("loading .. '%s'", _className);
final Class<?> cls = Class.forName(_className);
Logger.debug(" done.");
try {
final Driver driver = (Driver) cls.newInstance();
_version = driver.getMajorVersion() + "." + driver.getMinorVersion();
} catch (final Throwable t) {
// ign.
}
_loaded = true;
} catch (final Throwable t) {
Logger.debug(" failed: ", t);
_loaded = false;
}
return _loaded;
}
}
private final SortedMap<String, DriverDescription> _drivers;
private final ConfigurationContainer _config;
/**
* returns the command-strings this command can handle.
*/
@Override
public String[] getCommandList() {
return new String[] { "list-drivers", "register", "unregister" };
}
public DriverCommand(final HenPlus henplus) {
_drivers = new TreeMap<String, DriverDescription>();
_config = henplus.createConfigurationContainer(DRIVERS_FILENAME);
final Map<String,String> props = _config.readProperties();
for (Entry<String,String> entry : props.entrySet()) {
final String name = entry.getKey();
final String value = entry.getValue();
if (name.startsWith("driver.") && name.endsWith(".class")) {
final String databaseName = name.substring("driver.".length(), name.length() - ".class".length());
final String exampleName = "driver." + databaseName + ".example";
DriverDescription desc;
desc = new DriverDescription(value, props.get(exampleName));
_drivers.put(databaseName, desc);
}
}
if (_drivers.size() == 0) {
for (int i = 0; i < KNOWN_DRIVERS.length; ++i) {
final String[] row = KNOWN_DRIVERS[i];
_drivers.put(row[0], new DriverDescription(row[1], row[2]));
}
}
}
@Override
public boolean requiresValidSession(final String cmd) {
return false;
}
/**
* execute the command given.
*/
@Override
public int execute(final SQLSession currentSession, final String cmd, final String param) {
final StringTokenizer st = new StringTokenizer(param);
final int argc = st.countTokens();
if ("list-drivers".equals(cmd)) {
if (argc == 0) {
HenPlus.msg().println("loaded drivers are marked with '*' (otherwise not found in CLASSPATH)");
DRV_META[0].resetWidth();
DRV_META[1].resetWidth();
DRV_META[2].resetWidth();
DRV_META[3].resetWidth();
final TableRenderer table = new TableRenderer(DRV_META, HenPlus.out());
for (Entry<String,DriverDescription> entry : _drivers.entrySet()) {
final Column[] row = new Column[4];
final DriverDescription desc = entry.getValue();
final String dbName = entry.getKey();
row[0] = new Column((desc.isLoaded() ? "* " : " ") + dbName);
row[1] = new Column(desc.getClassName());
row[2] = new Column(desc.getVersion());
row[3] = new Column(desc.getSampleURL());
table.addRow(row);
}
table.closeTable();
return SUCCESS;
} else {
return SYNTAX_ERROR;
}
} else if ("register".equals(cmd)) {
if (argc < 2 || argc > 3) {
return SYNTAX_ERROR;
}
final String shortname = (String) st.nextElement();
final String driverClass = (String) st.nextElement();
String sampleURL = null;
if (argc >= 3) {
sampleURL = (String) st.nextElement();
}
DriverDescription drv;
drv = new DriverDescription(driverClass, sampleURL);
if (!drv.isLoaded()) {
HenPlus.msg().println("cannot load driver class '" + driverClass + "'");
return EXEC_FAILED;
} else {
_drivers.put(shortname, drv);
}
} else if ("unregister".equals(cmd)) {
if (argc != 1) {
return SYNTAX_ERROR;
}
final String shortname = (String) st.nextElement();
if (!_drivers.containsKey(shortname)) {
HenPlus.msg().println("unknown driver for '" + shortname + "'");
return EXEC_FAILED;
} else {
_drivers.remove(shortname);
}
}
return SUCCESS;
}
@Override
public Iterator<String> complete(final CommandDispatcher disp, final String partialCommand, final String lastWord) {
final StringTokenizer st = new StringTokenizer(partialCommand);
final String cmd = (String) st.nextElement();
final int argc = st.countTokens();
// list-drivers gets no names.
if ("list-drivers".equals(cmd)) {
return null;
}
// do not complete beyond first word.
if (argc > ("".equals(lastWord) ? 0 : 1)) {
return null;
}
return new SortedMatchIterator(lastWord, _drivers);
}
@Override
public void shutdown() {
final Map<String,String> result = new HashMap<String,String>();
for (Map.Entry<String,DriverDescription> entry : _drivers.entrySet()) {
final String shortName = entry.getKey();
final DriverDescription desc = entry.getValue();
result.put("driver." + shortName + ".class", desc.getClassName());
result.put("driver." + shortName + ".example", desc.getSampleURL());
}
_config.storeProperties(result, true, "JDBC drivers");
}
/**
* return a descriptive string.
*/
@Override
public String getShortDescription() {
return "handle JDBC drivers";
}
@Override
public String getSynopsis(final String cmd) {
if ("unregister".equals(cmd)) {
return cmd + " <shortname>";
} else if ("register".equals(cmd)) {
return cmd + " <shortname> <driver-class> [sample-url]";
}
return cmd;
}
@Override
public String getLongDescription(final String cmd) {
String dsc = null;
if ("register".equals(cmd)) {
dsc = "\tRegister a new driver. Basically this adds the JDBC\n"
+ "\tdriver represented by its driver class. You have to give\n"
+ "\ta short name, that is used in the user interface. You\n"
+ "\tmight give a sample JDBC-URL that is shown with the\n"
+ "\tlist-drivers command. This command tries to load the\n"
+ "\tdriver from the CLASSPATH; if it is not found, then it\n"
+ "\tis not added to the list of usable drivers.";
} else if ("unregister".equals(cmd)) {
dsc = "\tUnregister the driver with the given shortname. There\n"
+ "\tis a command line completion for the shortname, but you\n"
+ "\tcan list them as well with the list-drivers command.";
} else if ("list-drivers".equals(cmd)) {
dsc = "\tList the drivers that are registered. The drivers, that\n"
+ "\tare actually loaded have a little star (*) in the first\n"
+ "\tcolumn. If it is not loaded, than you have to augment your\n"
+ "\tCLASSPATH in order to be able to connect to some database\n" + "\tof that kind.";
}
return dsc;
}
}
/*
* Local variables: c-basic-offset: 4 compile-command:
* "ant -emacs -find build.xml" End:
*/