# DatabaseMultiplexer.java

    /*
    ** Module   : DatabaseMultiplexer.java
    ** Abstract : Abstract base class which enables multiplexing of service
    **            requests by physical database
    **
    ** Copyright (c) 2004-2017, Golden Code Development Corporation.
    **
    ** -#- -I- --Date-- -T- --JPRM-- ----------------Description-----------------
    ** 001 ECF 20071018 ADD  @35549  Created initial version. Abstract base class
    **                               which enables multiplexing of service
    **                               requests by physical database.
    ** 002 ECF 20071128 CHG  @36062  Fixed out of bounds condition in workers
    **                               list. Need to seed list with null values at
    **                               initialization.
    */
    /*
    ** This program is free software: you can redistribute it and/or modify
    ** it under the terms of the GNU Affero General Public License as
    ** published by the Free Software Foundation, either version 3 of the
    ** License, or (at your option) any later version.
    **
    ** This program is distributed in the hope that it will be useful,
    ** but WITHOUT ANY WARRANTY; without even the implied warranty of
    ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    ** GNU Affero General Public License for more details.
    **
    ** You may find a copy of the GNU Affero GPL version 3 at the following
    ** location: https://www.gnu.org/licenses/agpl-3.0.en.html
    **
    ** Additional terms under GNU Affero GPL version 3 section 7:
    **
    **   Under Section 7 of the GNU Affero GPL version 3, the following additional
    **   terms apply to the works covered under the License.  These additional terms
    **   are non-permissive additional terms allowed under Section 7 of the GNU
    **   Affero GPL version 3 and may not be removed by you.
    **
    **   0. Attribution Requirement.
    **
    **     You must preserve all legal notices or author attributions in the covered
    **     work or Appropriate Legal Notices displayed by works containing the covered
    **     work.  You may not remove from the covered work any author or developer
    **     credit already included within the covered work.
    **
    **   1. No License To Use Trademarks.
    **
    **     This license does not grant any license or rights to use the trademarks
    **     Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
    **     of Golden Code Development Corporation. You are not authorized to use the
    **     name Golden Code, FWD, or the names of any author or contributor, for
    **     publicity purposes without written authorization.
    **
    **   2. No Misrepresentation of Affiliation.
    **
    **     You may not represent yourself as Golden Code Development Corporation or FWD.
    **
    **     You may not represent yourself for publicity purposes as associated with
    **     Golden Code Development Corporation, FWD, or any author or contributor to
    **     the covered work, without written authorization.
    **
    **   3. No Misrepresentation of Source or Origin.
    **
    **     You may not represent the covered work as solely your work.  All modified
    **     versions of the covered work must be marked in a reasonable way to make it
    **     clear that the modified work is not originating from Golden Code Development
    **     Corporation or FWD.  All modified versions must contain the notices of
    **     attribution required in this license.
    */

    package com.goldencode.p2j.persist.remote;

    import java.util.*;
    import com.goldencode.p2j.persist.*;
    import com.goldencode.p2j.util.*;

    /**
     * The abstract base class of classes which must multiplex work requests to
     * multiple worker objects, where each worker is associated with a specific,
     * physical database.  Upon construction, all databases managed by the current
     * P2J server instance are inspected.  Client code invokes the {@link
     * #getMultiplexID(String)} method to obtain an ID for a particular physical
     * database name.  This ID will be provided with all future service requests
     * and is used to multiplex such requests to the appropriate worker object
     * which is associated with that database.
     * <p>
     * Subclasses are responsible for implementing the actual service methods,
     * each of which must accept as a parameter a multiplex ID returned by an
     * earlier call to <code>getMultiplexID()</code>.  The implementations of
     * those methods obtain the appropriate worker object to which the request is
     * delegated by invoking {@link #lookupWorker(int)}.  This method lazily
     * associates a worker object with the database identified by the multiplex
     * ID, the first time it is invoked for a particular ID.  This is accomplished
     * via the {@link #getWorker(Database)} method, which must be implemented by
     * the subclass.
     * <p>
     * Service requests may originate from more than one external server, so this
     * implementation is threadsafe.
     *
     * @param   <W>
     *          Type of worker object being multiplexed.
     *
     * @author  ECF
     */
    abstract class DatabaseMultiplexer<W>
    implements RemoteMultiplexer<String>
    {
       /** Databases managed by this P2J server instance */
       private final Database[] databases;

       /** Worker delegates to which requests will be multiplexed */
       private final List<W> workers;

       /**
        * Default constructor.
        */
       protected DatabaseMultiplexer()
       {
          List<Database> dbs = DatabaseManager.getManagedDatabases();
          int size = dbs.size();
          databases = dbs.toArray(new Database[size]);
          workers = new ArrayList<W>(size);

          // Seed workers list with null values;  these will be replaced lazily
          // as needed.
          for (int i = 0; i < size; i++)
          {
             workers.add(null);
          }
       }

       /**
        * Retrieve a unique ID for the given physical database name on this P2J
        * server.  The ID is guaranteed to be unique within the context of the
        * current server instance.
        *
        * @param   key
        *          Physical database name.
        *
        * @return  ID for the given database which is unique for the server.
        *
        * @throws  IllegalArgumentException
        *          if no database with the given physical name is configured at
        *          this server.
        */
       public final int getMultiplexID(String key)
       {
          int len = databases.length;
          for (int i = 0; i < len; i++)
          {
             if (databases[i].getName().equalsIgnoreCase(key))
             {
                return i;
             }
          }

          throw new IllegalArgumentException("Unrecognized database:  " + key);
       }

       /**
        * Access the appropriate worker delegate for the given database ID.
        *
        * @param   id
        *          Integer which uniquely identifies a physical database being
        *          managed by the current P2J server instance.
        *
        * @return  Worker associated with the specified database.
        */
       protected final W lookupWorker(int id)
       {
          synchronized (workers)
          {
             W worker = workers.get(id);
             if (worker == null)
             {
                worker = getWorker(databases[id]);
                workers.set(id, worker);
             }

             return worker;
          }
       }

       /**
        * Get the worker object which is associated with the given database.
        * The type of this worker and how the association with the database is
        * determined by a subclass' particular implementation.
        *
        * @param   database
        *          Physical database with which the returned worker is associated.
        *
        * @return  Worker object to which service requests associated with the
        *          given database must be delegated.
        */
       protected abstract W getWorker(Database database);
    }