/*******************************************************************************
* This file is part of OpenNMS(R).
*
* Copyright (C) 2006-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.capsd;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.lang.reflect.UndeclaredThrowableException;
import java.net.ConnectException;
import java.net.InetAddress;
import java.net.NoRouteToHostException;
import java.net.Socket;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.opennms.core.utils.DefaultSocketWrapper;
import org.opennms.core.utils.ParameterMap;
import org.opennms.core.utils.SocketWrapper;
import org.opennms.core.utils.ThreadCategory;
// TODO need to completely javadoc this class
/**
* Implements the basic functionality of a TCP-based service that can be
* discovered by OpenNMS. It extends the AbstractPlugin class and provides
* methods for creating the sockets and dealing with timeouts and retries.
*
* @author Matt Brozowski
* @version $Id: $
*/
public abstract class AbstractTcpPlugin extends AbstractPlugin {
int m_defaultPort;
int m_defaultRetry;
int m_defaultTimeout;
String m_pluginName;
String m_protocolName;
/**
* <p>Constructor for AbstractTcpPlugin.</p>
*
* @param protocol a {@link java.lang.String} object.
* @param defaultTimeout a int.
* @param defaultRetry a int.
*/
protected AbstractTcpPlugin(String protocol, int defaultTimeout, int defaultRetry) {
this(protocol, -1, defaultTimeout, defaultRetry);
}
/**
* <p>Constructor for AbstractTcpPlugin.</p>
*
* @param protocol a {@link java.lang.String} object.
* @param defaultPort a int.
* @param defaultTimeout a int.
* @param defaultRetry a int.
*/
protected AbstractTcpPlugin(String protocol, int defaultPort, int defaultTimeout, int defaultRetry) {
super();
if (protocol == null)
throw new NullPointerException("protocol is null");
m_protocolName = protocol;
m_defaultPort = defaultPort;
m_defaultTimeout = defaultTimeout;
m_defaultRetry = defaultRetry;
}
/**
* <P>
* Test to see if the passed host-port pair is the endpoint for an Citrix
* server. If there is an Citrix server at that destination then a value of
* true is returned from the method. Otherwise a false value is returned to
* the caller.
* </P>
*
* @param config a {@link org.opennms.netmgt.capsd.ConnectionConfig} object.
* @return True if server supports Citrix on the specified port, false
* otherwise
*/
final protected boolean checkConnection(ConnectionConfig config) {
// get a log to send errors
//
ThreadCategory log = ThreadCategory.getInstance(getClass());
// don't let the user set the timeout to 0, an infinite loop will occur
// if the server is down
int timeout = (config.getTimeout() == 0 ? 10 : config.getTimeout());
boolean isAServer = false;
for (int attempts = 0; attempts <= config.getRetry() && !isAServer; attempts++) {
if (!preconnectCheck(config)) {
// No chance of supporting this protocol just bail
break;
}
Socket socket = null;
try {
// create a connected socket
//
socket = new Socket();
socket.connect(config.getSocketAddress(), timeout);
socket.setSoTimeout(timeout);
log.debug(getPluginName() + ": connected to host: " + config.getInetAddress() + " on port: " + config.getPort());
socket = getSocketWrapper().wrapSocket(socket);
isAServer = checkProtocol(socket, config);
} catch (ConnectException cE) {
// Connection refused!! Continue to retry.
//
log.debug(getPluginName() + ": connection refused to " + config.getInetAddress() + ":" + config.getPort());
isAServer = false;
} catch (NoRouteToHostException e) {
// No route to host!! No need to perform retries.
e.fillInStackTrace();
log.info(getPluginName() + ": Unable to test host " + config.getInetAddress() + ", no route available", e);
isAServer = false;
throw new UndeclaredThrowableException(e);
} catch (InterruptedIOException e) {
log.debug(getPluginName() + ": did not connect to host within timeout: " + timeout + " attempt: " + attempts);
isAServer = false;
} catch (IOException e) {
log.info(getPluginName() + ": Error communicating with host " + config.getInetAddress(), e);
isAServer = false;
} catch (Throwable t) {
log.warn(getPluginName() + ": Undeclared throwable exception caught contacting host " + config.getInetAddress(), t);
isAServer = false;
} finally {
if (socket != null)
closeSocket(socket, config);
}
}
//
// return the success/failure of this
// attempt to contact the server.
//
return isAServer;
}
/**
* <p>closeSocket</p>
*
* @param socket a {@link java.net.Socket} object.
* @param config a {@link org.opennms.netmgt.capsd.ConnectionConfig} object.
*/
protected void closeSocket(Socket socket, ConnectionConfig config) {
try {
if (socket != null)
socket.close();
} catch (IOException e) {
}
}
/**
* <p>checkProtocol</p>
*
* @param socket a {@link java.net.Socket} object.
* @param config a {@link org.opennms.netmgt.capsd.ConnectionConfig} object.
* @return a boolean.
* @throws java.lang.Exception if any.
*/
protected abstract boolean checkProtocol(Socket socket, ConnectionConfig config) throws Exception;
/**
* <p>createConnectionConfig</p>
*
* @param address a {@link java.net.InetAddress} object.
* @param port a int.
* @return a {@link org.opennms.netmgt.capsd.ConnectionConfig} object.
*/
protected ConnectionConfig createConnectionConfig(InetAddress address, int port) {
return new ConnectionConfig(address, port);
}
/**
* <p>getConnectionConfigList</p>
*
* @param qualifiers a {@link java.util.Map} object.
* @param address a {@link java.net.InetAddress} object.
* @return a {@link java.util.List} object.
*/
protected List<ConnectionConfig> getConnectionConfigList(Map<String, Object> qualifiers, InetAddress address) {
if (m_defaultPort == -1)
throw new IllegalStateException("m_defaultPort == -1");
int port = getKeyedInteger(qualifiers, "port", m_defaultPort);
return Collections.singletonList(createConnectionConfig(address, port));
}
/**
* <p>getKeyedInteger</p>
*
* @param qualifiers a {@link java.util.Map} object.
* @param key a {@link java.lang.String} object.
* @param defaultVal a int.
* @return a int.
*/
final protected int getKeyedInteger(Map<String, Object> qualifiers, String key, int defaultVal) {
if (qualifiers == null)
return defaultVal;
else
return ParameterMap.getKeyedInteger(qualifiers, key, defaultVal);
}
/**
* <p>getKeyedIntegerArray</p>
*
* @param qualifiers a {@link java.util.Map} object.
* @param key a {@link java.lang.String} object.
* @param defaultVal an array of int.
* @return an array of int.
*/
final protected int[] getKeyedIntegerArray(Map<String, Object> qualifiers, String key, int[] defaultVal) {
if (qualifiers == null)
return defaultVal;
else
return ParameterMap.getKeyedIntegerArray(qualifiers, key, defaultVal);
}
/**
* <p>getPluginName</p>
*
* @return Returns the pluginName.
*/
final public String getPluginName() {
if (m_pluginName == null) {
String fullName = this.getClass().getName();
int idx = fullName.lastIndexOf('.');
m_pluginName = (idx < 0 ? fullName : fullName.substring(idx + 1));
}
return m_pluginName;
}
/**
* Returns the name of the protocol that this plugin checks on the target
* system for support.
*
* @return The protocol name for this plugin.
*/
final public String getProtocolName() {
return m_protocolName;
}
/**
* {@inheritDoc}
*
* Returns true if the protocol defined by this plugin is supported. If the
* protocol is not supported then a false value is returned to the caller.
*/
final public boolean isProtocolSupported(InetAddress address) {
return isProtocolSupported(address, null);
}
/**
* {@inheritDoc}
*
* Returns true if the protocol defined by this plugin is supported. If the
* protocol is not supported then a false value is returned to the caller.
* The qualifier map passed to the method is used by the plugin to return
* additional information by key-name. These key-value pairs can be added to
* service events if needed.
*/
final public boolean isProtocolSupported(InetAddress address, Map<String, Object> qualifiers) {
List<ConnectionConfig> connList = getConnectionConfigList(qualifiers, address);
for(ConnectionConfig config : connList) {
populateConnectionConfig(config, qualifiers);
if (checkConnection(config)) {
if (qualifiers != null)
saveConfig(qualifiers, config);
return true;
}
}
return false;
}
/**
* <p>populateConnectionConfig</p>
*
* @param config a {@link org.opennms.netmgt.capsd.ConnectionConfig} object.
* @param qualifiers a {@link java.util.Map} object.
*/
protected void populateConnectionConfig(ConnectionConfig config, Map<String, Object> qualifiers) {
config.setQualifiers(qualifiers);
config.setTimeout(getKeyedInteger(qualifiers, "timeout", m_defaultTimeout));
config.setRetry(getKeyedInteger(qualifiers, "retry", m_defaultRetry));
}
/**
* <p>preconnectCheck</p>
*
* @param config a {@link org.opennms.netmgt.capsd.ConnectionConfig} object.
* @return a boolean.
*/
protected boolean preconnectCheck(ConnectionConfig config) {
return true;
}
/**
* <p>saveConfig</p>
*
* @param qualifiers a {@link java.util.Map} object.
* @param config a {@link org.opennms.netmgt.capsd.ConnectionConfig} object.
*/
protected void saveConfig(Map<String, Object> qualifiers, ConnectionConfig config) {
saveKeyedInteger(qualifiers, "port", config.getPort());
}
/**
* <p>saveKeyedInteger</p>
*
* @param qualifiers a {@link java.util.Map} object.
* @param key a {@link java.lang.String} object.
* @param value a int.
*/
final protected void saveKeyedInteger(Map<String, Object> qualifiers, String key, int value) {
if (qualifiers != null && !qualifiers.containsKey(key))
qualifiers.put(key, Integer.valueOf(value));
}
/**
* <p>setPluginName</p>
*
* @param pluginName
* The pluginName to set.
*/
final public void setPluginName(String pluginName) {
m_pluginName = pluginName;
}
/**
* <p>getSocketWrapper</p>
*/
protected SocketWrapper getSocketWrapper() {
return new DefaultSocketWrapper();
}
}