KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > ozoneDB > AbstractFactory


1 // You can redistribute this software and/or modify it under the terms of
2
// the Ozone Library License version 1 published by ozone-db.org.
3
//
4
// The original code and portions created by SMB are
5
// Copyright (C) 1997-@year@ by SMB GmbH. All rights reserved.
6
//
7
// $Id: AbstractFactory.java,v 1.9 2003/04/27 13:43:51 per_nyfelt Exp $
8
package org.ozoneDB;
9
10 import java.util.ArrayList JavaDoc;
11 import java.util.Collection JavaDoc;
12 import java.util.HashMap JavaDoc;
13 import java.util.Iterator JavaDoc;
14
15 /** <p>Abstract base class for all ozone-compatible factories. Normally such
16  * factories are generated by OPP and you need not bother with them.
17  * A factory has a bit of a schizophrenic nature: on the client-side it 'links'
18  * to an ExternalDatabase, while on the server-side it does so to the Database
19  * that holds the instances that the factory creates. Note however that a
20  * factory running inside an Ozone server can also be linked to an External
21  * database outside that server (userclient -> server A -> server B). In that
22  * case such a factory would be 'linked' an ExternalDatabase.</p>
23  * <p>The idea behind factories is fourfold:<ul>
24  * <li>make typecasts for creating and retrieving (proxy)objects unneccessairy</li>
25  * <li>provide an abstraction from Ozone specific object creation</li>
26  * <li>facilitate creating objects with non-default constructors</li>
27  * <li>provide (almost) the same interface on the server side and the client
28  * side for object creation</li></ul>
29  * The differences in client side and server side operation are:<ul>
30  * <li>on the server side <code>getDefault()</code> returns a factory that is
31  * creates its objects inside that same server database and on the client side
32  * it returns a factory that creates its objects in the default database</li>
33  * <li>In order to use <code>getDefault()<code> on the client side, you need to
34  * call <code>setDefaultDatabaseUrl(String)</code> to specify the default
35  * database.</li>
36  * In a typical real-world scenario where you connect to only one ozone database
37  * you would call <code>setDefaultDatabaseUrl(String)</code> on the client only
38  * once, and for the rest of the programs lifespan call
39  * <code>xxxFactory.getDefault().create()</code> methods both on the server and
40  * client sides to create objects.</p>
41  * <p>Note: if you do not have a clue what factories are and how they work, you
42  * should probably brush up on your knowledge of <i>design patterns</i>.
43  * @author <a HREF="mailto:ozoneATmekenkampD0Tcom">Leo Mekenkamp (mind the anti-sp@m)</a>
44  * @since REPLACE_ME_SINCE
45  */

46 public abstract class AbstractFactory {
47
48     private static class DatabaseInfo {
49
50         private OzoneInterface database;
51         private boolean autoClose = true;
52         private int useCnt = 0;
53
54         public DatabaseInfo(OzoneInterface database) {
55             this.database = database;
56         }
57
58         public final void pin() {
59             useCnt++;
60         }
61
62         public final void unpin() {
63             useCnt--;
64             try {
65                 // note that a database may already have been closed by closeDatabase()
66
if ((!isPinned()) && getAutoClose() && ((ExternalDatabase) database).isOpen()) {
67                     ((ExternalDatabase) database).close();
68                 }
69             } catch (Exception JavaDoc e) {
70                 throw new UnexpectedException("could not close database " + e.getMessage());
71             }
72         }
73
74         public final boolean isPinned() {
75             return useCnt != 0;
76         }
77
78         public final boolean getAutoClose() {
79             return autoClose;
80         }
81
82         public final void setAutoClose(final boolean autoClose) {
83             if (database instanceof ExternalDatabase) {
84                 this.autoClose = autoClose;
85             } else {
86                 throw new UnexpectedException("cannot change autoClose on server side database");
87             }
88         }
89
90         public final OzoneInterface getDatabase() {
91             return database;
92         }
93     }
94
95     /** Keeps track of which factory classes there are; all in this collection
96      * can be notified through calling 'defaultDatabaseUrlChanged' by a default
97      * url change.
98      */

99     private static Collection JavaDoc factoryClassInfos = new ArrayList JavaDoc();
100
101     /** holds which databases are opened as client (actually holds databaseInfo
102      * instances)
103      */

104     private static HashMap JavaDoc databaseInfoMap = new HashMap JavaDoc();
105
106     /** client side only */
107     private static String JavaDoc defaultDatabaseUrl = null;
108
109     /** Called by the {@link Database} constructor only!
110      */

111     static void setDefaultDatabase(Database defaultDb) {
112         // synchronization here probably not needed, but doing it just in case;
113
// as this is only called once, there is little performace loss
114
synchronized(databaseInfoMap) {
115             DatabaseInfo defaultDatabaseInfo = new DatabaseInfo(defaultDb);
116             databaseInfoMap.put(null, defaultDatabaseInfo);
117         }
118     }
119
120     /** <p>Sets the url to identify the default database. This method can
121      * only be called from a client; a call from within the server will surely
122      * result in an exception, because in the server the default database is
123      * the server itself. Note that after challing this method all
124      * <code>XxxFactory.getDefault()</code> methods return a different factory
125      * than before.</p>
126      * <p>This method calls both <code>System.gc()</code> and <code>System.runFinalization()</code>,
127      * which usually means that if you have no references to factories linked
128      * to the database identified by the former value of <code>getDefaultDatabaseUrl</code>,
129      * that the link to that database will be closed automatically (if
130      * <code>setAutoClose()</code> is <code>true</code>).</p>
131      * @param url identifies the default database that factories created without
132      * specifying a database URL (i.e. their default constructor) will create
133      * their objects in
134      */

135     public static void setDefaultDatabaseUrl(String JavaDoc url) {
136         synchronized(databaseInfoMap) {
137             if (getDefaultDatabase() instanceof Database) {
138                 throw new UnexpectedException("cannot call setDefaultDatabaseUrl() on server/local side");
139             }
140             defaultDatabaseUrl = url;
141             for (Iterator JavaDoc i = factoryClassInfos.iterator(); i.hasNext(); ) {
142                 FactoryClassInfo info = (FactoryClassInfo) i.next();
143                 info.defaultDatabaseUrlChanged();
144             }
145         }
146         System.gc();
147         System.runFinalization();
148     }
149
150     public static String JavaDoc getDefaultDatabaseUrl() {
151         synchronized(databaseInfoMap) {
152             return defaultDatabaseUrl;
153         }
154     }
155
156     protected static OzoneInterface getDefaultDatabase() {
157         synchronized(databaseInfoMap) {
158             DatabaseInfo databaseInfo = (DatabaseInfo) databaseInfoMap.get(getDefaultDatabaseUrl());
159             return (databaseInfo == null) ? null : databaseInfo.getDatabase();
160         }
161     }
162
163     protected static void addFactoryClassInfo(FactoryClassInfo info) {
164         synchronized(databaseInfoMap) {
165             factoryClassInfos.add(info);
166         }
167     }
168
169     private String JavaDoc databaseUrl;
170
171     private DatabaseInfo databaseInfo;
172
173     /** Creates a factory object that creates its objects in the same database
174      * as the default factory.
175      */

176     protected AbstractFactory() throws Exception JavaDoc {
177         synchronized(databaseInfoMap) {
178             if ((getDefaultDatabase() == null) && (getDefaultDatabaseUrl() == null)) {
179                 throw new OzoneRemoteException("default database url not set");
180             }
181             init(getDefaultDatabaseUrl());
182         }
183     }
184
185     /** <p>Creates a factory that creates its objects in the database specified by
186      * <code>url</code>. Note that this constructor can only be used if the database in
187      * question is not opened by this client. Use <code>AbstractFactory()</code> or
188      * <code>AbstractFactory(Factory)</code> to create a factory for an already opened
189      * database. This might seem strange, or even annoying at first, but it is very
190      * logical from an object oriented point of view: you probably want a factory that
191      * creates its objects in the default database, or in the same realm as another
192      * factory you already have created...</p>
193      * @param databaseUrl url defining the remote database (something like
194      * <code>ozone:remote://localhost:3333</code>)
195      */

196     protected AbstractFactory(String JavaDoc databaseUrl) throws Exception JavaDoc {
197         init(databaseUrl);
198     }
199
200     private void init(String JavaDoc databaseUrl) throws Exception JavaDoc {
201         synchronized (databaseInfoMap) {
202             this.databaseUrl = databaseUrl;
203             databaseInfo = (DatabaseInfo) databaseInfoMap.get(databaseUrl);
204             if (databaseInfo == null) {
205                 ExternalDatabase db = ExternalDatabase.openDatabase(databaseUrl);
206                 databaseInfo = new DatabaseInfo(db);
207                 databaseInfoMap.put(databaseUrl, databaseInfo);
208             }
209             databaseInfo.pin();
210         }
211     }
212
213     /** <p>Creates a factory that creates its objects in the same database as a specific
214      * other fatory does.</p>
215      * @param factory the factory that creates its objects in the same database as the new factory
216      * should
217      */

218     protected AbstractFactory(AbstractFactory factory) {
219         synchronized (databaseInfoMap) {
220             databaseInfo = (DatabaseInfo) databaseInfoMap.get(factory.getDatabaseUrl());
221             databaseInfo.pin();
222         }
223     }
224
225     protected void finalize() throws Throwable JavaDoc {
226         synchronized (databaseInfoMap) {
227             databaseInfo.unpin();
228             if (!databaseInfo.isPinned()) {
229                 // this action is unnecessary when the database has been closed;
230
// the dbInfoMap has then already been removed.
231
databaseInfoMap.remove(getDatabaseUrl());
232             }
233         }
234         super.finalize();
235     }
236
237     /** <p>Closes the connection to the database this instance creates its
238      * objects in. Closing happens at once, unlike the autoClose property.</p>
239      * Note: when you call this method, you close the connection to the database
240      * FOR ALL OTHER FACTORIES that link to the same database as this instance
241      * as well!</p>
242      * @see #getAutoClose
243      * @see #setAutoClose
244      */

245     public final void closeDatabase() throws Exception JavaDoc {
246         synchronized (databaseInfoMap) {
247             OzoneInterface db = getDatabase();
248             if (db instanceof ExternalDatabase) {
249                 ((ExternalDatabase) db).close();
250                 databaseInfoMap.remove(getDatabaseUrl());
251             } else {
252                 throw new UnexpectedException("cannot close database on server side");
253             }
254         }
255     }
256
257     /** <p>Override this method to find out when the default database has been
258      * closed. Every class derived from <code>AbstractFactory</code> has its own
259      * default instance.</p>
260      */

261     abstract protected void defaultClosed();
262
263     /** <p>Returns the database this instance creates its objects in.</p>
264      * @return database this factory is 'linked' to
265      */

266     public final OzoneInterface getDatabase() {
267         return databaseInfo.getDatabase();
268     }
269
270     protected final String JavaDoc getDatabaseUrl() {
271         return databaseUrl;
272     }
273
274     /** <p>Gets current autoClose setting for this and all other factories
275      * linked to the same database. When <code>true</code> the connection to the
276      * database this instance creates its objects in is closed when all
277      * factories connected to that database are garbage collected.
278      */

279     public final boolean getAutoClose() {
280         return databaseInfo.getAutoClose();
281     }
282
283     /** <p>Sets current autoClose setting for this and all other factories
284      * linked to the same database. When autoClose is <code>true</code> the
285      * connection to the database this instance creates its objects in is closed
286      * when all factories connected to that database are garbage collected.</p>
287      * <p>Note: when you call this method, change the auto-close settings for all
288      * other factories that link to the same database as this instance as well!
289      * </p>
290      */

291     public final void setAutoClose(boolean autoClose) {
292         databaseInfo.setAutoClose(autoClose);
293     }
294
295 }
Popular Tags