/*******************************************************************************
* 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.tl1d;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.util.concurrent.BlockingQueue;
import org.jfree.util.Log;
import org.opennms.core.utils.ThreadCategory;
import org.opennms.netmgt.config.tl1d.Tl1Element;
/**
* Default Implementation of the Tl1Client API.
*
* @author <a href=mailto:david@opennms.org>David Hustace</a>
* @version $Id: $
*/
public class Tl1ClientImpl implements Tl1Client {
String m_host;
int m_port;
private volatile boolean m_started = false;
private Socket m_tl1Socket;
private Thread m_socketReader;
private BlockingQueue<Tl1AutonomousMessage> m_tl1Queue;
private BufferedReader m_reader;
private TimeoutSleeper m_sleeper;
private ThreadCategory m_log;
private Tl1AutonomousMessageProcessor m_messageProcessor;
//private long m_reconnectionDelay = 30000;
private long m_reconnectionDelay; //see configuration xsd for default and set by Tl1d after instantiation
private int m_reconnectAttempts = 0;
/**
* <p>Constructor for Tl1ClientImpl.</p>
*/
public Tl1ClientImpl() {
}
/**
* <p>Constructor for Tl1ClientImpl.</p>
*
* @param queue a {@link java.util.concurrent.BlockingQueue} object.
* @param element a {@link org.opennms.netmgt.config.tl1d.Tl1Element} object.
* @param log a {@link org.opennms.core.utils.ThreadCategory} object.
* @throws java.lang.InstantiationException if any.
* @throws java.lang.IllegalAccessException if any.
* @throws java.lang.ClassNotFoundException if any.
*/
public Tl1ClientImpl(BlockingQueue<Tl1AutonomousMessage> queue, Tl1Element element, ThreadCategory log)
throws InstantiationException, IllegalAccessException, ClassNotFoundException {
m_host = element.getHost();
m_port = element.getPort();
m_tl1Queue = queue;
m_messageProcessor = (Tl1AutonomousMessageProcessor) Class.forName(element.getTl1MessageParser()).newInstance();
m_reconnectionDelay = element.getReconnectDelay();
m_log = log;
}
/* (non-Javadoc)
* @see org.opennms.netmgt.tl1d.Tl1Client#start()
*/
/**
* <p>start</p>
*/
public void start() {
log().info("start: TL1 client: "+m_host+":"+String.valueOf(m_port));
log().info("start:Connection delay = " + m_reconnectionDelay );
setStarted(true);
m_socketReader = new Thread("TL1-Socket-Reader") {
public void run() {
readMessages();
}
};
m_socketReader.start();
log().info("Started TL1 client: "+m_host+":"+String.valueOf(m_port));
}
/* (non-Javadoc)
* @see org.opennms.netmgt.tl1d.Tl1Client#stop()
*/
/**
* <p>stop</p>
*/
public void stop() {
log().info("Stopping TL1 client: "+m_host+":"+String.valueOf(m_port));
setStarted(false);
//waiting a second or so for the reader thread to clean up the socket and shut
//itself down.
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Log.error("stop: "+e, e);
}
}
private BufferedReader getReader() throws InterruptedException {
if (m_reader == null) {
m_reader = createReader();
}
return m_reader;
}
private BufferedReader createReader() throws InterruptedException {
BufferedReader reader;
while (isStarted()) {
try {
m_tl1Socket = new Socket(m_host, m_port);
reader = new BufferedReader(new InputStreamReader(m_tl1Socket.getInputStream()));
resetTimeout();
return reader;
} catch (IOException e) {
log().error("TL1 Connection Failed to " + m_host + ":" + m_port);
log().debug(e.getMessage());
waitUntilNextConnectTime();
}
}
return null;
}
private void resetTimeout() {
log().debug("resetTimeout: Resetting timeout Thread");
m_reconnectAttempts = 0;
m_sleeper = null;
}
private void waitUntilNextConnectTime() throws InterruptedException {
log().debug("waitUntilNextConnectTime: current connection attempts: "+m_reconnectAttempts);
if (isStarted()) {
if (m_sleeper == null) {
m_sleeper = new TimeoutSleeper();
}
m_reconnectAttempts++;
/* If the system is not responding, we want to wait longer and longer for the retry */
long waitTime = computeWait();
log().info("waitUntilNextConnectTime: Waiting " + waitTime + " ms......");
m_sleeper.sleep(waitTime);
}
}
private long computeWait() {
long waitTime = m_reconnectionDelay;
if (m_reconnectAttempts > 5) {
waitTime = m_reconnectionDelay * 5;
} else if (m_reconnectAttempts > 10) {
waitTime = m_reconnectionDelay * 10;
}
return waitTime;
}
private void readMessages() {
StringBuilder rawMessageBuilder = new StringBuilder();
log().info("readMessages: Begin reading off socket...");
while (isStarted()) {
try {
log().debug("readMessages: reading line from TL1 socket...");
BufferedReader reader = null;
try {
reader = getReader();
} catch (InterruptedException e) {
log().warn("readMessages: interrupted.");
return;
}
if (reader != null) {
int ch;
while((ch = reader.read()) != -1 && isStarted()) {
rawMessageBuilder.append((char)ch);
if((char)ch == ';') {
createAndQueueTl1Message(rawMessageBuilder);
rawMessageBuilder.setLength(0);
}
}
rawMessageBuilder = null;
log().warn("readMessages: resetting socket reader to client: "+m_host+":"+m_port);
resetReader(null);
}
} catch (IOException e) {
resetReader(e);
}
}
log().info("TL1 client stopped for: "+m_host+":"+String.valueOf(m_port));
}
private ThreadCategory log() {
return m_log;
}
private void createAndQueueTl1Message(StringBuilder rawMessageBuilder) {
log().debug("readMessages: offering message to queue: "+rawMessageBuilder.toString());
Tl1AutonomousMessage message = detectMessageType(rawMessageBuilder);
if (message != null) {
m_tl1Queue.offer(message);
log().debug("readMessages: successfully offered to queue.");
} else {
log().debug("readMessages: message was null, not offered to queue.");
}
}
//TODO: Lots of work to do here
private Tl1AutonomousMessage detectMessageType(StringBuilder rawMessage) {
//check token 5 to see if this is a reply message. This implies that the Tl1Client must
//track message TAGs (Correlation TAGs (CTAG) vs. Autonomous TAGs (ATAG))
if(isAutonomousMessage(rawMessage)) {
return m_messageProcessor.process(rawMessage.toString(), Tl1Message.AUTONOMOUS);
}
return null;
}
//TODO: Lots of work to do here
private boolean isAutonomousMessage(StringBuilder rawMessage) {
return true;
}
private void resetReader(IOException ex) {
if (ex != null) {
log().error("resetReader: connection failure.", ex);
}
try {
m_reader.close();
} catch (IOException e) {
log().warn("resetReader: "+e, e);
} finally {
m_reader = null;
}
try {
m_tl1Socket.close();
} catch (IOException e) {
log().warn("resetReader: "+e, e);
m_tl1Socket = null;
}
}
/* (non-Javadoc)
* @see org.opennms.netmgt.tl1d.Tl1Client#getHost()
*/
/**
* <p>getHost</p>
*
* @return a {@link java.lang.String} object.
*/
public String getHost() {
return m_host;
}
/* (non-Javadoc)
* @see org.opennms.netmgt.tl1d.Tl1Client#setHost(java.lang.String)
*/
/** {@inheritDoc} */
public void setHost(String host) {
m_host = host;
}
/* (non-Javadoc)
* @see org.opennms.netmgt.tl1d.Tl1Client#getPort()
*/
/**
* <p>getPort</p>
*
* @return a int.
*/
public int getPort() {
return m_port;
}
/* (non-Javadoc)
* @see org.opennms.netmgt.tl1d.Tl1Client#setPort(int)
*/
/** {@inheritDoc} */
public void setPort(int port) {
m_port = port;
}
/* (non-Javadoc)
* @see org.opennms.netmgt.tl1d.Tl1Client#getReconnectionDelay()
*/
/**
* <p>getReconnectionDelay</p>
*
* @return a long.
*/
public long getReconnectionDelay() {
return m_reconnectionDelay;
}
/* (non-Javadoc)
* @see org.opennms.netmgt.tl1d.Tl1Client#setReconnectionDelay(long)
*/
/** {@inheritDoc} */
public void setReconnectionDelay(long reconnectionDelay) {
m_reconnectionDelay = reconnectionDelay;
}
/**
* <p>getTl1Queue</p>
*
* @return a {@link java.util.concurrent.BlockingQueue} object.
*/
public BlockingQueue<Tl1AutonomousMessage> getTl1Queue() {
return m_tl1Queue;
}
/** {@inheritDoc} */
public void setTl1Queue(BlockingQueue<Tl1AutonomousMessage> tl1Queue) {
m_tl1Queue = tl1Queue;
}
/**
* <p>getMessageProcessor</p>
*
* @return a {@link org.opennms.netmgt.tl1d.Tl1AutonomousMessageProcessor} object.
*/
public Tl1AutonomousMessageProcessor getMessageProcessor() {
return m_messageProcessor;
}
/** {@inheritDoc} */
public void setMessageProcessor(Tl1AutonomousMessageProcessor messageProcessor) {
m_messageProcessor = messageProcessor;
}
/**
* <p>setStarted</p>
*
* @param started a boolean.
*/
public void setStarted(boolean started) {
m_started = started;
}
/**
* <p>isStarted</p>
*
* @return a boolean.
*/
public boolean isStarted() {
return m_started;
}
/** {@inheritDoc} */
public void setLog(ThreadCategory log) {
m_log = log;
}
private class TimeoutSleeper {
public void sleep(long sleepTime) throws InterruptedException {
Thread.sleep(sleepTime);
}
}
/** {@inheritDoc} */
@Override
public String toString() {
return "Tl1Client: class: "+getClass()+"; host: "+m_host+"; port: "+m_port;
}
}