/******************************************************************************* * Copyright (c) 2013 hangum. * All rights reserved. This program and the accompanying materials * are made available under the terms of the GNU Lesser Public License v2.1 * which accompanies this distribution, and is available at * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html * * Contributors: * hangum - initial API and implementation ******************************************************************************/ package com.hangum.tadpole.engine.manager; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; import javax.sql.DataSource; import org.apache.commons.dbcp.BasicDataSource; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import com.hangum.tadpole.commons.exception.TadpoleSQLManagerException; import com.hangum.tadpole.commons.libs.core.define.PublicTadpoleDefine; import com.hangum.tadpole.commons.util.LoadConfigFile; import com.hangum.tadpole.db.metadata.TadpoleMetaData; import com.hangum.tadpole.db.metadata.constants.PostgreSQLConstant; import com.hangum.tadpole.db.metadata.constants.SQLConstants; import com.hangum.tadpole.engine.define.DBDefine; import com.hangum.tadpole.engine.define.DBGroupDefine; import com.hangum.tadpole.engine.manager.internal.map.SQLMap; import com.hangum.tadpole.engine.query.dao.system.UserDBDAO; import com.hangum.tadpole.session.manager.SessionManager; import com.ibatis.sqlmap.client.SqlMapClient; /** * DB Manager에서 관리하는 디비에 관해서 정의한다. * * @author hangum * */ public class TadpoleSQLManager extends AbstractTadpoleManager { private static final Logger logger = Logger.getLogger(TadpoleSQLManager.class); /** db 인스턴스를 가지고 있는 아이 */ private static Map<String, SqlMapClient> dbManager = null; /** db의 메타데이터를 가지고 있다 */ private static Map<String, TadpoleMetaData> dbMetadata = null; /** dbManager 의 키를 가지고 있는 친구 - logout시에 키를 사용하여 인스턴스를 삭제하기 위해 */ private static Map<String, List<String>> managerKey = null; /** password manager */ private static Map<String, String> pwdManager = null; private static TadpoleSQLManager tadpoleSQLManager = null; private static boolean isGatewayConnection = false; private static boolean isGateWayIDCheck = false; static { if(tadpoleSQLManager == null) { tadpoleSQLManager = new TadpoleSQLManager(); dbManager = new HashMap<String, SqlMapClient>(); dbMetadata = new HashMap<String, TadpoleMetaData>(); managerKey = new HashMap<String, List<String>>(); pwdManager = new HashMap<String, String>(); isGatewayConnection = LoadConfigFile.isEngineGateway(); isGateWayIDCheck = LoadConfigFile.isGateWayIDCheck(); } } private TadpoleSQLManager() {} /** * <pre> * DB 정보를 생성한다. * * 엔진환경에서 가지고 있어야 하는 것으로서 데이터베이스 부가정보를 가지고 있어야 할듯합니다. * 테이블의 "나 '가 대소문자 유무등을 환경정보로가지고 있어야겟습니다. * * </pre> * * @param userDB * @return * @throws Exception */ public static SqlMapClient getInstance(final UserDBDAO userDB) throws TadpoleSQLManagerException { SqlMapClient sqlMapClient = null; Connection conn = null; final String searchKey = getKey(userDB); final String pwdCacheKey = getDBPasswdKey(userDB); try { sqlMapClient = dbManager.get( searchKey ); if(sqlMapClient == null) { sqlMapClient = dbManager.get(searchKey); if(sqlMapClient != null) return sqlMapClient; // if(logger.isDebugEnabled()) logger.debug("==[search key]=============================> " + searchKey); // oracle 일 경우 locale 설정 try { if(DBGroupDefine.ORACLE_GROUP == userDB.getDBGroup()) { DriverManager.setLoginTimeout(10); if(userDB.getLocale() != null && !"".equals(userDB.getLocale())) { Locale.setDefault(new Locale(userDB.getLocale())); } } } catch(Exception e) { logger.error(String.format("set locale error: %s", e.getMessage())); } // gate way 서버에 연결하려는 디비 정보가 있는지 if(isGatewayConnection && userDB.getDBDefine() != DBDefine.TADPOLE_SYSTEM_MYSQL_DEFAULT) { final UserDBDAO gatawayUserDB = (UserDBDAO)userDB.clone(); TDBGatewayManager.makeGatewayServer(gatawayUserDB, isGateWayIDCheck); sqlMapClient = SQLMap.getInstance(gatawayUserDB); } else { sqlMapClient = SQLMap.getInstance(userDB); } dbManager.put(searchKey, sqlMapClient); List<String> listSearchKey = managerKey.get(userDB.getTdbUserID()); if(listSearchKey == null) { listSearchKey = new ArrayList<String>(); listSearchKey.add(searchKey); managerKey.put(userDB.getTdbUserID(), listSearchKey); } else { listSearchKey.add(searchKey); } // cache에서 사용하기 위해 패스워드를 기록해 놓는다. pwdManager.put(pwdCacheKey, userDB.getPasswd()); // metadata를 가져와서 저장해 놓습니다. conn = sqlMapClient.getDataSource().getConnection(); // connection initialize setConnectionInitialize(userDB, conn); // don't belive keyword. --;; initializeConnection(searchKey, userDB, conn.getMetaData()); } } catch(Exception e) { logger.error("***** get DB Instance seq is " + userDB.getSeq() + "\n" , e); managerKey.remove(userDB.getTdbUserID()); dbManager.remove(searchKey); pwdManager.remove(pwdCacheKey); throw new TadpoleSQLManagerException(e); } finally { if(conn != null) try {conn.close();} catch(Exception e) {} } return sqlMapClient; } /** * 사용자 커넥션을 얻는다. * * @param userDB * @return * @throws TadpoleSQLManagerException * @throws SQLException */ public static Connection getConnection(final UserDBDAO userDB) throws TadpoleSQLManagerException, SQLException { Connection javaConn = getInstance(userDB).getDataSource().getConnection(); Statement statement = null; try { if(userDB.getDBGroup() == DBGroupDefine.MYSQL_GROUP) { if("".equals(userDB.getSchema())) userDB.setSchema(userDB.getDb()); if(logger.isDebugEnabled()) logger.debug(String.format("**** set define schema %s ", userDB.getSchema())); statement = javaConn.createStatement(); statement.executeUpdate(String.format("use `%s`", userDB.getSchema())); } else if(userDB.getDBGroup() == DBGroupDefine.ORACLE_GROUP) { if("".equals(userDB.getSchema())) userDB.setSchema(userDB.getDb()); if(logger.isDebugEnabled()) logger.debug(String.format("**** set define schema %s ", userDB.getSchema())); statement = javaConn.createStatement(); statement.executeUpdate(String.format("ALTER SESSION SET CURRENT_SCHEMA = %s", userDB.getSchema())); } else if(userDB.getDBGroup() == DBGroupDefine.POSTGRE_GROUP) { if("".equals(userDB.getSchema())) userDB.setSchema(userDB.getDb()); if(logger.isDebugEnabled()) logger.debug(String.format("**** set define schema %s ", userDB.getSchema())); statement = javaConn.createStatement(); statement.executeUpdate(String.format("set schema '%s'", userDB.getSchema())); } } catch(Exception e) { logger.error("change scheman ", e); throw new SQLException(e); } finally { if(statement != null) statement.close(); } return javaConn; } /** * 각 DB의 metadata를 넘겨줍니다. * * @param searchKey * @param userDB * @param dbMetadata * @return */ public static void initializeConnection(String searchKey, final UserDBDAO userDB, DatabaseMetaData metaData) throws Exception { // 엔진디비는 메타데이터를 저장하지 않는다. if(userDB.getDBDefine() == DBDefine.TADPOLE_SYSTEM_DEFAULT || userDB.getDBDefine() == DBDefine.TADPOLE_SYSTEM_MYSQL_DEFAULT) return; String strIdentifierQuoteString = ""; try { strIdentifierQuoteString = StringUtils.stripToEmpty(metaData.getIdentifierQuoteString()); } catch(Exception e) { // ignore exception, not support quoteString } // https://github.com/hangum/TadpoleForDBTools/issues/412 디비의 메타데이터가 틀려서 설정하였습니다. TadpoleMetaData tadpoleMetaData = null; switch ( userDB.getDBDefine() ) { case ORACLE_DEFAULT: case TIBERO_DEFAULT: tadpoleMetaData = new TadpoleMetaData(strIdentifierQuoteString, TadpoleMetaData.STORES_FIELD_TYPE.LOWCASE_BLANK); break; case MSSQL_DEFAULT: case MSSQL_8_LE_DEFAULT: case MYSQL_DEFAULT: case MARIADB_DEFAULT: case SQLite_DEFAULT: tadpoleMetaData = new TadpoleMetaData(strIdentifierQuoteString, TadpoleMetaData.STORES_FIELD_TYPE.BLANK); break; case POSTGRE_DEFAULT: case TAJO_DEFAULT: tadpoleMetaData = new TadpoleMetaData(strIdentifierQuoteString, TadpoleMetaData.STORES_FIELD_TYPE.UPPERCASE_BLANK); break; default: tadpoleMetaData = new TadpoleMetaData(strIdentifierQuoteString, TadpoleMetaData.STORES_FIELD_TYPE.NONE); } // set keyword if(userDB.getDBGroup() == DBGroupDefine.SQLITE_GROUP) { // not support keyword http://sqlite.org/lang_keywords.html tadpoleMetaData.setKeywords(StringUtils.join(SQLConstants.QUOTE_SQLITE_KEYWORDS, ",")); } else if(userDB.getDBGroup() == DBGroupDefine.MYSQL_GROUP || userDB.getDBGroup() == DBGroupDefine.ORACLE_GROUP) { String strFullKeywords = StringUtils.join(SQLConstants.QUOTE_MYSQL_KEYWORDS, ",") + "," + dbMetadata; tadpoleMetaData.setKeywords(strFullKeywords); } else if(userDB.getDBDefine() == DBDefine.MONGODB_DEFAULT) { // not support this method tadpoleMetaData.setKeywords(""); } else if(userDB.getDBGroup() == DBGroupDefine.MSSQL_GROUP) { String strFullKeywords = StringUtils.join(SQLConstants.QUOTE_MSSQL_KEYWORDS, ",") + "," + metaData.getSQLKeywords(); tadpoleMetaData.setKeywords(strFullKeywords); } else if(userDB.getDBGroup() == DBGroupDefine.POSTGRE_GROUP) { String strFullKeywords = StringUtils.join(PostgreSQLConstant.QUOTE_POSTGRES_KEYWORDS, ",") + "," + metaData.getSQLKeywords(); tadpoleMetaData.setKeywords(strFullKeywords); } else { tadpoleMetaData.setKeywords(metaData.getSQLKeywords()); } tadpoleMetaData.setDbMajorVersion(metaData.getDatabaseMajorVersion()); tadpoleMetaData.setMinorVersion(metaData.getDatabaseMinorVersion()); dbMetadata.put(searchKey, tadpoleMetaData); } /** * 현재 연결된 Connection pool 정보를 리턴합니다. * * @return */ public static Map<String, SqlMapClient> getDbManager() { return dbManager; } /** * dbcp pool info * @param isAdmin * * @return */ public static List<DBCPInfoDAO> getDBCPInfo(boolean isAdmin) { List<DBCPInfoDAO> listDbcpInfo = new ArrayList<DBCPInfoDAO>(); final String strLoginEmail = SessionManager.getEMAIL(); Set<String> setKeys = getDbManager().keySet(); for (String stKey : setKeys) { String strArryKey[] = StringUtils.splitByWholeSeparator(stKey, PublicTadpoleDefine.DELIMITER); // 시스템 디비는 보여주지 않습니다. if(StringUtils.equals(PublicTadpoleDefine.USER_ROLE_TYPE.SYSTEM_ADMIN.name(), strArryKey[0])) continue; // 어드민 만이 모두 호출한다. if(!isAdmin) if(!StringUtils.equals(strLoginEmail, strArryKey[0])) continue; // add connection information SqlMapClient sqlMap = dbManager.get(stKey); DataSource ds = sqlMap.getDataSource(); BasicDataSource bds = (BasicDataSource)ds; DBCPInfoDAO dao = new DBCPInfoDAO(); dao.setEngineKey(stKey); dao.setUser(strArryKey[0]); dao.setDbSeq(Integer.parseInt(strArryKey[1])); dao.setDbType(strArryKey[2]); dao.setDisplayName(strArryKey[3]); dao.setNumberActive(bds.getNumActive()); dao.setMaxActive(bds.getMaxActive()); dao.setNumberIdle(bds.getNumIdle()); dao.setMaxWait(bds.getMaxWait()); listDbcpInfo.add(dao); } return listDbcpInfo; } /** * DBMetadata * @return */ public static TadpoleMetaData getDbMetadata(final UserDBDAO dbInfo) { return dbMetadata.get(getKey(dbInfo)); } /** * 사용자의 모든 인스턴스를 삭제한다. */ public static void removeAllInstance(String id) { final List<String> listKeyMap = managerKey.remove(id); if(listKeyMap == null) return; for (String searchKey : listKeyMap) { removeInstance(searchKey); } } /** * DB 정보를 삭제한다. * * @param dbInfo */ public static void removeInstance(UserDBDAO dbInfo) { String key = getKey(dbInfo); removeInstance(key); } /** * remove instance * @param searchKey */ public static void removeInstance(String searchKey) { TadpoleMetaData metaData = dbMetadata.remove(searchKey); metaData = null; SqlMapClient sqlMapClient = null; try { sqlMapClient = dbManager.remove(searchKey); if(sqlMapClient == null) return; DataSource ds = sqlMapClient.getDataSource(); if(ds != null) { // if(logger.isDebugEnabled()) logger.debug("\t #### [TadpoleSQLManager] remove Instance: " + searchKey); BasicDataSource basicDataSource = (BasicDataSource)ds; basicDataSource.close(); basicDataSource= null; ds = null; } } catch (Exception e) { logger.error("remove connection", e); } finally { sqlMapClient = null; } } /** * map의 카를 가져옵니다. * @param userDB * @return */ public static String getKey(final UserDBDAO userDB) { return userDB.getTdbUserID() + PublicTadpoleDefine.DELIMITER + userDB.getSeq() + PublicTadpoleDefine.DELIMITER + userDB.getDbms_type() + PublicTadpoleDefine.DELIMITER + userDB.getDisplay_name()+ PublicTadpoleDefine.DELIMITER + userDB.getUrl() + PublicTadpoleDefine.DELIMITER + userDB.getUsers() + PublicTadpoleDefine.DELIMITER; } /** * db password 의 키를 가지고 있는다. * * @param userDB * @return */ public static String getDBPasswdKey(final UserDBDAO userDB) { return userDB.getDbms_type() + PublicTadpoleDefine.DELIMITER + userDB.getUrl() + PublicTadpoleDefine.DELIMITER; } /** * cache password * * @param userDB * @return */ public static String getPassword(final UserDBDAO userDB) { return pwdManager.get(getDBPasswdKey(userDB)); } /** * 패스워드가 틀렸을 경우 패스워드를 초기화 한다. * @param userDB * @return */ public static String removePassword(final UserDBDAO userDB) { return pwdManager.remove(getDBPasswdKey(userDB)); } }