/******************************************************************************* * This file is part of OpenNMS(R). * * Copyright (C) 2008-2011 The OpenNMS Group, Inc. * OpenNMS(R) is Copyright (C) 1999-2011 The OpenNMS Group, Inc. * * OpenNMS(R) is a registered trademark of The OpenNMS Group, Inc. * * OpenNMS(R) 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. * * OpenNMS(R) 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 OpenNMS(R). If not, see: * http://www.gnu.org/licenses/ * * For more information contact: * OpenNMS(R) Licensing <license@opennms.org> * http://www.opennms.org/ * http://www.opennms.com/ *******************************************************************************/ package org.opennms.netmgt.provision.support; import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import org.apache.mina.core.filterchain.IoFilterAdapter; import org.apache.mina.core.future.ConnectFuture; import org.apache.mina.core.future.IoFutureListener; import org.apache.mina.core.service.IoHandler; import org.apache.mina.core.session.IdleStatus; import org.apache.mina.core.session.IoSession; import org.apache.mina.core.session.IoSessionInitializer; import org.apache.mina.filter.codec.ProtocolCodecFilter; import org.apache.mina.filter.codec.textline.TextLineCodecFactory; import org.apache.mina.filter.logging.LoggingFilter; import org.apache.mina.filter.ssl.SslFilter; import org.opennms.core.utils.InetAddressUtils; import org.opennms.core.utils.LogUtils; import org.opennms.netmgt.provision.DetectFuture; import org.opennms.netmgt.provision.support.trustmanager.RelaxedX509TrustManager; /** * <p>AsyncBasicDetectorMinaImpl class.</p> * * @author Donald Desloge * @version $Id: $ */ public abstract class AsyncBasicDetectorMinaImpl<Request, Response> extends AsyncBasicDetector<Request, Response> { private BaseDetectorHandler<Request, Response> m_detectorHandler = new BaseDetectorHandler<Request, Response>(); private IoFilterAdapter m_filterLogging = null; private ProtocolCodecFilter m_protocolCodecFilter = new ProtocolCodecFilter(new TextLineCodecFactory(CHARSET_UTF8)); private final ConnectionFactory m_connectionFactory; /** * <p>Constructor for AsyncBasicDetector.</p> * * @param serviceName a {@link java.lang.String} object. * @param port a int. * @param <Request> a Request object. * @param <Response> a Response object. */ public AsyncBasicDetectorMinaImpl(final String serviceName, final int port) { super(serviceName, port); m_connectionFactory = ConnectionFactory.getFactory(getTimeout()); } /** * <p>Constructor for AsyncBasicDetector.</p> * * @param serviceName a {@link java.lang.String} object. * @param port a int. * @param timeout a int. * @param retries a int. */ public AsyncBasicDetectorMinaImpl(final String serviceName, final int port, final int timeout, final int retries){ super(serviceName, port, timeout, retries); m_connectionFactory = ConnectionFactory.getFactory(getTimeout()); } /** * <p>dispose</p> */ @Override public void dispose(){ LogUtils.debugf(this, "calling dispose on detector %s", getServiceName()); ConnectionFactory.dispose(m_connectionFactory); } /** {@inheritDoc} */ @Override public final DetectFuture isServiceDetected(final InetAddress address) { final DetectFutureMinaImpl detectFuture = new DetectFutureMinaImpl(this); try { // Set this up here because it can throw an Exception, which we want // to throw now, not in initializeSession final SSLContext c = createClientSSLContext(); // Create an IoSessionInitializer that will configure this individual // session. Previously, all this was done on a new Connector each time // but that was leaking file handles all over the place. This way gives // us per-connection settings without the overhead of creating new // Connectors each time IoSessionInitializer<ConnectFuture> init = new IoSessionInitializer<ConnectFuture>() { public void initializeSession(IoSession session, ConnectFuture future) { // Add filters to the session if(isUseSSLFilter()) { final SslFilter filter = new SslFilter(c); filter.setUseClientMode(true); session.getFilterChain().addFirst("SSL", filter); } session.getFilterChain().addLast( "logger", getLoggingFilter() != null ? getLoggingFilter() : new LoggingFilter() ); session.getFilterChain().addLast( "codec", getProtocolCodecFilter()); session.getConfig().setIdleTime(IdleStatus.READER_IDLE, getIdleTime()); } }; // Start communication final InetSocketAddress socketAddress = new InetSocketAddress(address, getPort()); // Get an ephemeral port on the localhost interface final InetSocketAddress localAddress = new InetSocketAddress(InetAddressUtils.getLocalHostAddress(), 0); final ConnectFuture cf = m_connectionFactory.connect(socketAddress, localAddress, init, createDetectorHandler(detectFuture)); cf.addListener(retryAttemptListener(detectFuture, socketAddress, init, getRetries())); } catch (KeyManagementException e) { detectFuture.setException(e); } catch (NoSuchAlgorithmException e) { detectFuture.setException(e); } catch (Throwable e) { detectFuture.setException(e); } return detectFuture; } /** * @return * @throws NoSuchAlgorithmException * @throws KeyManagementException */ private static final SSLContext createClientSSLContext() throws NoSuchAlgorithmException, KeyManagementException { final TrustManager[] tm = { new RelaxedX509TrustManager() }; final SSLContext sslContext = SSLContext.getInstance("SSL"); sslContext.init(null, tm, new java.security.SecureRandom()); return sslContext; } /** * Handles the retry attempts. Listens to see when the ConnectFuture is finished and checks if there was * an exception thrown. If so, it then attempts a retry if there are more retries. * * @param connector * @param detectFuture * @param address * @param retryAttempt * @return IoFutureListener<ConnectFuture> */ private final IoFutureListener<ConnectFuture> retryAttemptListener(final DetectFutureMinaImpl detectFuture, final InetSocketAddress address, final IoSessionInitializer<ConnectFuture> init, final int retryAttempt) { return new IoFutureListener<ConnectFuture>() { @Override public void operationComplete(ConnectFuture future) { final Throwable cause = future.getException(); if (cause instanceof IOException) { if(retryAttempt == 0) { LogUtils.infof(this, "Service %s detected false",getServiceName()); detectFuture.setServiceDetected(false); }else { LogUtils.infof(this, "Connection exception occurred %s for service %s, retrying attempt %d", cause, getServiceName(), retryAttempt); // Get an ephemeral port on the localhost interface final InetSocketAddress localAddress = new InetSocketAddress(InetAddressUtils.getLocalHostAddress(), 0); future = m_connectionFactory.reConnect(address, localAddress, init, createDetectorHandler(detectFuture)); future.addListener(retryAttemptListener(detectFuture, address, init, retryAttempt - 1)); } }else if(cause instanceof Throwable) { LogUtils.infof(this, cause, "Threw a Throwable and detection is false for service %s", getServiceName()); detectFuture.setServiceDetected(false); } } }; } /** * <p>setDetectorHandler</p> * * @param detectorHandler a {@link org.opennms.netmgt.provision.support.BaseDetectorHandler} object. */ protected final void setDetectorHandler(final BaseDetectorHandler<Request, Response> detectorHandler) { m_detectorHandler = detectorHandler; } /** * <p>createDetectorHandler</p> * * @param future a {@link org.opennms.netmgt.provision.DetectFuture} object. * @return a {@link org.apache.mina.core.service.IoHandler} object. */ protected final IoHandler createDetectorHandler(final DetectFutureMinaImpl future) { m_detectorHandler.setConversation(getConversation()); m_detectorHandler.setFuture(future); return m_detectorHandler; } /** * <p>setLoggingFilter</p> * * @param filterLogging a {@link org.apache.mina.core.filterchain.IoFilterAdapter} object. */ protected final void setLoggingFilter(final IoFilterAdapter filterLogging) { m_filterLogging = filterLogging; } /** * <p>getLoggingFilter</p> * * @return a {@link org.apache.mina.core.filterchain.IoFilterAdapter} object. */ protected final IoFilterAdapter getLoggingFilter() { return m_filterLogging; } /** * <p>setProtocolCodecFilter</p> * * @param protocolCodecFilter a {@link org.apache.mina.filter.codec.ProtocolCodecFilter} object. */ protected final void setProtocolCodecFilter(final ProtocolCodecFilter protocolCodecFilter) { m_protocolCodecFilter = protocolCodecFilter; } /** * <p>getProtocolCodecFilter</p> * * @return a {@link org.apache.mina.filter.codec.ProtocolCodecFilter} object. */ protected final ProtocolCodecFilter getProtocolCodecFilter() { return m_protocolCodecFilter; } /** * <p>getDetectorHandler</p> * * @return a {@link org.apache.mina.core.service.IoHandler} object. */ protected final IoHandler getDetectorHandler() { return m_detectorHandler; } }