com.piusvelte.hydra.ConnectionManager.java Source code

Java tutorial

Introduction

Here is the source code for com.piusvelte.hydra.ConnectionManager.java

Source

/*
 * Hydra
 * Copyright (C) 2012 Bryan Emmanuel
 * 
 * This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU 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 General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *  
 *  Bryan Emmanuel piusvelte@gmail.com
 */
package com.piusvelte.hydra;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Properties;
import java.util.Set;

import javax.servlet.ServletContext;

import org.json.simple.JSONArray;
import org.json.simple.JSONObject;

public class ConnectionManager {

    private static final String sPassphrase = "passphrase";
    private static final String sDatabases = "databases";
    private static final String sType = "type";
    private static final String sDatabase = "database";
    private static final String sHost = "host";
    private static final String sPort = "port";
    private static final String sUsername = "username";
    private static final String sPassword = "password";
    private static final String sConnections = "connections";
    private static final String sQueueRetryInterval = "queueretryinterval";
    private static final String sDASU = "DASU";
    private static final String sDASP = "DASP";
    private static final String sSQLENVINIT = "SQLENVINIT";
    protected static final String DB_TYPE_UNIDATA = "unidata";
    protected static final String DB_TYPE_MYSQL = "mysql";
    protected static final String DB_TYPE_MSSQL = "mssql";
    protected static final String DB_TYPE_ORACLE = "oracle";
    protected static final String DB_TYPE_POSTGRESQL = "postgresql";
    private String passphrase = null;
    private int[] databaseLock = new int[0];
    private HashMap<String, HashMap<String, String>> sDatabaseSettings = new HashMap<String, HashMap<String, String>>();
    private HashMap<String, ArrayList<DatabaseConnection>> sDatabaseConnections = new HashMap<String, ArrayList<DatabaseConnection>>();
    private HashMap<String, Integer> queuedDatabaseRequests = new HashMap<String, Integer>();
    private String sHydraDir = null;
    private String sQueueFile = null;
    private QueueThread sQueueThread = null;
    private int[] sQueueLock = new int[0];
    private String tokenFile = null;
    private String HYDRA_PROPERTIES = "hydra.properties";
    private int mQueueRetryInterval;
    private int[] tokenLock = new int[0];
    private ArrayList<String> tokens = new ArrayList<String>();
    private HashMap<String, String> unauthorizedTokens = new HashMap<String, String>();

    private static final String WIN_DIR = "ProgramData";
    private static final String NIX_DIR = "var/lib";

    private static ConnectionManager hydraService = null;

    private ConnectionManager(ServletContext ctx) {

        ctx.log("Hydra ConnectionManager instantiated");

        String[] fullPathParts;

        if (System.getProperty("os.name").startsWith("Windows")) {
            fullPathParts = ctx.getRealPath(File.separator).split("\\\\", -1);
            sHydraDir = fullPathParts[0] + File.separator + WIN_DIR;

        } else {
            fullPathParts = ctx.getRealPath(File.separator).split(File.separator, -1);
            sHydraDir = fullPathParts[0] + File.separator + NIX_DIR;
        }

        sHydraDir += File.separator + "hydra";
        ctx.log("Working Directory: " + sHydraDir);

        if (fullPathParts.length > 2) {
            if (fullPathParts.length > 3) {
                sHydraDir += File.separator + fullPathParts[fullPathParts.length - 3];
            }
            sHydraDir += File.separator + fullPathParts[fullPathParts.length - 2];
        }

        mQueueRetryInterval = QueueThread.DEFAULT_QUEUERETRYINTERVAL;

        File hydraDir = new File(sHydraDir);
        if (hydraDir.exists()) {
            sHydraDir += File.separator;

            InputStream is = null;

            try {
                is = new FileInputStream(sHydraDir + HYDRA_PROPERTIES);

            } catch (FileNotFoundException e1) {
                ctx.log("The properties file at " + (sHydraDir + HYDRA_PROPERTIES) + " could not be found.");
                e1.printStackTrace();
            }

            if (is != null) {
                Properties properties = new Properties();
                try {
                    properties.load(is);
                    ctx.log("Hydra properties file read");
                    if (properties.containsKey(sPassphrase))
                        passphrase = properties.getProperty(sPassphrase);
                    sQueueFile = sHydraDir + "queue";
                    tokenFile = sHydraDir + "tokens";
                    try {
                        loadTokens();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    if (properties.containsKey(sQueueRetryInterval))
                        mQueueRetryInterval = Integer.parseInt(properties.getProperty(sQueueRetryInterval));
                    if (properties.containsKey(sDatabases)) {
                        String[] databaseAliases = properties.getProperty(sDatabases).split(",", -1);
                        String[] databaseProperties = new String[] { sType, sDatabase, sHost, sPort, sUsername,
                                sPassword, sConnections, sDASU, sDASP, sSQLENVINIT };
                        for (String databaseAlias : databaseAliases) {
                            HashMap<String, String> database = new HashMap<String, String>();
                            for (String databaseProperty : databaseProperties) {
                                database.put(databaseProperty,
                                        properties.getProperty(databaseAlias + "." + databaseProperty, ""));
                            }
                            synchronized (databaseLock) {
                                sDatabaseSettings.put(databaseAlias, database);
                                sDatabaseConnections.put(databaseAlias, new ArrayList<DatabaseConnection>());
                                queuedDatabaseRequests.put(databaseAlias, 0);
                            }
                        }
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            } else {
                initProps();
            }
        } else if (hydraDir.mkdirs()) {
            sHydraDir += File.separator;
            initProps();
        } else {
            ctx.log("properties doesn't exist, and creating it failed at: " + sHydraDir);
        }
    }

    private void initProps() {
        Properties properties = new Properties();
        properties.put(sPassphrase, "changeit");
        properties.put(sDatabases, "");
        PrintWriter pw;
        try {
            pw = new PrintWriter(new FileOutputStream(sHydraDir + HYDRA_PROPERTIES));

            properties.store(pw,
                    "The passphrase is used to authorize tokens\n Databases should be a comment delimited string of database aliases, followed by their connection properties\n"
                            + " database types:\n   unidata\n   mysql\n   mssql\n   oracle\n   postgresql\n\nexample:"
                            + "databases=mydb\n" + "mydb.type=unidata\n" + "mydb.database=C:\\U2\\ud73\\demo\n"
                            + "mydb.host=localhost\n" + "mydb.port=31438\n" + "mydb.username=myuser\n"
                            + "mydb.password=mypss\n" + "mydb.connections=1\n" + "mydb.DASU=mydasu\n"
                            + "mydb.DASP=mydasp\n" + "mddb.SQLENVINIT=\n");
            pw.close();
        } catch (FileNotFoundException e1) {
            e1.printStackTrace();
        } catch (IOException e1) {
            e1.printStackTrace();
        }
    }

    public static ConnectionManager getInstance(ServletContext ctx) {
        if (hydraService == null)
            hydraService = new ConnectionManager(ctx);
        return hydraService;
    }

    DatabaseConnection getDatabaseConnection(String database) throws Exception {
        // return an existing database connection, or spawn a new one if the pool isn't full
        // check for existing connection
        synchronized (databaseLock) {
            if (sDatabaseConnections.containsKey(database)) {
                ArrayList<DatabaseConnection> connections = sDatabaseConnections.get(database);
                for (DatabaseConnection databaseConnection : connections) {
                    if (databaseConnection.connect())
                        return databaseConnection;
                }
            }
        }
        DatabaseConnection databaseConnection = null;
        synchronized (databaseLock) {
            if (sDatabaseSettings.containsKey(database)) {
                ArrayList<DatabaseConnection> connections = sDatabaseConnections.get(database);
                HashMap<String, String> databaseSettings = sDatabaseSettings.get(database);
                if (connections.size() < Integer.parseInt(databaseSettings.get(sConnections))) {
                    String type = databaseSettings.get(sType);
                    int port = Integer.parseInt(databaseSettings.get(sPort));
                    if (DB_TYPE_UNIDATA.equals(type))
                        (databaseConnection = new UnidataConnection(databaseSettings.get(sHost), port,
                                databaseSettings.get(sDatabase), databaseSettings.get(sUsername),
                                databaseSettings.get(sPassword), databaseSettings.get(sDASU),
                                databaseSettings.get(sDASP), databaseSettings.get(sSQLENVINIT))).connect();
                    else if (DB_TYPE_MSSQL.equals(type))
                        (databaseConnection = new MSSQLConnection(databaseSettings.get(sHost), port,
                                databaseSettings.get(sDatabase), databaseSettings.get(sUsername),
                                databaseSettings.get(sPassword))).connect();
                    else if (DB_TYPE_ORACLE.equals(type))
                        (databaseConnection = new OracleConnection(databaseSettings.get(sHost), port,
                                databaseSettings.get(sDatabase), databaseSettings.get(sUsername),
                                databaseSettings.get(sPassword))).connect();
                    else if (DB_TYPE_MYSQL.equals(type))
                        (databaseConnection = new MySQLConnection(databaseSettings.get(sHost), port,
                                databaseSettings.get(sDatabase), databaseSettings.get(sUsername),
                                databaseSettings.get(sPassword))).connect();
                    else if (DB_TYPE_POSTGRESQL.equals(type))
                        (databaseConnection = new PostgreSQLConnection(databaseSettings.get(sHost), port,
                                databaseSettings.get(sDatabase), databaseSettings.get(sUsername),
                                databaseSettings.get(sPassword))).connect();
                    else
                        throw new Exception("unknown type:" + type);
                    connections.add(databaseConnection);
                    sDatabaseConnections.put(database, connections);
                    return databaseConnection;
                }
            } else
                throw new Exception("Database " + database + " not defined.");
        }
        return databaseConnection;
    }

    void queueDatabaseRequest(String database) {
        synchronized (databaseLock) {
            queuedDatabaseRequests.put(database, queuedDatabaseRequests.get(database) + 1);
        }
    }

    void dequeueDatabaseRequest(String database) {
        synchronized (databaseLock) {
            queuedDatabaseRequests.put(database, queuedDatabaseRequests.get(database) - 1);
        }
    }

    void cleanDatabaseConnections(String database) {
        synchronized (databaseLock) {
            ArrayList<DatabaseConnection> connections = sDatabaseConnections.get(database);
            for (int i = 0; (i < connections.size())
                    && (connections.size() > queuedDatabaseRequests.get(database)); i++) {
                DatabaseConnection connection = connections.get(i);
                if (!connection.isLocked()) {
                    try {
                        connection.disconnect();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    connections.remove(i--);
                }
            }
            sDatabaseConnections.put(database, connections);
        }
    }

    @SuppressWarnings("unchecked")
    JSONObject getDatabases() {
        JSONObject response = new JSONObject();
        JSONArray rows = new JSONArray();
        synchronized (databaseLock) {
            Set<String> databases = sDatabaseSettings.keySet();
            Iterator<String> iter = databases.iterator();
            while (iter.hasNext()) {
                JSONArray rowData = new JSONArray();
                rowData.add(iter.next());
                rows.add(rowData);
            }
        }
        response.put("result", rows);
        return response;
    }

    @SuppressWarnings("unchecked")
    JSONObject getDatabase(String database) {
        JSONObject response = new JSONObject();
        JSONArray rows = new JSONArray();
        synchronized (databaseLock) {
            if (sDatabaseSettings.containsKey(database)) {
                JSONArray rowData = new JSONArray();
                HashMap<String, String> databaseSettings = sDatabaseSettings.get(database);
                rowData.add(database);
                rowData.add(databaseSettings.get(sDatabase));
                rowData.add(databaseSettings.get(sHost));
                rowData.add(databaseSettings.get(sPort));
                rowData.add(databaseSettings.get(sType));
                rows.add(rowData);
            }
        }
        response.put("result", rows);
        return response;
    }

    static String getHash64(String in) {
        String out = null;
        MessageDigest md;
        try {
            md = MessageDigest.getInstance("SHA-256");
            md.update(in.getBytes("UTF-8"));
            out = new BigInteger(1, md.digest()).toString(16);
            StringBuffer hexString = new StringBuffer();
            byte[] hash = md.digest();
            for (byte b : hash) {
                if ((0xFF & b) < 0x10)
                    hexString.append("0" + Integer.toHexString((0xFF & b)));
                else
                    hexString.append(Integer.toHexString(0xFF & b));
            }
            out = hexString.toString();
            if (out.length() > 64)
                return out.substring(0, 64);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return out;
    }

    public boolean isAuthenticated(String token) {
        if (token == null)
            return false;
        else
            return tokens.contains(token);
    }

    void loadTokens() throws Exception {
        if (tokenFile != null) {
            synchronized (tokenLock) {
                String token = null;
                BufferedReader br;
                try {
                    br = new BufferedReader(new InputStreamReader(new FileInputStream(tokenFile)));
                    br.readLine();
                    while ((token = br.readLine()) != null)
                        tokens.add(getHash64(token + passphrase));
                    br.close();
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    String createToken() throws Exception {
        if (tokenFile != null) {
            String token = new BigInteger(256, new SecureRandom()).toString(16);
            MessageDigest md;
            try {
                md = MessageDigest.getInstance("SHA-256");
                md.update(token.getBytes("UTF-8"));
                token = new BigInteger(1, md.digest()).toString(16);
                if (token.length() > 64)
                    token = token.substring(0, 64);
            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
                throw new Exception("error generating token");
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
                throw new Exception("error generating token");
            }
            synchronized (tokenLock) {
                unauthorizedTokens.put(getHash64(token + passphrase), token);
                return token;
            }
        } else
            throw new Exception("no tokens available");
    }

    void authorizeToken(String token) throws Exception {
        if (tokenFile != null) {
            synchronized (tokenLock) {
                if (unauthorizedTokens.containsKey(token)) {
                    PrintWriter pw;
                    try {
                        pw = new PrintWriter(new FileOutputStream(tokenFile, true));
                        pw.println(unauthorizedTokens.get(token));
                        pw.close();
                        tokens.add(getHash64(unauthorizedTokens.get(token) + passphrase));
                    } catch (FileNotFoundException e) {
                        e.printStackTrace();
                        throw new Exception("error storing token");
                    }
                    unauthorizedTokens.remove(token);
                }
            }
        } else
            throw new Exception("no tokens available");
    }

    boolean queueRequest(String request) {
        boolean queued = false;
        if (sQueueFile != null) {
            synchronized (sQueueLock) {
                PrintWriter pw;
                try {
                    pw = new PrintWriter(new FileOutputStream(sQueueFile, true));
                    pw.println(request);
                    pw.close();
                    queued = true;
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                }
            }
            if (queued)
                startQueueThread();
        }
        return queued;
    }

    String dequeueRequest() {
        String request = null;
        synchronized (sQueueLock) {
            BufferedReader br;
            try {
                br = new BufferedReader(new InputStreamReader(new FileInputStream(sQueueFile)));
                request = br.readLine();
                br.close();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return request;
        }
    }

    void requeueRequest(String request) {
        ArrayList<String> requests = new ArrayList<String>();
        synchronized (sQueueLock) {
            BufferedReader br;
            try {
                br = new BufferedReader(new InputStreamReader(new FileInputStream(sQueueFile)));
                br.readLine();
                while ((request = br.readLine()) != null) {
                    requests.add(request);
                }
                br.close();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        for (String r : requests)
            queueRequest(r);
    }

    void startQueueThread() {
        if (sQueueFile != null) {
            synchronized (sQueueLock) {
                if (sQueueThread == null) {
                    sQueueThread = new QueueThread(this, mQueueRetryInterval);
                    sQueueThread.start();
                }
            }
        }
    }

    boolean stopQueueThread() {
        synchronized (sQueueLock) {
            if (sQueueThread != null) {
                sQueueThread.shutdown();
                sQueueThread = null;
                return true;
            } else
                return false;
        }
    }
}