binky.reportrunner.service.impl.DatasourceServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for binky.reportrunner.service.impl.DatasourceServiceImpl.java

Source

/*******************************************************************************
 * Copyright (c) 2009 Daniel Grout.
 * 
 * GNU GENERAL PUBLIC LICENSE - Version 3
 * 
 * This file is part of Report Runner (http://code.google.com/p/reportrunner).
 * 
 * Report Runner 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.
 * 
 * Report Runner 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 Report Runner. If not, see <http://www.gnu.org/licenses/>.
 * 
 * Module: DatasourceServiceImpl.java
 ******************************************************************************/
package binky.reportrunner.service.impl;

import java.beans.PropertyVetoException;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;

import org.apache.commons.dbcp.BasicDataSource;
import org.apache.commons.digester.Digester;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.xml.sax.SAXException;

import binky.dan.utils.encryption.EncryptionException;
import binky.reportrunner.dao.ReportRunnerDao;
import binky.reportrunner.data.RunnerDataSource;
import binky.reportrunner.data.RunnerGroup;
import binky.reportrunner.service.DatasourceService;
import binky.reportrunner.service.misc.JDBCDriverDefinition;
import binky.reportrunner.service.misc.JDBCDrivers;
import binky.reportrunner.util.EncryptionUtil;

import com.googlecode.ehcache.annotations.Cacheable;
import com.googlecode.ehcache.annotations.TriggersRemove;

public class DatasourceServiceImpl implements DatasourceService {

    private ReportRunnerDao<RunnerDataSource, String> dataSourceDao;
    private ReportRunnerDao<RunnerGroup, String> groupDao;
    private Map<String, DataSource> dataSources = new HashMap<String, DataSource>();;
    private String secureKey;

    private Logger logger = Logger.getLogger(DatasourceServiceImpl.class);

    private DataSource getDs(RunnerDataSource runnerDs)
            throws SecurityException, InstantiationException, IllegalAccessException, ClassNotFoundException,
            PropertyVetoException, NamingException, EncryptionException {

        final String jndiDataSource = runnerDs.getJndiName();

        if (StringUtils.isBlank(jndiDataSource)) {
            EncryptionUtil enc = new EncryptionUtil();
            logger.info("using dbcp pooled connection for: " + runnerDs.getDataSourceName());

            String jdbcUser = runnerDs.getUsername();
            if (StringUtils.isBlank(runnerDs.getPassword()))
                throw new SecurityException("password is empty");
            String jdbcPassword = enc.decrpyt(secureKey, runnerDs.getPassword());

            String jdbcUrl = runnerDs.getJdbcUrl();
            String databaseDriver = runnerDs.getJdbcClass();

            Class.forName(databaseDriver).newInstance();

            BasicDataSource ds1 = new BasicDataSource();
            ds1.setDriverClassName(databaseDriver);
            ds1.setUrl(jdbcUrl);
            ds1.setUsername(jdbcUser);
            ds1.setPassword(jdbcPassword);
            ds1.setInitialSize(runnerDs.getInitialPoolSize());
            ds1.setMaxActive(runnerDs.getMaxPoolSize());

            ds1.setRemoveAbandoned(true);
            ds1.setRemoveAbandonedTimeout(600);

            // do not want anything updating anything
            ds1.setDefaultReadOnly(true);

            ds1.setLogAbandoned(true);
            ds1.setTestOnBorrow(true);
            ds1.setTestOnReturn(true);
            ds1.setTestWhileIdle(true);

            // does this work across all RBMS? - no it doesn't
            //ds1.setValidationQuery("select 1");
            //ds1.setValidationQueryTimeout(300);

            return ds1;
        } else {
            logger.info(
                    "getting datasource from JNDI url: " + jndiDataSource + " for " + runnerDs.getDataSourceName());
            Context initContext = new InitialContext();
            DataSource ds = (DataSource) initContext.lookup("java:/comp/env/" + jndiDataSource);
            return ds;
        }
    }

    public void purgeConnections(String dataSourceName) throws SQLException {
        DataSource ds = dataSources.get(dataSourceName);
        if (ds != null && ds instanceof BasicDataSource) {
            dumpLogInfo(dataSourceName);
            // reset the datasource
            ((BasicDataSource) ds).close();
        }
    }

    private void dumpLogInfo(String dsName) throws SQLException {
        // dump out a shed load of info about the datasource
        logger.debug("Datasource info for " + dsName);
    }

    @Override
    public String testDataSource(RunnerDataSource runnerDs) {

        try {
            //fix for issue 105 - when not editing password but just testing
            if (runnerDs.getPassword() == null || runnerDs.getPassword().trim().isEmpty()) {
                // see if ds already exists but we are hiding the password
                RunnerDataSource pwget = this.dataSourceDao.get(runnerDs.getDataSourceName());
                if (pwget != null) {
                    logger.debug("supplied password was blank - using stored password (if any)");
                    runnerDs.setPassword(pwget.getPassword());
                }
            } else {
                EncryptionUtil enc = new EncryptionUtil();
                runnerDs.setPassword(enc.encrpyt(this.secureKey, runnerDs.getPassword()));
            }

            DataSource ds = this.getDs(runnerDs);
            Connection conn = ds.getConnection();
            DatabaseMetaData meta = conn.getMetaData();
            String information = meta.getDatabaseProductName() + ", " + meta.getDatabaseProductVersion();
            conn.close();
            if (ds instanceof BasicDataSource) {
                ((BasicDataSource) ds).close();
            }
            return information;
        } catch (Exception e) {
            if (e instanceof NullPointerException) {
                logger.fatal(e.getMessage(), e);
            }
            logger.debug(e.getMessage());
            return "ERROR - " + e.getClass().getSimpleName() + ": " + e.getMessage();
        }
    }

    public DataSource getJDBCDataSource(RunnerDataSource runnerDs) throws SQLException {
        if (dataSources == null)
            dataSources = new HashMap<String, DataSource>();
        DataSource ds = dataSources.get(runnerDs.getDataSourceName());
        if (ds == null) {
            try {
                logger.info("datasource not already loaded for: " + runnerDs.getDataSourceName());
                ds = getDs(runnerDs);
                logger.info("Stored datasource: " + runnerDs.getDataSourceName());
                dataSources.put(runnerDs.getDataSourceName(), ds);

                dumpLogInfo(runnerDs.getDataSourceName());

            } catch (Exception e) {
                logger.fatal("Unable to create datasource: " + runnerDs.getDataSourceName(), e);
            }
        }

        return ds;
    }

    public void setDataSourceDao(ReportRunnerDao<RunnerDataSource, String> dataSourceDao) {
        this.dataSourceDao = dataSourceDao;
    }

    @TriggersRemove(cacheName = "dataSourceCache")
    public void deleteDataSource(String dataSourceName) {
        if (dataSources.get(dataSourceName) != null) {
            dataSources.remove(dataSourceName);
        }
        dataSourceDao.delete(dataSourceName);

    }

    public RunnerDataSource getDataSource(String dataSourceName) {
        RunnerDataSource ds = dataSourceDao.get(dataSourceName);
        return ds;
    }

    @Cacheable(cacheName = "dataSourceCache")
    public List<RunnerDataSource> listDataSources() {
        return dataSourceDao.getAll();
    }

    @TriggersRemove(cacheName = "dataSourceCache", removeAll = true)
    public void saveUpdateDataSource(RunnerDataSource dataSource) throws EncryptionException {
        if (dataSources.get(dataSource.getDataSourceName()) != null) {
            dataSources.remove(dataSource.getDataSourceName());
        }
        EncryptionUtil enc = new EncryptionUtil();
        if (StringUtils.isNotBlank(dataSource.getPassword())) {
            dataSource.setPassword(enc.encrpyt(secureKey, dataSource.getPassword()));
        } else {
            RunnerDataSource ds = dataSourceDao.get(dataSource.getDataSourceName());
            if (ds != null)
                dataSource.setPassword(ds.getPassword());
        }

        dataSourceDao.saveOrUpdate(dataSource);
    }

    public void setSecureKey(String secureKey) {
        this.secureKey = secureKey;
    }

    public JDBCDrivers getJDBCDriverDefinitions() throws IOException, SAXException {

        InputStream in = DatasourceServiceImpl.class.getResourceAsStream("/jdbcDrivers.xml");

        Digester digester = new Digester();
        digester.setValidating(false);
        digester.addObjectCreate("jdbcDrivers", JDBCDrivers.class);
        digester.addObjectCreate("jdbcDrivers/driver", JDBCDriverDefinition.class);
        digester.addBeanPropertySetter("jdbcDrivers/driver/label", "label");
        digester.addBeanPropertySetter("jdbcDrivers/driver/url", "url");
        digester.addBeanPropertySetter("jdbcDrivers/driver/driverName", "driverName");
        digester.addSetNext("jdbcDrivers/driver", "addDefinition");

        JDBCDrivers drivers = (JDBCDrivers) digester.parse(in);

        return drivers;
    }

    @Override
    public List<RunnerDataSource> getDataSourcesForGroup(String groupName) {
        logger.debug("getting datasources for group: " + groupName);
        // List<RunnerDataSource> dsList =
        // groupDao.get(groupName).getDataSources();

        // some crazy ass shit going down here - the join returns an array for
        // each row that would be returned by the db
        // if i try prefixing the names query with select d (which makes sense
        // to me) then it won't run
        List<RunnerDataSource> dsList = new LinkedList<RunnerDataSource>();
        Object holder = dataSourceDao.findByNamedQuery("findAllForGroup", new String[] { groupName });
        for (Object[] o : (ArrayList<Object[]>) holder) {
            RunnerDataSource d = (RunnerDataSource) o[0];
            logger.debug(d.getDataSourceName());
            dsList.add(d);
        }
        logger.debug("got : " + dsList.size() + " data sources for group: " + groupName);
        return dsList;
    }

    public ReportRunnerDao<RunnerGroup, String> getGroupDao() {
        return groupDao;
    }

    public void setGroupDao(ReportRunnerDao<RunnerGroup, String> groupDao) {
        this.groupDao = groupDao;
    }

    @Override
    public void reEncryptPasswords(String newKey) throws EncryptionException {
        EncryptionUtil enc = new EncryptionUtil();

        logger.warn(
                "re-encrypting the datasource passwords.  I hope you copied the key from the UI as instructed!!");

        for (RunnerDataSource ds : dataSourceDao.getAll()) {
            if (!StringUtils.isEmpty(ds.getPassword())) {
                String pw = enc.decrpyt(secureKey, ds.getPassword());
                ds.setPassword(enc.encrpyt(newKey, pw));
                dataSourceDao.saveOrUpdate(ds);
                logger.debug("updated ds: " + ds.getDataSourceName());
            }
        }

        logger.warn(
                "re-encryption complete.  Please ensure you update the properties file with the new key and restart the server");
    }

}