/* * Copyright 2010 NCHOVY * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.krakenapps.sentry.impl; import java.net.InetSocketAddress; import java.util.ArrayList; import java.util.Collection; import java.util.List; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.TrustManagerFactory; import org.apache.felix.ipojo.annotations.Component; import org.apache.felix.ipojo.annotations.Invalidate; import org.apache.felix.ipojo.annotations.Provides; import org.apache.felix.ipojo.annotations.Requires; import org.apache.felix.ipojo.annotations.Validate; import org.krakenapps.api.KeyStoreManager; import org.krakenapps.rpc.RpcAgent; import org.krakenapps.rpc.RpcConnection; import org.krakenapps.rpc.RpcConnectionProperties; import org.krakenapps.rpc.RpcSession; import org.krakenapps.sentry.Base; import org.krakenapps.sentry.ConnectionWatchdog; import org.krakenapps.sentry.Sentry; import org.krakenapps.sentry.SentryRpcService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @Component(name = "sentry-connection-watchdog") @Provides public class ConnectionWatchdogImpl implements ConnectionWatchdog, Runnable { private final Logger logger = LoggerFactory.getLogger(ConnectionWatchdogImpl.class.getName()); private volatile boolean stop = false; private volatile boolean stopped = false; private Thread t; @Requires private SentryRpcService sentryRpcService; @Requires private RpcAgent rpc; @Requires private Sentry sentry; @Requires private KeyStoreManager keyStoreManager; @Validate @Override public void start() { t = new Thread(this, "Sentry Connection Watchdog"); t.start(); } @Invalidate @Override public boolean stop() { stop = true; t.interrupt(); // wait for thread stop for (int i = 0; i < 10; i++) { try { if (stopped) break; Thread.sleep(500); } catch (InterruptedException e) { } } if (!stopped) logger.error("kraken-sentry: watchdog thread didn't stop in 5seconds, give up."); return stopped; } @Override public void run() { int i = 0; try { while (!stop) { // check every 10secs. if ((i % 20) == 0) checkNow(); Thread.sleep(500); i++; } } catch (InterruptedException e) { logger.info("kraken-sentry: watchdog stopped"); } // clear flags stop = false; stopped = true; } @Override public void checkNow() { try { logger.debug("kraken-sentry: checking sentry connections"); if (sentry.getGuid() == null) { logger.trace("kraken-sentry: set guid first"); return; } checkConnections(); } catch (Exception e) { logger.error("kraken-sentry: watchdog check failed", e); } } private void checkConnections() { Collection<String> names = sentry.getCommandSessionNames(); // live connection check for (Base base : sentry.getBases()) { String name = base.getName(); if (names.contains(name)) { RpcSession commandSession = sentry.getCommandSession(name); RpcConnection conn = commandSession.getConnection(); // if connection is closed, remove it and reconnect. if (!conn.isOpen()) { sentry.removeCommandSession(name); connect(sentry, base); } } else { try { connect(sentry, base); } catch (Exception e) { logger.warn("kraken-sentry: cannot connect to base [{}]", base.getName()); } } } // dead connection check (just removed by admin) List<String> removeTargets = new ArrayList<String>(); for (String name : names) { Base found = null; for (Base base : sentry.getBases()) { if (base.getName().equals(name)) { found = base; break; } } // it should be removed if (found == null) { removeTargets.add(name); } } for (String target : removeTargets) { RpcSession commandSession = sentry.removeCommandSession(target); if (commandSession != null) { commandSession.getConnection().close(); } } } private void connect(Sentry sentry, Base base) { RpcConnection connection = null; try { InetSocketAddress endpoint = base.getAddress(); KeyManagerFactory kmf = keyStoreManager.getKeyManagerFactory("rpc-agent", "SunX509"); TrustManagerFactory tmf = keyStoreManager.getTrustManagerFactory("rpc-ca", "SunX509"); RpcConnectionProperties props = new RpcConnectionProperties(endpoint, kmf, tmf); connection = rpc.connectSsl(props); if (connection == null) { logger.warn("kraken-sentry: connect failed to [{}]", base.getAddress()); return; } // bind sentry service, and connect to base service. connection.bind("kraken-sentry", sentryRpcService); RpcSession commandSession = connection.createSession("kraken-base"); sentry.addCommandSession(base.getName(), commandSession); commandSession.call("hello", new Object[] { sentry.getGuid() }, 5000); logger.info("kraken-sentry: new sentry connection [{}] to base [{}]", connection, base.getName()); } catch (Exception e) { logger.info("kraken-sentry: failed to connect, closing connection", e); if (connection != null) connection.close(); } } }