001: package org.mandarax.jdbc.server;
002:
003: /*
004: * Copyright (C) 1999-2004 <a href="mailto:mandarax@jbdietrich.com">Jens Dietrich</a>
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation; either
009: * version 2 of the License, or (at your option) any later version.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: *
016: * You should have received a copy of the GNU Lesser General Public
017: * License along with this library; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
019: */
020:
021: import java.lang.reflect.*;
022: import java.sql.*;
023: import java.util.*;
024: import org.mandarax.jdbc.*;
025: import org.mandarax.jdbc.client.Record;
026: import org.mandarax.jdbc.rpc.*;
027: import org.mandarax.util.StringUtils;
028:
029: /**
030: * Default server facade implementation.
031: * @author <A HREF="mailto:mandarax@jbdietrich.com">Jens Dietrich</A>
032: * @version 3.3.2 <29 December 2004>
033: * @since 3.0
034: */
035:
036: public class DefaultServerFacade extends
037: org.mandarax.jdbc.server.ServerFacade {
038:
039: static Map methodsByName = new HashMap();
040: static {
041: try {
042: Class.forName("org.mandarax.jdbc.DriverImpl");
043: } catch (Exception x) {
044: LOG_JDBC.error("Cannot initialize local server driver", x);
045: }
046: try {
047: java.lang.reflect.Method[] methods = ServerFacade.class
048: .getMethods();
049: for (int i = 0; i < methods.length; i++) {
050: String name = methods[i].getName();
051: int modifier = methods[i].getModifiers();
052: if (!name.equals("perform")
053: && Modifier.isPublic(modifier))
054: methodsByName.put(methods[i].getName(), methods[i]);
055: }
056: } catch (Exception x) {
057:
058: }
059: }
060:
061: // simple datastructure to keep track of dependencies
062: // this is important in order to close resources recursively (garbage collection)
063: // dependencies are in particular: connection -> statements -> result sets
064: public class Dependency {
065: String parent, child = null;
066:
067: Dependency(String id1, String id2) {
068: super ();
069: this .parent = id1;
070: this .child = id2;
071: }
072: }
073:
074: private Map registry = new Hashtable();
075: private List dependencies = new ArrayList();
076:
077: /**
078: * Constructor.
079: */
080: public DefaultServerFacade() {
081: super ();
082: }
083:
084: /**
085: * Bind an object, and return the (generated) id.
086: * @param obj an object
087: * @param kindOfObject a string used to produce more readable ids, some implementation might ignore this
088: * @return the id of this object
089: */
090: private String bind(Object obj, String kindOfObject) {
091: String id = kindOfObject + (registry.size() + 1);
092: registry.put(id, obj);
093: return id;
094: }
095:
096: /**
097: * Lookup an object by id.
098: * @param id an id
099: * @return an object
100: */
101: private Object lookup(String id) {
102: return registry.get(id);
103: }
104:
105: /**
106: * Convert a db url to a local url.
107: * This implementation simply removes the NET token.
108: * @param a db url
109: * @return a local db url (to be used by the local driver)
110: */
111: public String convertToLocalUrl(String url) {
112: int pos = url.indexOf(JDBCUtils.NET);
113: if (pos == -1)
114: return url;
115: else
116: return url.substring(0, pos)
117: + url.substring(pos + JDBCUtils.NET.length());
118: }
119:
120: /**
121: * Create a connection.
122: * @param serverId .. currently ignored (but expected - do not remove)
123: * @param url the local database url
124: * @return a connection id.
125: */
126: public String CreateConnection(String serverId, String url)
127: throws Exception {
128: String localUrl = this .convertToLocalUrl(url);
129: Connection localConnection = DriverManager
130: .getConnection(localUrl);
131: return this .bind(localConnection, CONNECTION);
132: }
133:
134: /**
135: * Create a statement.
136: * @param connectionId a connection id
137: * @return a statement id.
138: */
139: public String Connection_createStatement(String connectionId)
140: throws Exception {
141: Connection localConnection = (Connection) lookup(connectionId);
142: Statement stmnt = localConnection.createStatement();
143: String id = this .bind(stmnt, STATEMENT);
144: this .addDependency(connectionId, id);
145: return id;
146: }
147:
148: /**
149: * Prepare a statement.
150: * @param connectionId a connection id
151: * @param sql a sql statement (with ? as parameter placeholders)
152: * @return a statement id.
153: */
154: public String Connection_prepareStatement(String connectionId,
155: String sql) throws Exception {
156: Connection localConnection = (Connection) lookup(connectionId);
157: PreparedStatement stmnt = localConnection.prepareStatement(sql);
158: String id = this .bind(stmnt, PREPARED_STATEMENT);
159: this .addDependency(connectionId, id);
160: return id;
161: }
162:
163: /**
164: * Perform a query. Only the result set id is returned,
165: * rows are returned using next(), and the result set is assembled
166: * on the client side.
167: * @param statementId a statement id
168: * @param sql an sql SELECT command
169: * @return a result set id.
170: */
171: public String Statement_executeQuery(String statementId, String sql)
172: throws Exception {
173: Statement stmnt = (Statement) lookup(statementId);
174: ResultSet rs = stmnt.executeQuery(sql);
175: String id = bind(rs, RESULT_SET);
176: addDependency(statementId, id);
177: return id;
178: }
179:
180: /**
181: * Get the current result set. Only the result set id is returned,
182: * rows are returned using next(), and the result set is assembled
183: * on the client side.
184: * @param statementId a statement id
185: * @return a result set id.
186: */
187: public String Statement_getResultSet(String statementId)
188: throws Exception {
189: Statement stmnt = (Statement) lookup(statementId);
190: ResultSet rs = stmnt.getResultSet();
191: String id = bind(rs, RESULT_SET);
192: addDependency(statementId, id);
193: return id;
194: }
195:
196: /**
197: * Perform a query. Only the result set id is returned,
198: * rows are returned using next(), and the result set is assembled
199: * on the client side.
200: * @param statementId a statement id
201: * @return a result set id.
202: */
203: public String PreparedStatement_executeQuery(String statementId)
204: throws Exception {
205: PreparedStatement stmnt = (PreparedStatement) lookup(statementId);
206: ResultSet rs = stmnt.executeQuery();
207: String id = bind(rs, RESULT_SET);
208: addDependency(statementId, id);
209: return id;
210: }
211:
212: /**
213: * Perform a query.
214: * @param statementId a statement id
215: */
216: public void Statement_execute(String statementId) throws Exception {
217: PreparedStatement stmnt = (PreparedStatement) lookup(statementId);
218: stmnt.execute();
219: }
220:
221: /**
222: * Set the object on a certain index.
223: * @param statementId a statement id
224: * @param parameterIndex the index
225: * @param x the object
226: */
227: public void PreparedStatement_setObject(String statementId,
228: int parameterIndex, Object x) throws SQLException {
229: PreparedStatement stmnt = (PreparedStatement) lookup(statementId);
230: stmnt.setObject(parameterIndex, x);
231: }
232:
233: /**
234: * Clear the parameters.
235: * @param statementId a statement id
236: * @return null (no return required)
237: */
238: public void PreparedStatement_clearParameters(String statementId)
239: throws Exception {
240: PreparedStatement stmnt = (PreparedStatement) lookup(statementId);
241: stmnt.clearParameters();
242: }
243:
244: /**
245: * Get the column info. Only the result set id is returned,
246: * rows are returned using next(), and the result set is assembled
247: * on the client side.
248: * @param connectionId a connection id
249: * @param sql an sql SELECT command
250: * @param catalog
251: * @param schemaPattern
252: * @param tableNamePattern
253: * @param columnNamePattern
254: * @return a result set id.
255: */
256: public String Connection_getMetaData_getColumns(
257: String connectionId, String catalog, String schemaPattern,
258: String tableNamePattern, String columnNamePattern)
259: throws Exception {
260: Connection localConnection = (Connection) lookup(connectionId);
261: ResultSet rs = localConnection.getMetaData().getColumns(
262: catalog, schemaPattern, tableNamePattern,
263: columnNamePattern);
264: String id = bind(rs, RESULT_SET);
265: this .addDependency(connectionId, id);
266: return id;
267: }
268:
269: /**
270: * Retrieves a description of the tables available in the given catalog.
271: * Only the result set id is returned, rows are returned using next(), and
272: * the result set is assembled on the client side.
273: * @param connectionId a connection id
274: * @param sql an sql SELECT command
275: * @param catalog
276: * @param schemaPattern
277: * @param tableNamePattern
278: * @param types
279: * @return a result set id.
280: */
281: public String Connection_getMetaData_getTables(String connectionId,
282: String catalog, String schemaPattern,
283: String tableNamePattern, String[] types) throws Exception {
284: Connection localConnection = (Connection) lookup(connectionId);
285: ResultSet rs = localConnection.getMetaData().getTables(catalog,
286: schemaPattern, tableNamePattern, types);
287: String id = bind(rs, RESULT_SET);
288: this .addDependency(connectionId, id);
289: return id;
290: }
291:
292: /**
293: * Return the result set meta data.
294: * @param resultsetId a result set id
295: * @return result set meta data
296: */
297: public ResultSetMetaData ResultSet_getMetaData(String resultsetId)
298: throws Exception {
299: ResultSet rs = (ResultSet) lookup(resultsetId);
300: return new org.mandarax.jdbc.client.ResultSetMetaDataImpl(rs
301: .getMetaData());
302: }
303:
304: /**
305: * Fetch the next row from a result set.
306: * @param id the result set id
307: * @return a record or null indicating that there is no (more) row
308: */
309: public Record ResultSet_next(String id) throws Exception {
310: ResultSet rs = (ResultSet) this .lookup(id);
311: if (rs.next())
312: return new Record(rs);
313: else
314: return null;
315: }
316:
317: /**
318: * Fetch the next rows from a result set.
319: * This is usually more effective than fetching a single row (but can also
320: * be dangerous, e.g. in case the computation of the second result is slow).
321: * @param id the result set id
322: * @param rows the number of rows
323: * @return a list of records
324: */
325: public List ResultSet_nextRows(String id, int rows)
326: throws Exception {
327: List records = new ArrayList(rows);
328: ResultSet rs = (ResultSet) this .lookup(id);
329: int counter = 0;
330: while (counter < rows && rs.next()) {
331: records.add(new Record(rs));
332: counter++;
333: }
334: return records;
335: }
336:
337: /**
338: * Close an object (connection, statement or result set).
339: * @param id the id of the object to be closed
340: */
341: public void Close(String id) throws Exception {
342:
343: //System.out.println("Closing object " + id);
344: try {
345: Object obj = this .lookup(id);
346: if (LOG_JDBC.isDebugEnabled())
347: LOG_JDBC.debug("Closing object " + id + " : " + obj);
348: if (obj != null) {
349: if (obj instanceof ResultSet) {
350: try {
351: ((ResultSet) obj).close();
352: } catch (SQLException x) {
353: LOG_JDBC
354: .error("Error closing object " + obj, x);
355: }
356: } else if (obj instanceof Connection) {
357: try {
358: ((Connection) obj).close();
359: } catch (SQLException x) {
360: LOG_JDBC
361: .error("Error closing object " + obj, x);
362: }
363: } else if (obj instanceof Statement) {
364: try {
365: ((Statement) obj).close();
366: } catch (SQLException x) {
367: LOG_JDBC
368: .error("Error closing object " + obj, x);
369: }
370: }
371: List dependents = this .removeDependencies(id);
372: registry.remove(id);
373: for (int i = 0; i < dependents.size(); i++) {
374: Close((String) dependents.get(i));
375: }
376: }
377: } catch (Exception x) {
378: registry.remove(id);
379: }
380: }
381:
382: /**
383: * Add a dependency.
384: * @param id1 the first id
385: * @param id2 the second id
386: */
387: private void addDependency(String id1, String id2) {
388: dependencies.add(new Dependency(id1, id2));
389: }
390:
391: /**
392: * Remove all dependents for the given id recursively.
393: * @param id an object id
394: * @return a list of ids found as values in dependencies where the given id was the key ("children").
395: */
396: private List removeDependencies(String id) {
397: List removed = new ArrayList();
398: for (Iterator iter = dependencies.iterator(); iter.hasNext();) {
399: Dependency dep = (Dependency) iter.next();
400: if (dep.parent.equals(id)) {
401: iter.remove();
402: removed.add(dep.child);
403: }
404: if (dep.child.equals(id)) {
405: iter.remove();
406: }
407: }
408: return removed;
409: }
410:
411: /**
412: * Invoke a call. No exception is thrown. Instead, all exception are
413: * caught and an ExceptionResult is returned.
414: * @param call a call
415: * @return a value
416: */
417: public CallResult perform(Call call) {
418:
419: try {
420: String id = call.getRef();
421: String method = call.getMethod();
422: Object[] param = call.getParam();
423: Object[] args = new Object[param.length + 1];
424: args[0] = id;
425: System.arraycopy(param, 0, args, 1, param.length);
426:
427: if (LOG_JDBC.isDebugEnabled()) {
428: LOG_JDBC.debug("Perform call " + method + " on object "
429: + id + " with parameters "
430: + StringUtils.toString(args));
431: }
432:
433: Method m = (Method) methodsByName.get(method);
434: if (m == null)
435: return new ExceptionResult(
436: "No such public method in ServerFacade: "
437: + method);
438: else {
439: Object obj = m.invoke(this , args);
440: return new ReturnValue(obj);
441: }
442: } catch (Throwable x) {
443: return new ExceptionResult(x);
444: }
445:
446: }
447:
448: /**
449: * Release all resources.
450: */
451: public void release() throws Exception {
452: while (!registry.isEmpty()) {
453: Iterator iter = registry.keySet().iterator();
454: String id = (String) iter.next();
455: Close(id);
456: }
457: }
458:
459: }
|