⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 abstractfactory.java

📁 Java的面向对象数据库系统的源代码
💻 JAVA
字号:
// You can redistribute this software and/or modify it under the terms of
// the Ozone Library License version 1 published by ozone-db.org.
//
// The original code and portions created by SMB are
// Copyright (C) 1997-@year@ by SMB GmbH. All rights reserved.
//
// $Id: AbstractFactory.java,v 1.9 2003/04/27 13:43:51 per_nyfelt Exp $
package org.ozoneDB;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;

/** <p>Abstract base class for all ozone-compatible factories. Normally such
 * factories are generated by OPP and you need not bother with them.
 * A factory has a bit of a schizophrenic nature: on the client-side it 'links'
 * to an ExternalDatabase, while on the server-side it does so to the Database
 * that holds the instances that the factory creates. Note however that a
 * factory running inside an Ozone server can also be linked to an External
 * database outside that server (userclient -> server A -> server B). In that
 * case such a factory would be 'linked' an ExternalDatabase.</p>
 * <p>The idea behind factories is fourfold:<ul>
 * <li>make typecasts for creating and retrieving (proxy)objects unneccessairy</li>
 * <li>provide an abstraction from Ozone specific object creation</li>
 * <li>facilitate creating objects with non-default constructors</li>
 * <li>provide (almost) the same interface on the server side and the client
 * side for object creation</li></ul>
 * The differences in client side and server side operation are:<ul>
 * <li>on the server side <code>getDefault()</code> returns a factory that is
 * creates its objects inside that same server database and on the client side
 * it returns a factory that creates its objects in the default database</li>
 * <li>In order to use <code>getDefault()<code> on the client side, you need to
 * call <code>setDefaultDatabaseUrl(String)</code> to specify the default
 * database.</li>
 * In a typical real-world scenario where you connect to only one ozone database
 * you would call <code>setDefaultDatabaseUrl(String)</code> on the client only
 * once, and for the rest of the programs lifespan call
 * <code>xxxFactory.getDefault().create()</code> methods both on the server and
 * client sides to create objects.</p>
 * <p>Note: if you do not have a clue what factories are and how they work, you
 * should probably brush up on your knowledge of <i>design patterns</i>.
 * @author <a href="mailto:ozoneATmekenkampD0Tcom">Leo Mekenkamp (mind the anti-sp@m)</a>
 * @since REPLACE_ME_SINCE
 */
public abstract class AbstractFactory {

    private static class DatabaseInfo {

        private OzoneInterface database;
        private boolean autoClose = true;
        private int useCnt = 0;

        public DatabaseInfo(OzoneInterface database) {
            this.database = database;
        }

        public final void pin() {
            useCnt++;
        }

        public final void unpin() {
            useCnt--;
            try {
                // note that a database may already have been closed by closeDatabase()
                if ((!isPinned()) && getAutoClose() && ((ExternalDatabase) database).isOpen()) {
                    ((ExternalDatabase) database).close();
                }
            } catch (Exception e) {
                throw new UnexpectedException("could not close database " + e.getMessage());
            }
        }

        public final boolean isPinned() {
            return useCnt != 0;
        }

        public final boolean getAutoClose() {
            return autoClose;
        }

        public final void setAutoClose(final boolean autoClose) {
            if (database instanceof ExternalDatabase) {
                this.autoClose = autoClose;
            } else {
                throw new UnexpectedException("cannot change autoClose on server side database");
            }
        }

        public final OzoneInterface getDatabase() {
            return database;
        }
    }

    /** Keeps track of which factory classes there are; all in this collection
     * can be notified through calling 'defaultDatabaseUrlChanged' by a default
     * url change.
     */
    private static Collection factoryClassInfos = new ArrayList();

    /** holds which databases are opened as client (actually holds databaseInfo
     * instances)
     */
    private static HashMap databaseInfoMap = new HashMap();

    /** client side only */
    private static String defaultDatabaseUrl = null;

    /** Called by the {@link Database} constructor only!
     */
    static void setDefaultDatabase(Database defaultDb) {
        // synchronization here probably not needed, but doing it just in case;
        // as this is only called once, there is little performace loss
        synchronized(databaseInfoMap) {
            DatabaseInfo defaultDatabaseInfo = new DatabaseInfo(defaultDb);
            databaseInfoMap.put(null, defaultDatabaseInfo);
        }
    }

    /** <p>Sets the url to identify the default database. This method can
     * only be called from a client; a call from within the server will surely
     * result in an exception, because in the server the default database is
     * the server itself. Note that after challing this method all
     * <code>XxxFactory.getDefault()</code> methods return a different factory
     * than before.</p>
     * <p>This method calls both <code>System.gc()</code> and <code>System.runFinalization()</code>,
     * which usually means that if you have no references to factories linked
     * to the database identified by the former value of <code>getDefaultDatabaseUrl</code>,
     * that the link to that database will be closed automatically (if
     * <code>setAutoClose()</code> is <code>true</code>).</p>
     * @param url identifies the default database that factories created without
     * specifying a database URL (i.e. their default constructor) will create
     * their objects in
     */
    public static void setDefaultDatabaseUrl(String url) {
        synchronized(databaseInfoMap) {
            if (getDefaultDatabase() instanceof Database) {
                throw new UnexpectedException("cannot call setDefaultDatabaseUrl() on server/local side");
            }
            defaultDatabaseUrl = url;
            for (Iterator i = factoryClassInfos.iterator(); i.hasNext(); ) {
                FactoryClassInfo info = (FactoryClassInfo) i.next();
                info.defaultDatabaseUrlChanged();
            }
        }
        System.gc();
        System.runFinalization();
    }

    public static String getDefaultDatabaseUrl() {
        synchronized(databaseInfoMap) {
            return defaultDatabaseUrl;
        }
    }

    protected static OzoneInterface getDefaultDatabase() {
        synchronized(databaseInfoMap) {
            DatabaseInfo databaseInfo = (DatabaseInfo) databaseInfoMap.get(getDefaultDatabaseUrl());
            return (databaseInfo == null) ? null : databaseInfo.getDatabase();
        }
    }

    protected static void addFactoryClassInfo(FactoryClassInfo info) {
        synchronized(databaseInfoMap) {
            factoryClassInfos.add(info);
        }
    }

    private String databaseUrl;

    private DatabaseInfo databaseInfo;

    /** Creates a factory object that creates its objects in the same database
     * as the default factory.
     */
    protected AbstractFactory() throws Exception {
        synchronized(databaseInfoMap) {
            if ((getDefaultDatabase() == null) && (getDefaultDatabaseUrl() == null)) {
                throw new OzoneRemoteException("default database url not set");
            }
            init(getDefaultDatabaseUrl());
        }
    }

    /** <p>Creates a factory that creates its objects in the database specified by
     * <code>url</code>. Note that this constructor can only be used if the database in
     * question is not opened by this client. Use <code>AbstractFactory()</code> or
     * <code>AbstractFactory(Factory)</code> to create a factory for an already opened
     * database. This might seem strange, or even annoying at first, but it is very
     * logical from an object oriented point of view: you probably want a factory that
     * creates its objects in the default database, or in the same realm as another
     * factory you already have created...</p>
     * @param databaseUrl url defining the remote database (something like
     * <code>ozone:remote://localhost:3333</code>)
     */
    protected AbstractFactory(String databaseUrl) throws Exception {
        init(databaseUrl);
    }

    private void init(String databaseUrl) throws Exception {
        synchronized (databaseInfoMap) {
            this.databaseUrl = databaseUrl;
            databaseInfo = (DatabaseInfo) databaseInfoMap.get(databaseUrl);
            if (databaseInfo == null) {
                ExternalDatabase db = ExternalDatabase.openDatabase(databaseUrl);
                databaseInfo = new DatabaseInfo(db);
                databaseInfoMap.put(databaseUrl, databaseInfo);
            }
            databaseInfo.pin();
        }
    }

    /** <p>Creates a factory that creates its objects in the same database as a specific
     * other fatory does.</p>
     * @param factory the factory that creates its objects in the same database as the new factory
     * should
     */
    protected AbstractFactory(AbstractFactory factory) {
        synchronized (databaseInfoMap) {
            databaseInfo = (DatabaseInfo) databaseInfoMap.get(factory.getDatabaseUrl());
            databaseInfo.pin();
        }
    }

    protected void finalize() throws Throwable {
        synchronized (databaseInfoMap) {
            databaseInfo.unpin();
            if (!databaseInfo.isPinned()) {
                // this action is unnecessary when the database has been closed;
                // the dbInfoMap has then already been removed.
                databaseInfoMap.remove(getDatabaseUrl());
            }
        }
        super.finalize();
    }

    /** <p>Closes the connection to the database this instance creates its
     * objects in. Closing happens at once, unlike the autoClose property.</p>
     * Note: when you call this method, you close the connection to the database
     * FOR ALL OTHER FACTORIES that link to the same database as this instance
     * as well!</p>
     * @see #getAutoClose
     * @see #setAutoClose
     */
    public final void closeDatabase() throws Exception {
        synchronized (databaseInfoMap) {
            OzoneInterface db = getDatabase();
            if (db instanceof ExternalDatabase) {
                ((ExternalDatabase) db).close();
                databaseInfoMap.remove(getDatabaseUrl());
            } else {
                throw new UnexpectedException("cannot close database on server side");
            }
        }
    }

    /** <p>Override this method to find out when the default database has been
     * closed. Every class derived from <code>AbstractFactory</code> has its own
     * default instance.</p>
     */
    abstract protected void defaultClosed();

    /** <p>Returns the database this instance creates its objects in.</p>
     * @return database this factory is 'linked' to
     */
    public final OzoneInterface getDatabase() {
        return databaseInfo.getDatabase();
    }

    protected final String getDatabaseUrl() {
        return databaseUrl;
    }

    /** <p>Gets current autoClose setting for this and all other factories
     * linked to the same database. When <code>true</code> the connection to the
     * database this instance creates its objects in is closed when all
     * factories connected to that database are garbage collected.
     */
    public final boolean getAutoClose() {
        return databaseInfo.getAutoClose();
    }

    /** <p>Sets current autoClose setting for this and all other factories
     * linked to the same database. When autoClose is <code>true</code> the
     * connection to the database this instance creates its objects in is closed
     * when all factories connected to that database are garbage collected.</p>
     * <p>Note: when you call this method, change the auto-close settings for all
     * other factories that link to the same database as this instance as well!
     * </p>
     */
    public final void setAutoClose(boolean autoClose) {
        databaseInfo.setAutoClose(autoClose);
    }

}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -