1 package com.quantum.sql;
3 import java.io.ByteArrayOutputStream;
5 import java.io.InputStream;
7 import java.io.UnsupportedEncodingException;
8 import java.net.MalformedURLException;
10 import java.net.URLClassLoader;
11 import java.sql.Connection;
12 import java.sql.DatabaseMetaData;
13 import java.sql.Driver;
14 import java.sql.ResultSet;
15 import java.sql.ResultSetMetaData;
16 import java.sql.SQLException;
17 import java.sql.Statement;
18 import java.util.Hashtable;
19 import java.util.Properties;
20 import java.util.Vector;
22 import com.quantum.model.Bookmark;
23 import com.quantum.model.ConnectionException;
24 import com.quantum.model.Entity;
25 import com.quantum.model.PasswordFinder;
26 import com.quantum.sql.metadata.MetaDataJDBCInterface;
27 import com.quantum.sql.metadata.ObjectMetaData;
28 import com.quantum.view.LogProxy;
29 import com.quantum.view.bookmark.EntityNode;
30 import com.quantum.view.bookmark.TreeNode;
34 * MultiSQLServer is a Singleton, used as a interface with the sql drivers.
35 * Use MultiSQLServer.getInstance() to get the object.
37 public class MultiSQLServer implements ConnectionEstablisher {
38 private static final int STREAM = 1024 * 2;
39 public static final String USERNAME = "user"; //$NON-NLS-1$
40 public static final String PASSWORD = "password"; //$NON-NLS-1$
41 private static MultiSQLServer instance = null;
42 private Hashtable classLoaderCache = new Hashtable();
43 boolean running = true;
45 public MultiSQLServer() {
48 public synchronized static MultiSQLServer getInstance() {
49 if (instance == null) {
50 instance = new MultiSQLServer();
55 public void commit(Connection con) {
56 LogProxy log = LogProxy.getInstance();
59 } catch (SQLException e) {
60 log.addText(LogProxy.ERROR, "Error commiting: " + e, e); //$NON-NLS-1$
64 public void rollback(Connection con) {
65 LogProxy log = LogProxy.getInstance();
68 } catch (SQLException e) {
69 log.addText(LogProxy.ERROR, "Error rolling back: " + e, e); //$NON-NLS-1$
73 public void setAutoCommit(Connection con, boolean enabled) {
74 LogProxy log = LogProxy.getInstance();
77 con.setAutoCommit(enabled);
79 log.addText(LogProxy.ERROR, "Please connect before setting autocommit"); //$NON-NLS-1$
81 } catch (SQLException e) {
82 log.addText(LogProxy.ERROR, "Error setting autocommit: " + e, e); //$NON-NLS-1$
86 public void disconnect(Connection connection) throws ConnectionException {
88 if (connection != null) {
91 } catch (SQLException e) {
92 throw new ConnectionException(e);
96 public Vector getSchemas(Connection con) {
98 Vector schemaList = new Vector();
100 DatabaseMetaData meta = con.getMetaData();
101 set = meta.getSchemas();
103 schemaList.add(set.getString("TABLE_SCHEM")); //$NON-NLS-1$
106 } catch (SQLException e) {
107 LogProxy log = LogProxy.getInstance();
108 log.addText(LogProxy.ERROR, e);
113 * Makes a connection to a JDBC driver based on the data from a bookmark
115 * The Bookmark with the data needed to make the connection
116 * @param passwordFinder -
117 * A utility class that can be invoked if the bookmark does not
119 * @return The Connection object if everything went OK
121 public Connection connect(Bookmark bookmark, PasswordFinder passwordFinder)
122 throws ConnectionException {
124 String password = bookmark.getPassword();
125 if (bookmark.getPromptForPassword()) {
126 password = passwordFinder.getPassword();
127 if (passwordFinder.isPasswordMeantToBeSaved()) {
128 bookmark.setPassword(password);
132 if (password != null) {
133 con = connect(bookmark, password);
137 // Set the autoCommit state of the bookmark to the default on new connections
138 bookmark.setAutoCommit(bookmark.getDefaultAutoCommit());
139 // Set the autoCommit state of the JDBC connection to the bookmark autoCommit statec
140 setAutoCommit(con, bookmark.isAutoCommit());
144 private Connection connect(Bookmark bookmark, String password)
145 throws ConnectionException {
146 LogProxy log = LogProxy.getInstance();
147 log.addText(LogProxy.QUERY, "Connecting to: " + bookmark.getName()); //$NON-NLS-1$
148 URL urls[] = new URL[1];
150 String driverFile = bookmark.getDriverFile();
151 URLClassLoader loader =
152 (URLClassLoader) classLoaderCache.get(driverFile);
153 if (loader == null) {
154 urls[0] = new File(driverFile).toURL();
155 loader = new URLClassLoader(urls);
156 classLoaderCache.put(driverFile, loader);
157 System.out.println("Creating new classloader"); //$NON-NLS-1$
159 System.out.println("Using classloader in cache"); //$NON-NLS-1$
161 Class driverClass = loader.loadClass(bookmark.getDriver());
162 Driver driver = (Driver) driverClass.newInstance();
163 Properties props = new Properties();
164 props.put(USERNAME, bookmark.getUsername());
165 props.put(PASSWORD, password);
166 Connection connection =
167 driver.connect(bookmark.getConnect(), props);
168 if (connection == null) {
169 throw new ConnectionException("Error: Driver returned a null connection: " + bookmark.toString()); //$NON-NLS-1$
171 log.addText(LogProxy.RESULTS, "Connected to: " + bookmark.getName()); //$NON-NLS-1$
172 System.out.println("Connected"); //$NON-NLS-1$
174 } catch (SQLException e) {
175 throw new ConnectionException(e);
176 } catch (MalformedURLException e) {
177 throw new ConnectionException(e);
178 } catch (ClassNotFoundException e) {
179 throw new ConnectionException(e);
180 } catch (InstantiationException e) {
181 throw new ConnectionException(e);
182 } catch (IllegalAccessException e) {
183 throw new ConnectionException(e);
186 public SQLResults execute(Connection con, String s) throws SQLException {
187 return execute(con, s, -1, -1);
189 public SQLResults execute(Connection con, String s, int startRow, int endRow) throws SQLException {
190 return execute(con, s, -1, -1, Integer.MAX_VALUE);
193 public SQLResults execute(Connection con, String s, int startRow, int endRow, int maxLength) throws SQLException {
194 return execute(con, s, startRow, endRow, maxLength, ""); //$NON-NLS-1$
197 public SQLResults execute(
204 throws SQLException {
206 SQLResults results = new SQLResults();
208 System.out.println("Executing"); //$NON-NLS-1$
209 LogProxy log = LogProxy.getInstance();
210 log.addText(LogProxy.QUERY, "Executing Request [" + s + "]"); //$NON-NLS-1$ //$NON-NLS-2$
211 boolean metadata = false;
212 if (s.startsWith("METADATA")) { //$NON-NLS-1$
217 String table = s.substring(s.indexOf(':') + 1);
218 String query = "SELECT * FROM " + table + " WHERE (1 = 0)"; //$NON-NLS-1$ //$NON-NLS-2$
220 log.addText(LogProxy.QUERY, "Metadata Request [" + s + "]"); //$NON-NLS-1$ //$NON-NLS-2$
225 Statement stmt = con.createStatement();
226 boolean flag = stmt.execute(s);
227 results.setResultSet(flag);
229 genMetadataResultSet(results, stmt);
233 int updates = stmt.getUpdateCount();
234 results.setUpdateCount(updates);
235 log.addText(LogProxy.RESULTS, "Success: " + updates + " records updated"); //$NON-NLS-1$ //$NON-NLS-2$
238 ResultSet set = stmt.getResultSet();
239 ResultSetMetaData metaData = set.getMetaData();
240 int columnCount = metaData.getColumnCount();
241 Vector columnNames = new Vector();
242 for (int i = 1; i <= columnCount; i++) {
243 columnNames.addElement(metaData.getColumnName(i));
245 results.setColumnNames(columnNames);
246 Vector columnTypes = new Vector();
247 for (int i = 1; i <= columnCount; i++) {
248 columnTypes.addElement(metaData.getColumnTypeName(i));
250 results.setColumnTypes(columnTypes);
251 int columnSizes[] = new int[columnCount];
252 for (int i = 1; i <= columnCount; i++) {
253 columnSizes[i - 1] = metaData.getColumnDisplaySize(i);
256 boolean exitEarly = false;
258 boolean disable = startRow < 1 || endRow < 1;
259 boolean start = rowCount >= startRow;
260 boolean end = rowCount <= endRow;
261 if (disable || (start && end)) {
262 Vector row = new Vector();
263 for (int i = 1; i <= columnCount; i++) {
265 if (columnSizes[i - 1] < STREAM
266 && columnSizes[i - 1] < maxLength) {
267 if (encoding.equals("")) { //$NON-NLS-1$
268 value = set.getString(i);
272 new String(set.getBytes(i), encoding);
273 } catch (UnsupportedEncodingException e) {
274 log.addText(LogProxy.ERROR, "Error Unsupported encoding " + encoding.toString() + ":" + e.toString(), e); //$NON-NLS-1$ //$NON-NLS-2$
275 value = new String(set.getBytes(i));
280 if (encoding.equals("")) { //$NON-NLS-1$
281 Reader reader = set.getCharacterStream(i);
282 StringBuffer buffer = new StringBuffer();
283 if (reader != null) {
284 int retVal = reader.read();
286 while (retVal >= 0) {
287 buffer.append((char) retVal);
288 retVal = reader.read();
290 if (count > maxLength) {
291 buffer.append("...>>>"); //$NON-NLS-1$
297 value = buffer.toString();
299 InputStream binaryStream =
300 set.getBinaryStream(i);
301 ByteArrayOutputStream baos =
302 new ByteArrayOutputStream();
303 if (binaryStream != null) {
304 int retVal = binaryStream.read();
306 while (retVal >= 0) {
308 retVal = binaryStream.read();
310 if (count > maxLength) {
314 binaryStream.close();
321 } catch (Throwable e) {
322 // hack for mysql which doesn't implement
324 value = set.getString(i);
328 row.addElement("<NULL>"); //$NON-NLS-1$
330 row.addElement(value);
336 if (!disable && (rowCount > endRow)) {
342 results.setHasMore(set.next());
344 results.setMaxSize(rowCount);
345 results.setHasMore(false);
349 log.addText(LogProxy.RESULTS, "Success: result set displayed"); //$NON-NLS-1$
351 System.out.println("Executed"); //$NON-NLS-1$
352 System.out.println();
355 private void genMetadataResultSet(SQLResults results, Statement stmt)
356 throws SQLException {
357 ResultSet set = stmt.getResultSet();
358 ResultSetMetaData metaData = set.getMetaData();
359 int columnCount = metaData.getColumnCount();
360 Vector columnNames = new Vector();
361 columnNames.addElement("ColumnName"); //$NON-NLS-1$
362 columnNames.addElement("Type"); //$NON-NLS-1$
363 columnNames.addElement("Size"); //$NON-NLS-1$
364 columnNames.addElement("Nullable"); //$NON-NLS-1$
365 columnNames.addElement("AutoIncrement"); //$NON-NLS-1$
366 results.setColumnNames(columnNames);
367 for (int i = 1; i <= columnCount; i++) {
368 Vector row = new Vector();
369 row.addElement(metaData.getColumnName(i));
370 row.addElement(metaData.getColumnTypeName(i));
371 int textSize = metaData.getColumnDisplaySize(i);
372 int precision = metaData.getPrecision(i);
373 int scale = metaData.getScale(i);
374 if (scale == 0 && precision == 0) {
375 row.addElement(Integer.toString(precision));
377 row.addElement(textSize + ", " + precision + ", " + scale); //$NON-NLS-1$ //$NON-NLS-2$
379 int nullable = metaData.isNullable(i);
380 if (nullable == ResultSetMetaData.columnNoNulls) {
381 row.addElement("Not Null"); //$NON-NLS-1$
382 } else if (nullable == ResultSetMetaData.columnNullable) {
383 row.addElement("Nullable"); //$NON-NLS-1$
385 nullable == ResultSetMetaData.columnNullableUnknown) {
386 row.addElement("Nullable"); //$NON-NLS-1$
388 row.addElement("<Error>"); //$NON-NLS-1$
391 (metaData.isAutoIncrement(i)
397 results.setHasMore(false);
402 * Returns an ObjectMetadata object got from the connection 'con' using the name and schema of the node.
406 * @throws SQLException
408 public ObjectMetaData getObjectMetadata(Connection con, TreeNode node) throws SQLException {
409 ObjectMetaData metadata = new ObjectMetaData();
410 if (!(node instanceof Entity)) return metadata;
412 String schema = ((Entity)node).getSchema();
413 String tableName = node.getName();
415 if (schema.length() == 0) schema = null;
416 metadata.setColumns(MetaDataJDBCInterface.getColumns(con, schema, tableName));
417 if (node instanceof EntityNode && ((EntityNode) node).isTable()) {
418 metadata.setPrimaryKeys(MetaDataJDBCInterface.getPrimaryKeys(con, schema, tableName));
419 metadata.setForeignKeys(MetaDataJDBCInterface.getForeignKeys(con, schema, tableName, true));
420 metadata.setIndexInfo(MetaDataJDBCInterface.getIndexInfo(con, schema, tableName));
421 metadata.setBestRowId(MetaDataJDBCInterface.getBestRowId(con, schema, tableName));