view geo-backend/src/main/java/de/intevation/gnv/geobackend/sde/datasources/ArcSDEConnectionPool.java @ 129:110e3ac1b7d2

Library Dependencies Added to pom.xml-File Import of SDE-Datasources geo-backend/trunk@5 c6561f87-3c4e-4783-a992-168aeb5c3f6f
author Tim Englich <tim.englich@intevation.de>
date Wed, 02 Sep 2009 09:07:03 +0000
parents
children
line wrap: on
line source
/*******************************************************************************
 * Copyright � 2007 52�North Initiative for Geospatial Open Source Software GmbH
 *
 * Author: Oliver Meyer, University of Muenster
 *
 * Contact: Andreas Wytzisk, 52�North Initiative for Geospatial Open Source
 * Software GmbH, Martin-Luther-King-Weg 24, 48155 Muenster, Germany,
 * info@52north.org
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License version 2 as published by the
 * Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; even without the implied WARRANTY OF MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program (see gnu-gpl v2.txt). If not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA or
 * visit the Free Software Foundation�s web page, http://www.fsf.org.
 *
 ******************************************************************************/
// Last changes on: 2007-06-03
// Last changes by: Oliver Meyer

package de.intevation.gnv.geobackend.sde.datasources;

import java.util.Date;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.Map.Entry;

import org.apache.log4j.Logger;

import com.esri.sde.sdk.client.SeConnection;
import com.esri.sde.sdk.client.SeException;

import de.intevation.gnv.geobackend.sde.datasources.exception.ConnectionException;
import de.intevation.gnv.geobackend.sde.datasources.exception.TechnicalException;

/**
 * Connection Pool for ArcSDE databases.
 *
 * @author Oliver Meyer
 * @version 1.0
 */
public class ArcSDEConnectionPool {

    /**
     * Default Logging instance
     */
    private static final Logger sLogger = Logger.getLogger(ArcSDEConnectionPool.class);
    private static boolean sDebug = sLogger.isDebugEnabled();

    /**
     * timer for holding connections
     */
    private Timer timer;

    /**
     * the logger, used to log exceptions and additonaly information
     */
    private static Logger log = Logger.getLogger(ArcSDEConnectionPool.class);

    /**
     * Hashtable containing the connections as keys and Boolean as value (False if the connection is not used)
     */
    private Hashtable<SeConnection, Boolean> connections;

    private static ArcSDEConnectionPool sInstance = null;

    private static ArcSDEConnectionParams sParams = null;

    public static ArcSDEConnectionPool getInstance() throws TechnicalException {
        if (sInstance == null) {
            throw new TechnicalException("The ArcSDEConnectionPool has to be configured first!");
        }
        return sInstance;
    }

    public static boolean isConfigured() {
        return sParams != null;
    }

    public static void configure(ArcSDEConnectionParams pParams) throws TechnicalException {
        if (sDebug) sLogger.debug("configure()");

        if (sInstance == null) {
            synchronized (ArcSDEConnectionPool.class) {
                sInstance = new ArcSDEConnectionPool(pParams);
            }
        } else {
            throw new TechnicalException("The ArcSDEConnectionPool is already configured: " + pParams.toString());
        }

    }


    private ArcSDEConnectionPool(ArcSDEConnectionParams pParams) throws ConnectionException {
        if (sDebug) sLogger.debug("ArcSDEConnectionPool()");
        sParams = pParams;

        connections = new Hashtable<SeConnection, Boolean>();
        initPool(sParams.getInitConnections());
        timer = new Timer("ArcSDEConnectionPoolTimer");
        startConnectionTask();
    }

    /**
     * help method for initializing the connection pool
     *
     * @param initConnections number of initial connections
     * @throws OwsExceptionReport if creating connections failed while initializing the connection pool
     */
    protected void initPool(int initConnections) throws ConnectionException {

        // initialize connections;
        // "false" indicates that the connection is not used yet
        for (int i = 0; i < initConnections; ++i) {
            SeConnection con = getNewConnection();
            connections.put(con, Boolean.FALSE);
        }
    }

    /**
     * Method returns an available connection from the pool, and sets it on "not available". After the query
     * operation, you have to "give back" the connection to the pool with the returnConnection method!
     *
     * @return ArcSDE connection to execute the query
     * @throws OwsExceptionReport If all connections are in use and no further connection could be established
     * @throws OwsExceptionReport
     */
    protected SeConnection getConnection() throws ConnectionException {

        SeConnection con = null;

        Enumeration<SeConnection> cons = connections.keys();

        // no other operation (maybe adding a connection) while checking which
        // (or whether a) connection is available
        synchronized (connections) {

            while (cons.hasMoreElements()) {
                con = cons.nextElement();
                Boolean b = connections.get(con);

                // checking whether connection is available
                if (b == Boolean.FALSE) {
                    // connection is available, now test, whether connection is
                    // OK (with setAutoCommit)
                    try {
                        con.setTransactionAutoCommit(1000);
                    }
                    catch (SeException sdeEx) {
                        // problem with connection, so remove and create new
                        // connection
                        connections.remove(con);

                        con = getNewConnection();

                    }
                    // set connection "not available" (Value=true)
                    connections.put(con, Boolean.TRUE);
                    return con;
                }
            }
        }

        // if no connections are available, create new one
        if (connections.size() <= sParams.getMaxConnections()) {

            con = getNewConnection();

            connections.put(con, Boolean.TRUE);
        }
        // if maximal number of connections is arrived, throw exception!
        else {
            throw new ConnectionException("All db connections are in use. Please try again later!");
        }

        // return new Connection
        return con;
    }

    /**
     * abstract method creates a new connection; must be implemented by all subclasses!
     *
     * @return Returns connection - new connection to the database
     * @throws OwsExceptionReport if creating a new connection failed
     */
    private SeConnection getNewConnection() throws ConnectionException {
        SeConnection con;

        // creating new connection
        try {

            con = new SeConnection(sParams.getServer(), sParams.getInstance(), sParams.getDatabase(), sParams.getUser(), sParams.getPwd());

            //            Class.forName("org.postgresql.Driver");
            //            con = DriverManager.getConnection(props.getProperty("CONNECTION"), props);

        }
        catch (SeException sdeEx) {
            throw new ConnectionException("Establishing a connection to database failed: " + sdeEx.toString(), sdeEx);
        }

        return con;
    }

    /**
     * Invoke this method after executing the query with this connection, so that the connection is already
     * available in the pool
     *
     * @param con the connection which was used and now is available again
     */
    public void returnConnection(SeConnection con) {
        if (connections.containsKey(con)) {
            connections.put(con, Boolean.FALSE);
        }
    }

    /**
     * method sends a query for every connection
     */
    private void holdConnections() {
        Set<Entry<SeConnection, Boolean>> entrySet = connections.entrySet();
        Iterator<Entry<SeConnection, Boolean>> iter = entrySet.iterator();
        SeConnection con = null;
        while (iter.hasNext()) {
            Entry<SeConnection, Boolean> entry = iter.next();

            //Connection in use?
            if (entry.getValue() == false) {
                con = entry.getKey();
                try {
                    con.setTransactionAutoCommit(1000);
                }
                catch (SeException sdeEx) {

                    //if sql exception occurs, try to built new connection and put connection into pool
                    connections.remove(con);
                    try {
                        SeConnection conNew = getNewConnection();
                        connections.put(conNew, Boolean.FALSE);
                    }
                    catch (ConnectionException e) {
                        log.debug("Erroe while refreshing connections: " + e.getMessage());
                    }
                    log.error("An error occurred while holding connections through query!", sdeEx);
                }
            }
        }
    }

    /**
     * method invokes the schedule method of the timer with a new InstantFeederTask, the actual date and the
     * period from the config file as parameters.
     *
     * @throws OwsExceptionReport if creation of the InstantFeederTask failed
     */
    private void startConnectionTask() throws ConnectionException {
        timer.schedule(new ConnectionTask(), new Date(), sParams.getTimeToHold());
    }

    /**
     * connection task used for holding connections and making sure, that these
     *
     * @author Christoph Stasch
     */
    protected class ConnectionTask extends TimerTask {

        /**
         * overwritten run method of TimerTask which queries offeringIDs from db to make sure, that
         * connections won't be blocked by the firewalls
         */
        public void run() {
            holdConnections();
        }

    }

}

http://dive4elements.wald.intevation.org