001: // You can redistribute this software and/or modify it under the terms of
002: // the Ozone Library License version 1 published by ozone-db.org.
003: //
004: // The original code and portions created by SMB are
005: // Copyright (C) 1997-@year@ by SMB GmbH. All rights reserved.
006: //
007: // $Id: ExternalDatabase.java,v 1.5 2002/06/25 11:34:38 mediumnet Exp $
008:
009: package org.ozoneDB;
010:
011: import java.io.IOException;
012: import java.net.MalformedURLException;
013: import java.util.Hashtable;
014: import java.util.StringTokenizer;
015: import javax.transaction.xa.XAResource;
016:
017: import org.ozoneDB.DxLib.*;
018: import org.ozoneDB.core.DbRemote.*;
019: import org.ozoneDB.core.Transaction;
020: import org.ozoneDB.core.TransactionID;
021: import org.ozoneDB.core.admin.Admin;
022: import org.ozoneDB.core.admin.AdminImpl;
023: import org.ozoneDB.xa.OzoneXAResource;
024: import org.ozoneDB.xml.util.SAXChunkConsumer;
025: import org.w3c.dom.Document;
026: import org.w3c.dom.Node;
027: import org.xml.sax.ContentHandler;
028:
029: /**
030: * Base class for implementations of the OzoneInterface which are used from
031: * a client application to access an ozone.<p>
032: *
033: * Each thread is associated with a server connection. This connection is used
034: * if the thread is not associated with a transaction. Otherwise the connection
035: * of the transaction is used. So in case the thread has *joined* a transaction
036: * it does not use its own connection but the connection of the transaction.
037: * The prepare/commit/abort methods don't need to be called from the thread that
038: * is joined to the transaction.<p>
039: *
040: * Impl. note: The OzoneInterface methods don't need to be synchronized any
041: * longer because each threads has its own connection.<p>
042: *
043: * @author <a href="http://www.softwarebuero.de/">SMB</a>
044: * @author <a href="http://www.medium.net/">Medium.net</a>
045: * @version $Revision: 1.5 $Date: 2002/06/25 11:34:38 $
046: * @see OzoneInterface
047: */
048: public abstract class ExternalDatabase extends AbstractDatabase
049: implements OzoneInterface {
050:
051: // Constants
052:
053: public final static String PROP_HOST = "host";
054:
055: public final static String PROP_PORT = "port";
056:
057: public final static String PROP_DIR = "dir";
058:
059: public final static String PROP_USER = "user";
060:
061: public final static String PROP_PASSWD = "passwd";
062:
063: public final static String PROP_DEBUG = "debug";
064:
065: // Data
066:
067: /**
068: * The wrapper that uses this as its delegate or null if there is no such
069: * wrapper. Especially the linkForProxy() calls are redirected to the
070: * wrapper to have the proxies link to the wrapper instead of this database.
071: */
072: private ExternalDatabase wrapper;
073:
074: /**
075: * Table of all current external transactions. thread -> transaction
076: */
077: private DxMap txTable;
078:
079: /**
080: * Pool of currently available database connections.
081: */
082: private DxDeque apool;
083:
084: /**
085: * Pool of currently used database connections.
086: */
087: private DxSet upool;
088:
089: /**
090: * The XAResource of this database connection.
091: */
092: private XAResource xares;
093:
094: /**
095: * If this is non-null it is used by all threads as the current transaction.
096: */
097: // private AbstractTransaction globalTX;
098: /**
099: * All ExternalDatabase objects that have been created in the current VM.
100: * This is used by the forThread() method.
101: */
102: private static DxBag databases = new DxArrayBag();
103:
104: /**
105: * Static method to find a database connection that has an associated
106: * transaction that has been joined by the given thread. Used by proxy
107: * constructors to determine its database. This method returns the first
108: * database that was found.
109: *
110: *
111: * @param _thread The thread for which to find the corresponding database.
112: * @return The database for the given thread.
113: */
114: public static ExternalDatabase forThread(Thread _thread) {
115: DxIterator it = databases.iterator();
116: ExternalDatabase db;
117: while ((db = (ExternalDatabase) it.next()) != null) {
118: DxSet threads = db.txTable.keySet();
119: if (threads.contains(_thread)) {
120: return db;
121: }
122: }
123: return null;
124: }
125:
126: // Constructors ***************************************
127:
128: public ExternalDatabase() {
129: }
130:
131: // XAResource factory *********************************
132:
133: /**
134: * Return a new XAResource for this database connection.
135: * @return new XAResource.
136: */
137: public final XAResource getXAResource() {
138: if (xares == null) {
139: synchronized (this ) {
140: if (xares == null) {
141: xares = new OzoneXAResource(this );
142: }
143: }
144: }
145: return xares;
146: }
147:
148: // external transaction handling **********************
149:
150: protected final AbstractTransaction txForThread(Thread thread) {
151: return (AbstractTransaction) txTable.elementForKey(thread);
152: }
153:
154: /**
155: * Create a new transaction. Before using this transaction it must be started.
156: *
157: * @return The newly created transaction.
158: */
159: public ExternalTransaction newTransaction() {
160: return new ExternalTransaction(this );
161: }
162:
163: /**
164: * Obtain the {@link ExternalTransaction} that is assocaited to the
165: * caller's thread or null, if there is no such transaction. This does not
166: * necessarily mean that there is no transaction associated to this
167: * thread at all. For example there might be an XA transaction.
168: *
169: * @return The transaction that is associated to the caller's thread.
170: * @see #currentTransaction()
171: */
172: public ExternalTransaction currentExternalTransaction() {
173: AbstractTransaction atx = txForThread(Thread.currentThread());
174:
175: if (atx instanceof ExternalTransaction) {
176: return (ExternalTransaction) atx;
177: } else {
178: return null;
179: }
180: }
181:
182: /**
183: * Obtain the transaction that is assocaited to the caller's thread or null,
184: * if there is no such transaction. The transaction might be an instance
185: * of any subclass of AbstractTransaction, which includes also non-user
186: * transactions like {@link org.ozoneDB.xa.XATransaction}.
187: *
188: * @return The transaction that is associated to the caller's thread.
189: * @see #currentExternalTransaction()
190: */
191: public AbstractTransaction currentTransaction() {
192: return txForThread(Thread.currentThread());
193: }
194:
195: /**
196: * This method is never directly called from the client code.
197: */
198: public void beginTX(AbstractTransaction tx) throws TransactionExc,
199: IOException {
200:
201: // check if the transaction is already started
202: if (tx.connection != null) {
203: throw new TransactionExc("Transaction already started.",
204: TransactionExc.STATE);
205: }
206:
207: synchronized (tx) {
208: try {
209: tx.connection = acquirePooledConnection();
210: joinTX(tx);
211:
212: // exceptions are catched and re-thrown be sendCommand()
213: TransactionID taID = (TransactionID) sendCommand(
214: new DbTransaction(DbTransaction.MODE_BEGIN),
215: true, tx.connection);
216: } catch (IOException e) {
217: throw e;
218: } catch (Exception e) {
219: throw new TransactionExc(e.toString(),
220: TransactionExc.UNEXPECTED);
221: }
222: }
223: }
224:
225: /**
226: * This method is never directly called from the client code.
227: */
228: public void joinTX(AbstractTransaction tx) throws TransactionExc {
229: Thread thread = Thread.currentThread();
230:
231: synchronized (txTable) {
232: // check if the current thread is already joined to a transaction
233: AbstractTransaction txOfThread = (AbstractTransaction) txTable
234: .elementForKey(thread);
235: if (txOfThread != null && txOfThread != tx) {
236: throw new TransactionExc(
237: "Thread is already joined to a transaction.",
238: TransactionExc.STATE);
239: }
240: txTable.addForKey(tx, thread);
241: }
242: }
243:
244: /**
245: * This method is never directly called from the client code.
246: */
247: public boolean leaveTX(AbstractTransaction tx) {
248: return txTable.removeForKey(Thread.currentThread()) != null;
249: }
250:
251: /**
252: * This method is never directly called from the client code.
253: */
254: public void checkpointTX(AbstractTransaction tx)
255: throws TransactionExc, IOException {
256: commandTX(tx, new DbTransaction(DbTransaction.MODE_CHECKPOINT));
257: }
258:
259: /**
260: * This method is never directly called from the client code.
261: */
262: public void prepareTX(AbstractTransaction tx)
263: throws TransactionExc, IOException {
264: if (tx.connection == null) {
265: throw new TransactionExc("Illegal state.",
266: TransactionExc.STATE);
267: }
268: commandTX(tx, new DbTransaction(DbTransaction.MODE_PREPARE));
269: }
270:
271: /**
272: * This method is never directly called from the client code.
273: */
274: public void commitTX(AbstractTransaction tx, boolean onePhase)
275: throws TransactionExc, IOException {
276: synchronized (tx) {
277: if (tx.connection == null) {
278: throw new TransactionExc("Illegal state.",
279: TransactionExc.STATE);
280: }
281:
282: if (onePhase) {
283: commandTX(tx, new DbTransaction(
284: DbTransaction.MODE_COMMIT_ONEPHASE));
285: } else {
286: commandTX(tx, new DbTransaction(
287: DbTransaction.MODE_COMMIT_TWOPHASE));
288: }
289: releasePooledConnection(tx.connection);
290: tx.connection = null;
291: }
292:
293: synchronized (txTable) {
294: // disassociate all threads that have joined the transaction
295: DxIterator it = txTable.iterator();
296: AbstractTransaction cursorTX;
297: while ((cursorTX = (AbstractTransaction) it.next()) != null) {
298: if (cursorTX == tx) {
299: it.removeObject();
300: }
301: }
302: }
303: }
304:
305: /**
306: * This method is never directly called from the client code.
307: */
308: public void rollbackTX(AbstractTransaction tx)
309: throws TransactionExc, IOException {
310: synchronized (tx) {
311: if (tx.connection == null) {
312: // don't throw an exception to allow subsequent calls
313: return;
314: }
315: commandTX(tx, new DbTransaction(DbTransaction.MODE_ABORT));
316: releasePooledConnection(tx.connection);
317: tx.connection = null;
318: }
319:
320: synchronized (txTable) {
321: // disassociate all threads that have joined the transaction
322: DxIterator it = txTable.iterator();
323: AbstractTransaction cursorTX;
324: while ((cursorTX = (AbstractTransaction) it.next()) != null) {
325: if (cursorTX == tx) {
326: it.removeObject();
327: }
328: }
329: }
330: }
331:
332: /**
333: * Obtain the _internal_ server status of this transaction.
334: * @return Status of the transaction. Defined in
335: * {@link org.ozoneDB.core.Transaction}.<p>
336: *
337: * This method is never directly called from the client code.
338: */
339: public final int getStatusTX(AbstractTransaction tx)
340: throws TransactionExc, IOException {
341: if (tx.connection == null) {
342: return Transaction.STATUS_NONE;
343: } else {
344: DbTransaction command = new DbTransaction(
345: DbTransaction.MODE_STATUS);
346: commandTX(tx, command);
347: return ((Integer) command.result).intValue();
348: }
349: }
350:
351: /**
352: * This method is never directly called from the client code.
353: */
354: protected final Object commandTX(AbstractTransaction tx,
355: DbTransaction command) throws TransactionExc, IOException {
356: if (tx.connection == null) {
357: throw new TransactionExc(
358: "Thread has not yet joined a transaction.",
359: TransactionExc.STATE);
360: }
361:
362: try {
363: // exceptions are catched and re-thrown be sendCommand();
364: // use the connection of the transaction to allow non-joined
365: // transaction to complete the transaction
366: return sendCommand(command, true, tx.connection);
367: } catch (IOException e) {
368: throw e;
369: } catch (TransactionExc e) {
370: throw e;
371: } catch (Exception e) {
372: throw new TransactionExc(e.toString(),
373: TransactionExc.UNEXPECTED);
374: }
375: }
376:
377: // connection pooling *********************************
378:
379: /**
380: * The way for the actual database types to create a corresponding
381: * connection.
382: */
383: protected abstract DbClient newConnection() throws Exception;
384:
385: /**
386: * Get a connection from the connection pool.<p>
387: *
388: * Earlier versions kept one connection for each client thread. That is,
389: * client threads were directly mapped to server threads. Especially for
390: * servlet environment this produces a lot of connections (server threads),
391: * which in fact is not needed.<p>
392: *
393: * So we are using a pool of connections now. Connections are actually
394: * created and added to the pool when the pool is empty. Currently connections
395: * are never closed once they are added to the pool.
396: *
397: * @param DbClient A connection from the pool.
398: */
399: protected final DbClient acquirePooledConnection() throws Exception {
400: // keep both pools stable while we are messing with them
401: synchronized (apool) {
402: synchronized (upool) {
403: if (apool.isEmpty()) {
404: DbClient connection = newConnection();
405: apool.push(connection);
406: }
407: DbClient connection = (DbClient) apool.pop();
408: if (upool.add(connection) == false) {
409: throw new IllegalStateException(
410: "Connection is already in use.");
411: }
412: return connection;
413: }
414: }
415: }
416:
417: /**
418: * Release a formerly acquired pooled connection so that it may used by
419: * another request.
420: *
421: * @param connection The pooled connection to be released
422: * @see #acquirePooledConnection
423: */
424: protected final void releasePooledConnection(DbClient connection) {
425: // keep both pools stable while we are messing with it
426: synchronized (apool) {
427: synchronized (upool) {
428: if (upool.remove(connection) == false) {
429: throw new IllegalStateException(
430: "Given connection is not element of the pool of used connections.");
431: }
432: apool.push(connection);
433: }
434: }
435: }
436:
437: /**
438: * Return the server connection for the specified thread. If the thread is
439: * not yet joined to a connection create a new one and associate with the
440: * thread.
441: */
442: // protected final DbClient connectionForThread( Thread thread ) throws Exception {
443: // if (!isOpen()) {
444: // throw new RuntimeException( "Database not open" );
445: // }
446: //
447: // // is there a connection associated to this thread?
448: // DbClient connection = (DbClient)connectionTable.elementForKey( thread );
449: //
450: // if (connection == null) {
451: // synchronized (connectionTable) {
452: // // close all connections that are no longer used by a thread;
453: // // don't re-use connections because they are not really stateless
454: // DxIterator it = connectionTable.iterator();
455: // while (it.next() != null) {
456: // if (!((Thread)it.key()).isAlive()) {
457: // // System.out.println ("closing connection...");
458: // connection = (DbClient)it.removeObject();
459: // connection.send( new DbCloseConn() );
460: // connection.close();
461: // }
462: // }
463: // // make a new connection
464: // connection = newConnection();
465: // connectionTable.addForKey( connection, thread );
466: // }
467: // }
468: // return connection;
469: // }
470:
471: /**
472: * Send the specified command to the server. If there is a global
473: * transaction, its connection is used. Else, if the current thread is joined
474: * to a transaction the connection of the transaction is used. Otherwise a
475: * connection from the pool is used.
476: */
477: protected final Object sendCommand(DbCommand command,
478: boolean waitForResult) throws Exception {
479: if (!isOpen()) {
480: throw new IllegalStateException("Database is not open.");
481: }
482: Thread thread = Thread.currentThread();
483:
484: AbstractTransaction txOfThread = (AbstractTransaction) txTable
485: .elementForKey(thread);
486: if (txOfThread != null) {
487: DbClient connection = txOfThread.connection;
488: return sendCommand(command, waitForResult, connection);
489: } else {
490: DbClient connection = null;
491: try {
492: connection = acquirePooledConnection();
493: return sendCommand(command, waitForResult, connection);
494: } finally {
495: releasePooledConnection(connection);
496: }
497: }
498: }
499:
500: /**
501: * Send the specified command to the server. Use the specified connection.
502: * While working the connection is synchronized to allow multiple threads
503: * to use this connection.
504: *
505: * @param waitForResult
506: * true: read the result from the external database and return it
507: * false: do not read the result from the external database and return null.
508: * This is dangerous if not properly used. In this case, the result
509: * is not read from the stream and left to be read from the next reader.
510: * As this is not desireable, only supply false if the command does not return any result.
511: */
512: protected Object sendCommand(DbCommand command,
513: boolean waitForResult, DbClient connection)
514: throws Exception, ExceptionInOzoneObjectException {
515:
516: Object result = null;
517:
518: // this allows multiple thread to use one connection; this happens,
519: // if a thread joines a thransaction instead of beginning it
520: synchronized (connection) {
521: connection.send(command);
522: if (waitForResult) {
523: //read the result and set proxy links
524: result = connection.receive();
525:
526: if (result != null) {
527: // FIXME: It is not wise to overload the result with both a normal result and an exception.
528: if (result instanceof RuntimeException) {
529: if (result instanceof ExceptionInOzoneObjectException) {
530: throw (ExceptionInOzoneObjectException) result;
531: } else {
532: ((Exception) result).fillInStackTrace();
533: throw (RuntimeException) result;
534: }
535: } else if (result instanceof Exception) {
536: ((Exception) result).fillInStackTrace();
537: throw (Exception) result;
538: } else {
539: if (result instanceof Error) {
540: ((Error) result).fillInStackTrace();
541: throw (Error) result;
542: }
543: }
544: }
545: }
546: }
547: return result;
548: }
549:
550: protected ExternalDatabase linkForProxy(OzoneProxy proxy) {
551: ExternalDatabase link = wrapper != null ? wrapper : this ;
552: // System.out.println ("*** linkForProxy(): " + link.getClass().getName());
553: return link;
554: }
555:
556: protected synchronized void setWrapper(ExternalDatabase _wrapper) {
557: wrapper = _wrapper;
558: }
559:
560: // OzoneInterface methods *****************************
561:
562: public boolean isOpen() throws Exception {
563: return txTable != null;
564: }
565:
566: /**
567: * Open this database connection according to the specified properties.
568: */
569: protected void open(Hashtable _props) throws Exception {
570: txTable = new DxHashMap();
571: apool = new DxArrayDeque(32);
572: upool = new DxHashSet(32);
573:
574: databases.add(this );
575: }
576:
577: /**
578: * Factory method that creates a new database object. The actual type of the
579: * database object ({@link ExternalDatabase} or {@link LocalDatabase})
580: * depends on the specified database URL. The returned database connection
581: * is already open.
582: */
583: public static ExternalDatabase openDatabase(String _url,
584: String _username, String _passwd) throws Exception {
585:
586: Hashtable props = createProps(_url);
587: props.put(PROP_USER, _username);
588: props.put(PROP_PASSWD, _passwd);
589:
590: if (_url.startsWith("ozonedb:remote")) {
591: RemoteDatabase db = new RemoteDatabase();
592: db.open(props);
593: return db;
594: } else if (_url.startsWith("ozonedb:local")) {
595: LocalDatabase db = new LocalDatabase();
596: db.open(props);
597: return db;
598: } else {
599: throw new MalformedURLException(_url);
600: }
601: }
602:
603: /**
604: * Factory method that creates a new database object. The actual type of the
605: * database object ({@link ExternalDatabase} or {@link LocalDatabase})
606: * depends on the specified database URL. The returned database connection
607: * is already open.
608: */
609: public static ExternalDatabase openDatabase(String _url)
610: throws Exception {
611:
612: Hashtable props = createProps(_url);
613: String userName = System.getProperty("org.ozoneDB.user.name");
614:
615: if (userName == null) {
616: userName = System.getProperty("user.name");
617: }
618:
619: props.put(PROP_USER, userName);
620: props.put(PROP_PASSWD, userName);
621:
622: if (_url.startsWith("ozonedb:remote")) {
623: RemoteDatabase db = new RemoteDatabase();
624: db.open(props);
625: return db;
626: } else if (_url.startsWith("ozonedb:local")) {
627: LocalDatabase db = new LocalDatabase();
628: db.open(props);
629: return db;
630: } else {
631: throw new MalformedURLException(_url);
632: }
633: }
634:
635: /**
636: * @param _url The URL of the database (ozonedb:remote://host:port or
637: * ozonedb:local://datadir)
638: */
639: protected static Hashtable createProps(String _url)
640: throws MalformedURLException {
641: Hashtable props = new Hashtable();
642:
643: StringTokenizer tkn = new StringTokenizer(_url, ":");
644: String protocol = tkn.nextToken();
645: if (!protocol.equals("ozonedb")) {
646: throw new MalformedURLException("protocol: " + protocol);
647: }
648:
649: String protocol2 = tkn.nextToken();
650: if (protocol2.equals("local")) {
651: String filename = tkn.nextToken();
652: // on win the filename may contain ":"
653: if (tkn.hasMoreTokens()) {
654: filename = filename + ":" + tkn.nextToken();
655: }
656: props.put(PROP_DIR, filename);
657: } else if (protocol2.equals("remote")) {
658: String hostname = tkn.nextToken();
659: if (!hostname.startsWith("//")) {
660: throw new MalformedURLException("hostname: " + hostname);
661: }
662: // skip the leading "//"
663: hostname = hostname.substring(2);
664: int port = Integer.parseInt(tkn.nextToken());
665: props.put(PROP_HOST, hostname);
666: props.put(PROP_PORT, new Integer(port));
667: } else {
668: throw new MalformedURLException("protocol: " + protocol2);
669: }
670: return props;
671: }
672:
673: /**
674: * Close this database.
675: */
676: public synchronized void close() throws Exception {
677: if (isOpen()) {
678: databases.remove(this );
679:
680: // close all current database connections.
681: try {
682: apool.addAll(upool);
683:
684: DxIterator it = apool.iterator();
685: DbClient connection;
686: while ((connection = (DbClient) it.next()) != null) {
687: connection.send(new DbCloseConn());
688: Thread.sleep(1000);
689: connection.close();
690: it.removeObject();
691: }
692: } finally {
693: apool = null;
694: upool = null;
695: txTable = null;
696: }
697: }
698: }
699:
700: protected void finalize() throws Throwable {
701: close();
702: }
703:
704: public void reloadClasses() throws Exception {
705: Integer result = (Integer) sendCommand(new DbReloadClasses(),
706: true);
707: }
708:
709: public OzoneProxy createObject(String className, int access,
710: String name, String sig, Object[] args)
711: throws RuntimeException, ExceptionInOzoneObjectException {
712: try {
713: OzoneProxy proxy = (OzoneProxy) sendCommand(
714: new DbCreateObj(className, access, name, sig, args),
715: true);
716:
717: if (org.ozoneDB.core.Env.selfCheck) {
718: if (proxy == null) { // The proxy should never be null.
719: throw new Error("Found during createObject(\""
720: + className + "\"," + access + "," + name
721: + ",\"" + sig + "\"): returned proxy is "
722: + proxy);
723: }
724: }
725:
726: return proxy;
727: } catch (ExceptionInOzoneObjectException e) {
728: throw e;
729: } catch (RuntimeException e) {
730: throw e;
731: } catch (Exception e) {
732: // only supported from JDK1.4 on
733: // throw new RuntimeException("Caught during createObject(\""+className+"\","+access+","+name+",\""+sig+"\")",e);
734: throw new RuntimeException("Caught during createObject(\""
735: + className + "\"," + access + "," + name + ",\""
736: + sig + "\"): " + e);
737: }
738: }
739:
740: public void deleteObject(OzoneRemote obj) throws RuntimeException,
741: ExceptionInOzoneObjectException {
742: try {
743: sendCommand(new DbDeleteObj((OzoneProxy) obj), true);
744: } catch (ExceptionInOzoneObjectException e) {
745: throw e;
746: } catch (Exception e) {
747: // only supported from JDK1.4 on
748: // throw new RuntimeException("Caught during deleteObject()",e);
749: throw new RuntimeException("Caught during deleteObject(): "
750: + e);
751: }
752: }
753:
754: public OzoneProxy copyObject(OzoneRemote obj) throws Exception {
755: return (OzoneProxy) sendCommand(
756: new DbCopyObj((OzoneProxy) obj), true);
757: }
758:
759: public void nameObject(OzoneRemote obj, String name)
760: throws Exception {
761: sendCommand(new DbNameObj((OzoneProxy) obj, name), true);
762: }
763:
764: public OzoneProxy objectForName(String name) throws Exception {
765: return (OzoneProxy) sendCommand(new DbObjForName(name), true);
766: }
767:
768: public OzoneProxy objectForHandle(String handle) throws Exception {
769: return (OzoneProxy) sendCommand(new DbObjForHandle(handle),
770: true);
771: }
772:
773: public OzoneProxy[] objectsOfClass(String name) throws Exception {
774: throw new RuntimeException("Method not implemented.");
775: }
776:
777: public Object invoke(OzoneProxy rObj, String methodName,
778: String sig, Object[] args, int lockLevel) throws Exception {
779: // Kommando verschicken
780: Object result = sendCommand(new DbInvoke(rObj, methodName, sig,
781: args, lockLevel), true);
782: return result;
783: }
784:
785: public Object invoke(OzoneProxy rObj, int methodIndex,
786: Object[] args, int lockLevel) throws Exception {
787: // Kommando verschicken
788: Object result = sendCommand(new DbInvoke(rObj, methodIndex,
789: args, lockLevel), true);
790: return result;
791: }
792:
793: public OzoneCompatible fetch(OzoneProxy rObj, int lockLevel)
794: throws Exception {
795: return null;
796: }
797:
798: public Node xmlForObject(OzoneRemote rObj, Document domFactory)
799: throws Exception {
800: byte[] bytes = (byte[]) sendCommand(new DbXMLForObj(
801: (OzoneProxy) rObj), true);
802:
803: SAXChunkConsumer consumer = new SAXChunkConsumer(domFactory,
804: null);
805: consumer.processChunk(bytes);
806:
807: return consumer.getResultNode();
808: }
809:
810: public void xmlForObject(OzoneRemote rObj, ContentHandler ch)
811: throws Exception {
812: byte[] bytes = (byte[]) sendCommand(new DbXMLForObj(
813: (OzoneProxy) rObj), true);
814:
815: SAXChunkConsumer consumer = new SAXChunkConsumer(ch);
816: consumer.processChunk(bytes);
817: }
818:
819: /**
820: * Return the administration object for this database.
821: *
822: * @return The admin object for this database;
823: */
824: public Admin admin() throws Exception {
825: Admin admin = (Admin) objectForName(AdminImpl.OBJECT_NAME);
826: return admin;
827: }
828:
829: /**
830: Internal method. This method is called by {@link OzoneProxy}s when they are dying (during finalize()). This
831: is required, as the database may track the references the database client has to objects within the database
832: in order to properly support garbage collection. If this method is called from anyone else than from the
833: {@link OzoneProxy}.finalize()-Method, data loss may occur!
834:
835: @param proxy the OzoneProxy object which is dying. It may call this method exaclty once.
836: */
837: public void notifyProxyDeath(OzoneProxy proxy) {
838: try {
839: sendCommand(new DbProxyDeath(proxy), false);
840: } catch (Exception e) {
841: // only supported from JDK1.4 on
842: // throw new RuntimeException("Caught during notifyProxyDeath()",e);
843: throw new RuntimeException(
844: "Caught during notifyProxyDeath(): " + e);
845: }
846: }
847: }
|