diff --git a/utils/src/com/cloud/utils/db/ConnectionConcierge.java b/utils/src/com/cloud/utils/db/ConnectionConcierge.java new file mode 100644 index 00000000000..05755321750 --- /dev/null +++ b/utils/src/com/cloud/utils/db/ConnectionConcierge.java @@ -0,0 +1,217 @@ +/** + * Copyright (C) 2010 Cloud.com, Inc. All rights reserved. + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It 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 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 . + * + */ +package com.cloud.utils.db; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import javax.management.StandardMBean; + +import org.apache.log4j.Logger; + +import com.cloud.utils.concurrency.NamedThreadFactory; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.mgmt.JmxUtil; + +/** + * ConnectionConcierge keeps stand alone database connections alive. This is + * useful when the code needs to keep a database connection for itself and + * needs someone to keep that database connection from being garbage collected + * because the connection has been idled for a long time. + * + * ConnectionConierge also has JMX hooks to allow for connections to be reset + * and validated on a live system so using it means you don't need to implement + * your own. + */ +public class ConnectionConcierge { + + static final Logger s_logger = Logger.getLogger(ConnectionConcierge.class); + + static final ConnectionConciergeManager s_mgr = new ConnectionConciergeManager(); + + Connection _conn; + String _name; + boolean _keepAlive; + boolean _autoCommit; + + public ConnectionConcierge(String name, Connection conn, boolean autoCommit, boolean keepAlive) { + _name = name + s_mgr.getNextId(); + _keepAlive = keepAlive; + _autoCommit = autoCommit; + reset(conn); + } + + public void reset(Connection conn) { + try { + release(); + } catch (Throwable th) { + s_logger.error("Unable to release a connection", th); + } + _conn = conn; + try { + _conn.setAutoCommit(_autoCommit); + } catch (SQLException e) { + s_logger.error("Unable to release a connection", e); + } + s_mgr.register(_name, this); + s_logger.debug("Registering a database connection for " + _name); + } + + public final Connection conn() { + return _conn; + } + + public void release() { + s_mgr.unregister(_name); + try { + if (_conn != null) { + _conn.close(); + } + _conn = null; + } catch (SQLException e) { + throw new CloudRuntimeException("Problem in closing a connection", e); + } + } + + @Override + protected void finalize() throws Exception { + if (_conn != null) { + release(); + } + } + + public boolean keepAlive() { + return _keepAlive; + } + + protected static class ConnectionConciergeManager extends StandardMBean implements ConnectionConciergeMBean { + ScheduledExecutorService _executor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("ConnectionKeeper")); + final ConcurrentHashMap _conns = new ConcurrentHashMap(); + final AtomicInteger _idGenerator = new AtomicInteger(); + + ConnectionConciergeManager() { + super(ConnectionConciergeMBean.class, false); + resetKeepAliveTask(20); + try { + JmxUtil.registerMBean("ConnectionConciergeManager", "ConnectionConciergeManager", this); + } catch (Exception e) { + s_logger.error("Unable to register mbean", e); + } + } + + public Integer getNextId() { + return _idGenerator.incrementAndGet(); + } + + public void register(String name, ConnectionConcierge concierge) { + _conns.put(name, concierge); + } + + public void unregister(String name) { + _conns.remove(name); + } + + protected String testValidity(String name, Connection conn) { + PreparedStatement pstmt = null; + try { + if (conn != null) { + pstmt = conn.prepareStatement("SELECT 1"); + pstmt.executeQuery(); + } + return null; + } catch (Throwable th) { + s_logger.error("Unable to keep the db connection for " + name, th); + return th.toString(); + } finally { + if (pstmt != null) { + try { + pstmt.close(); + } catch (SQLException e) { + } + } + } + } + + @Override + public List testValidityOfConnections() { + ArrayList results = new ArrayList(_conns.size()); + for (Map.Entry entry : _conns.entrySet()) { + String result = testValidity(entry.getKey(), entry.getValue().conn()); + results.add(entry.getKey() + "=" + (result == null ? "OK" : result)); + } + return results; + } + + @Override + public String resetConnection(String name) { + ConnectionConcierge concierge = _conns.get(name); + if (concierge == null) { + return "Not Found"; + } + + Connection conn = Transaction.getStandaloneConnection(); + if (conn == null) { + return "Unable to get anotehr db connection"; + } + + concierge.reset(conn); + return "Done"; + } + + @Override + public String resetKeepAliveTask(int seconds) { + if (_executor != null) { + try { + _executor.shutdown(); + } catch(Exception e) { + s_logger.error("Unable to shutdown executor", e); + } + } + + _executor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("ConnectionConcierge")); + _executor.schedule(new Runnable() { + @Override + public void run() { + s_logger.trace("connection concierge keep alive task"); + for (Map.Entry entry : _conns.entrySet()) { + ConnectionConcierge concierge = entry.getValue(); + if (concierge.keepAlive()) { + testValidity(entry.getKey(), entry.getValue().conn()); + } + } + } + }, seconds, TimeUnit.SECONDS); + + return "As you wish."; + } + + @Override + public List getConnectionsNotPooled() { + return new ArrayList(_conns.keySet()); + } + } +} diff --git a/utils/src/com/cloud/utils/db/ConnectionConciergeMBean.java b/utils/src/com/cloud/utils/db/ConnectionConciergeMBean.java new file mode 100644 index 00000000000..c6c40410bb4 --- /dev/null +++ b/utils/src/com/cloud/utils/db/ConnectionConciergeMBean.java @@ -0,0 +1,31 @@ +/** + * Copyright (C) 2010 Cloud.com, Inc. All rights reserved. + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It 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 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 . + * + */ +package com.cloud.utils.db; + +import java.util.List; + +public interface ConnectionConciergeMBean { + + List testValidityOfConnections(); + + String resetConnection(String name); + + String resetKeepAliveTask(int seconds); + + List getConnectionsNotPooled(); +}