org.snmp4j.transport.DefaultTcpTransportMapping Maven / Gradle / Ivy
/*_############################################################################
_##
_## SNMP4J 2 - DefaultTcpTransportMapping.java
_##
_## Copyright (C) 2003-2016 Frank Fock and Jochen Katz (SNMP4J.org)
_##
_## 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.snmp4j.transport;
import java.io.*;
import java.net.*;
import java.nio.*;
import java.nio.channels.*;
import java.util.*;
import org.snmp4j.TransportStateReference;
import org.snmp4j.asn1.*;
import org.snmp4j.asn1.BER.*;
import org.snmp4j.log.*;
import org.snmp4j.mp.SnmpConstants;
import org.snmp4j.security.SecurityLevel;
import org.snmp4j.smi.*;
import org.snmp4j.SNMP4JSettings;
import org.snmp4j.util.WorkerTask;
import org.snmp4j.util.CommonTimer;
/**
* The DefaultTcpTransportMapping
implements a TCP transport
* mapping with the Java 1.4 new IO API.
*
* It uses a single thread for processing incoming and outgoing messages.
* The thread is started when the listen
method is called, or
* when an outgoing request is sent using the sendMessage
method.
*
* @author Frank Fock
* @version 1.11
*/
public class DefaultTcpTransportMapping extends TcpTransportMapping {
/**
* The maximum number of loops trying to read data from an incoming port but no data has been received.
* A value of 0 or less disables the check.
*/
public static final int DEFAULT_MAX_BUSY_LOOPS = 100;
private static final LogAdapter logger =
LogFactory.getLogger(DefaultTcpTransportMapping.class);
private Map
sockets = new Hashtable();
private WorkerTask server;
private ServerThread serverThread;
private CommonTimer socketCleaner;
// 1 minute default timeout
private long connectionTimeout = 60000;
private boolean serverEnabled = false;
private static final int MIN_SNMP_HEADER_LENGTH = 6;
private MessageLengthDecoder messageLengthDecoder =
new SnmpMesssageLengthDecoder();
private int maxBusyLoops = DEFAULT_MAX_BUSY_LOOPS;
/**
* Creates a default TCP transport mapping with the server for incoming
* messages disabled.
* @throws IOException
* on failure of binding a local port.
*/
public DefaultTcpTransportMapping() throws IOException {
super(new TcpAddress(InetAddress.getLocalHost(), 0));
}
/**
* Creates a default TCP transport mapping that binds to the given address
* (interface) on the local host.
*
* @param serverAddress
* the TcpAddress instance that describes the server address to listen
* on incoming connection requests.
* @throws IOException
* if the given address cannot be bound.
*/
public DefaultTcpTransportMapping(TcpAddress serverAddress) throws IOException
{
super(serverAddress);
this.serverEnabled = true;
}
/**
* Listen for incoming and outgoing requests. If the serverEnabled
* member is false
the server for incoming requests is not
* started. This starts the internal server thread that processes messages.
* @throws SocketException
* when the transport is already listening for incoming/outgoing messages.
* @throws IOException
*/
public synchronized void listen() throws java.io.IOException {
if (server != null) {
throw new SocketException("Port already listening");
}
serverThread = new ServerThread();
server = SNMP4JSettings.getThreadFactory().createWorkerThread(
"DefaultTCPTransportMapping_"+getAddress(), serverThread, true);
if (connectionTimeout > 0) {
// run as daemon
socketCleaner = SNMP4JSettings.getTimerFactory().createTimer();
}
server.run();
}
/**
* Changes the priority of the server thread for this TCP transport mapping.
* This method has no effect, if called before {@link #listen()} has been
* called for this transport mapping or if SNMP4J is configured to use
* a non-default thread factory.
*
* @param newPriority
* the new priority.
* @see Thread#setPriority
* @since 1.2.2
*/
public void setPriority(int newPriority) {
WorkerTask st = server;
if (st instanceof Thread) {
((Thread)st).setPriority(newPriority);
}
}
/**
* Returns the priority of the internal listen thread.
* @return
* a value between {@link Thread#MIN_PRIORITY} and
* {@link Thread#MAX_PRIORITY}.
* @since 1.2.2
*/
public int getPriority() {
WorkerTask st = server;
if (st instanceof Thread) {
return ((Thread)st).getPriority();
}
else {
return Thread.NORM_PRIORITY;
}
}
/**
* Sets the name of the listen thread for this UDP transport mapping.
* This method has no effect, if called before {@link #listen()} has been
* called for this transport mapping.
*
* @param name
* the new thread name.
* @since 1.6
*/
public void setThreadName(String name) {
WorkerTask st = server;
if (st instanceof Thread) {
((Thread)st).setName(name);
}
}
/**
* Returns the name of the listen thread.
* @return
* the thread name if in listening mode, otherwise null
.
* @since 1.6
*/
public String getThreadName() {
WorkerTask st = server;
if (st != null) {
return ((Thread)st).getName();
}
else {
return null;
}
}
/**
* Closes all open sockets and stops the internal server thread that
* processes messages.
*/
public void close() {
WorkerTask st = server;
if (st != null) {
st.terminate();
st.interrupt();
try {
st.join();
}
catch (InterruptedException ex) {
logger.warn(ex);
}
server = null;
for (SocketEntry entry : sockets.values()) {
Socket s = entry.getSocket();
if (s != null) {
try {
SocketChannel sc = s.getChannel();
s.close();
if (logger.isDebugEnabled()) {
logger.debug("Socket to " + entry.getPeerAddress() + " closed");
}
if (sc != null) {
sc.close();
if (logger.isDebugEnabled()) {
logger.debug("Socket channel to " +
entry.getPeerAddress() + " closed");
}
}
}
catch (IOException iox) {
// ingore
logger.debug(iox);
}
}
}
if (socketCleaner != null) {
socketCleaner.cancel();
}
socketCleaner = null;
}
}
/**
* Closes a connection to the supplied remote address, if it is open. This
* method is particularly useful when not using a timeout for remote
* connections.
*
* @param remoteAddress
* the address of the peer socket.
* @return
* true
if the connection has been closed and
* false
if there was nothing to close.
* @throws IOException
* if the remote address cannot be closed due to an IO exception.
* @since 1.7.1
*/
public synchronized boolean close(TcpAddress remoteAddress) throws IOException {
if (logger.isDebugEnabled()) {
logger.debug("Closing socket for peer address "+remoteAddress);
}
SocketEntry entry = sockets.remove(remoteAddress);
if (entry != null) {
Socket s = entry.getSocket();
if (s != null) {
SocketChannel sc = entry.getSocket().getChannel();
entry.getSocket().close();
if (logger.isInfoEnabled()) {
logger.info("Socket to " + entry.getPeerAddress() + " closed");
}
if (sc != null) {
sc.close();
if (logger.isDebugEnabled()) {
logger.debug("Closed socket channel for peer address "+
remoteAddress);
}
}
}
return true;
}
return false;
}
/**
* Sends a SNMP message to the supplied address.
* @param address
* an TcpAddress
. A ClassCastException
is thrown
* if address
is not a TcpAddress
instance.
* @param message byte[]
* the message to sent.
* @param tmStateReference
* the (optional) transport model state reference as defined by
* RFC 5590 section 6.1.
* @throws IOException
*/
public void sendMessage(TcpAddress address, byte[] message,
TransportStateReference tmStateReference)
throws java.io.IOException
{
if (server == null) {
listen();
}
serverThread.sendMessage(address, message, tmStateReference);
}
/**
* Gets the connection timeout. This timeout specifies the time a connection
* may be idle before it is closed.
* @return long
* the idle timeout in milliseconds.
*/
public long getConnectionTimeout() {
return connectionTimeout;
}
/**
* Sets the connection timeout. This timeout specifies the time a connection
* may be idle before it is closed.
* @param connectionTimeout
* the idle timeout in milliseconds. A zero or negative value will disable
* any timeout and connections opened by this transport mapping will stay
* opened until they are explicitly closed.
*/
public void setConnectionTimeout(long connectionTimeout) {
this.connectionTimeout = connectionTimeout;
}
/**
* Checks whether a server for incoming requests is enabled.
* @return boolean
*/
public boolean isServerEnabled() {
return serverEnabled;
}
public MessageLengthDecoder getMessageLengthDecoder() {
return messageLengthDecoder;
}
/**
* Sets whether a server for incoming requests should be created when
* the transport is set into listen state. Setting this value has no effect
* until the {@link #listen()} method is called (if the transport is already
* listening, {@link #close()} has to be called before).
* @param serverEnabled
* if true
if the transport will listens for incoming
* requests after {@link #listen()} has been called.
*/
public void setServerEnabled(boolean serverEnabled) {
this.serverEnabled = serverEnabled;
}
/**
* Sets the message length decoder. Default message length decoder is the
* {@link SnmpMesssageLengthDecoder}. The message length decoder must be
* able to decode the total length of a message for this transport mapping
* protocol(s).
* @param messageLengthDecoder
* a MessageLengthDecoder
instance.
*/
public void setMessageLengthDecoder(MessageLengthDecoder messageLengthDecoder) {
if (messageLengthDecoder == null) {
throw new NullPointerException();
}
this.messageLengthDecoder = messageLengthDecoder;
}
/**
* Gets the inbound buffer size for incoming requests. When SNMP packets are
* received that are longer than this maximum size, the messages will be
* silently dropped and the connection will be closed.
* @return
* the maximum inbound buffer size in bytes.
*/
public int getMaxInboundMessageSize() {
return super.getMaxInboundMessageSize();
}
/**
* Sets the maximum buffer size for incoming requests. When SNMP packets are
* received that are longer than this maximum size, the messages will be
* silently dropped and the connection will be closed.
* @param maxInboundMessageSize
* the length of the inbound buffer in bytes.
*/
public void setMaxInboundMessageSize(int maxInboundMessageSize) {
this.maxInboundMessageSize = maxInboundMessageSize;
}
private synchronized void timeoutSocket(SocketEntry entry) {
if (connectionTimeout > 0) {
socketCleaner.schedule(new SocketTimeout(entry), connectionTimeout);
}
}
public boolean isListening() {
return (server != null);
}
protected int getMaxBusyLoops() {
return maxBusyLoops;
}
protected void setMaxBusyLoops(int maxBusyLoops) {
this.maxBusyLoops = maxBusyLoops;
}
/**
* Sets optional server socket options. The default implementation does
* nothing.
* @param serverSocket
* the ServerSocket
to apply additional non-default options.
*/
protected void setSocketOptions(ServerSocket serverSocket) {
}
class SocketEntry {
private Socket socket;
private TcpAddress peerAddress;
private long lastUse;
private LinkedList message = new LinkedList();
private ByteBuffer readBuffer = null;
private volatile int registrations = 0;
private volatile int busyLoops = 0;
public SocketEntry(TcpAddress address, Socket socket) {
this.peerAddress = address;
this.socket = socket;
this.lastUse = System.nanoTime();
}
public synchronized void addRegistration(Selector selector, int opKey)
throws ClosedChannelException
{
if ((this.registrations & opKey) == 0) {
this.registrations |= opKey;
if (logger.isDebugEnabled()) {
logger.debug("Adding operation "+opKey+" for: " + toString());
}
socket.getChannel().register(selector, registrations, this);
}
else if (!socket.getChannel().isRegistered()) {
this.registrations = opKey;
if (logger.isDebugEnabled()) {
logger.debug("Registering new operation "+opKey+" for: " + toString());
}
socket.getChannel().register(selector, opKey, this);
}
}
public synchronized void removeRegistration(Selector selector, int opKey)
throws ClosedChannelException {
if ((this.registrations & opKey) == opKey) {
this.registrations &= ~opKey;
socket.getChannel().register(selector, this.registrations, this);
}
}
public synchronized boolean isRegistered(int opKey) {
return (this.registrations & opKey) == opKey;
}
public long getLastUse() {
return lastUse;
}
public void used() {
lastUse = System.nanoTime();
}
public Socket getSocket() {
return socket;
}
public TcpAddress getPeerAddress() {
return peerAddress;
}
public synchronized void addMessage(byte[] message) {
this.message.add(message);
}
public synchronized byte[] nextMessage() {
if (this.message.size() > 0) {
return this.message.removeFirst();
}
return null;
}
public synchronized boolean hasMessage() {
return !this.message.isEmpty();
}
public void setReadBuffer(ByteBuffer byteBuffer) {
this.readBuffer = byteBuffer;
}
public ByteBuffer getReadBuffer() {
return readBuffer;
}
public int nextBusyLoop() {
return ++busyLoops;
}
public void resetBusyLoops() {
busyLoops = 0;
}
public String toString() {
return "SocketEntry[peerAddress="+peerAddress+
",socket="+socket+",lastUse="+new Date(lastUse/SnmpConstants.MILLISECOND_TO_NANOSECOND)+
",readBufferPosition="+((readBuffer== null)?-1:readBuffer.position())+
"]";
}
/*
public boolean equals(Object o) {
if (o instanceof SocketEntry) {
SocketEntry other = (SocketEntry)o;
return other.peerAddress.equals(peerAddress) &&
((other.message == message) ||
((message != null) && (message.equals(other.message))));
}
return false;
}
public int hashCode() {
return peerAddress.hashCode();
}
*/
}
public static class SnmpMesssageLengthDecoder implements MessageLengthDecoder {
public int getMinHeaderLength() {
return MIN_SNMP_HEADER_LENGTH;
}
public MessageLength getMessageLength(ByteBuffer buf) throws IOException {
MutableByte type = new MutableByte();
BERInputStream is = new BERInputStream(buf);
int ml = BER.decodeHeader(is, type, false);
int hl = (int)is.getPosition();
MessageLength messageLength = new MessageLength(hl, ml);
return messageLength;
}
}
class SocketTimeout extends TimerTask {
private SocketEntry entry;
public SocketTimeout(SocketEntry entry) {
this.entry = entry;
}
public void run() {
long now = System.nanoTime();
SocketEntry entryCopy = entry;
if (entryCopy == null) {
// nothing to do
return;
}
long idleMillis = (now - entryCopy.getLastUse()) / SnmpConstants.MILLISECOND_TO_NANOSECOND;
if ((socketCleaner == null) ||
(idleMillis >= connectionTimeout)) {
if (logger.isDebugEnabled()) {
logger.debug("Socket has not been used for " + idleMillis + " milliseconds, closing it");
}
try {
synchronized (entryCopy) {
if (idleMillis >= connectionTimeout) {
sockets.remove(entryCopy.getPeerAddress());
entryCopy.getSocket().close();
if (logger.isInfoEnabled()) {
logger.info("Socket to " + entryCopy.getPeerAddress() + " closed due to timeout");
}
}
else {
rescheduleCleanup(idleMillis, entryCopy);
}
}
}
catch (IOException ex) {
logger.error(ex);
}
}
else {
rescheduleCleanup(idleMillis, entryCopy);
}
}
private void rescheduleCleanup(long idleMillisAlreadyElapsed, SocketEntry entry) {
long nextRun = connectionTimeout - idleMillisAlreadyElapsed;
if (logger.isDebugEnabled()) {
logger.debug("Scheduling " + nextRun);
}
socketCleaner.schedule(new SocketTimeout(entry), nextRun);
}
public boolean cancel(){
boolean result = super.cancel();
// free objects early
entry = null;
return result;
}
}
class ServerThread implements WorkerTask {
private byte[] buf;
private volatile boolean stop = false;
private Throwable lastError = null;
private ServerSocketChannel ssc;
private Selector selector;
private LinkedList pending = new LinkedList();
public ServerThread() throws IOException {
buf = new byte[getMaxInboundMessageSize()];
// Selector for incoming requests
selector = Selector.open();
if (serverEnabled) {
// Create a new server socket and set to non blocking mode
ssc = ServerSocketChannel.open();
try {
ssc.configureBlocking(false);
// Bind the server socket
InetSocketAddress isa = new InetSocketAddress(tcpAddress.getInetAddress(),
tcpAddress.getPort());
setSocketOptions(ssc.socket());
ssc.socket().bind(isa);
// Register accepts on the server socket with the selector. This
// step tells the selector that the socket wants to be put on the
// ready list when accept operations occur, so allowing multiplexed
// non-blocking I/O to take place.
ssc.register(selector, SelectionKey.OP_ACCEPT);
}
catch (IOException iox) {
logger.warn("Socket bind failed for "+tcpAddress+": "+iox.getMessage());
try {
ssc.close();
}
catch (IOException ioxClose) {
logger.warn("Socket close failed after bind failure for "+tcpAddress+": "+ioxClose.getMessage());
}
throw iox;
}
}
}
private void processPending() {
synchronized (pending) {
for (int i=0; i 0) {
if (stop) {
break;
}
// Someone is ready for I/O, get the ready keys
Set readyKeys = selector.selectedKeys();
Iterator it = readyKeys.iterator();
// Walk through the ready keys collection and process date requests.
while (it.hasNext()) {
try {
SelectionKey sk = it.next();
it.remove();
SocketChannel readChannel = null;
TcpAddress incomingAddress = null;
if (sk.isAcceptable()) {
logger.debug("Key is acceptable");
// The key indexes into the selector so you
// can retrieve the socket that's ready for I/O
ServerSocketChannel nextReady =
(ServerSocketChannel) sk.channel();
Socket s = nextReady.accept().socket();
readChannel = s.getChannel();
readChannel.configureBlocking(false);
incomingAddress = new TcpAddress(s.getInetAddress(),
s.getPort());
SocketEntry entry = new SocketEntry(incomingAddress, s);
entry.addRegistration(selector, SelectionKey.OP_READ);
sockets.put(incomingAddress, entry);
timeoutSocket(entry);
TransportStateEvent e =
new TransportStateEvent(DefaultTcpTransportMapping.this,
incomingAddress,
TransportStateEvent.
STATE_CONNECTED,
null);
fireConnectionStateChanged(e);
if (e.isCancelled()) {
logger.warn("Incoming connection cancelled");
s.close();
sockets.remove(incomingAddress);
readChannel = null;
}
}
else if (sk.isWritable()) {
logger.debug("Key is writable");
incomingAddress = writeData(sk, incomingAddress);
}
else if (sk.isReadable()) {
logger.debug("Key is readable");
readChannel = (SocketChannel) sk.channel();
incomingAddress =
new TcpAddress(readChannel.socket().getInetAddress(),
readChannel.socket().getPort());
}
else if (sk.isConnectable()) {
logger.debug("Key is connectable");
connectChannel(sk, incomingAddress);
}
if (readChannel != null) {
logger.debug("Key is reading");
try {
if (!readMessage(sk, readChannel, incomingAddress)) {
SocketEntry entry = (SocketEntry) sk.attachment();
if ((entry != null) && (getMaxBusyLoops() > 0)) {
int busyLoops = entry.nextBusyLoop();
if (busyLoops > getMaxBusyLoops()) {
if (logger.isDebugEnabled()) {
logger.debug("After " + busyLoops + " read key has been removed: " + entry);
}
entry.removeRegistration(selector, SelectionKey.OP_READ);
entry.resetBusyLoops();
}
}
}
}
catch (IOException iox) {
// IO exception -> channel closed remotely
socketClosedRemotely(sk, readChannel, incomingAddress);
}
}
}
catch (CancelledKeyException ckex) {
if (logger.isDebugEnabled()) {
logger.debug("Selection key cancelled, skipping it");
}
}
}
}
}
catch (NullPointerException npex) {
// There seems to happen a NullPointerException within the select()
npex.printStackTrace();
logger.warn("NullPointerException within select()?");
stop = true;
}
processPending();
}
if (ssc != null) {
ssc.close();
}
if (selector != null) {
selector.close();
}
}
catch (IOException iox) {
logger.error(iox);
lastError = iox;
}
if (!stop) {
stop = true;
synchronized (DefaultTcpTransportMapping.this) {
server = null;
}
}
if (logger.isDebugEnabled()) {
logger.debug("Worker task finished: " + getClass().getName());
}
}
private void connectChannel(SelectionKey sk, TcpAddress incomingAddress) {
SocketEntry entry = (SocketEntry) sk.attachment();
try {
SocketChannel sc = (SocketChannel) sk.channel();
if (!sc.isConnected()) {
if (sc.finishConnect()) {
sc.configureBlocking(false);
logger.debug("Connected to " + entry.getPeerAddress());
// make sure conncetion is closed if not used for timeout
// micro seconds
timeoutSocket(entry);
entry.removeRegistration(selector, SelectionKey.OP_CONNECT);
entry.addRegistration(selector, SelectionKey.OP_WRITE);
}
else {
entry = null;
}
}
if (entry != null) {
Address addr = (incomingAddress == null) ?
entry.getPeerAddress() : incomingAddress;
logger.debug("Fire connected event for "+addr);
TransportStateEvent e =
new TransportStateEvent(DefaultTcpTransportMapping.this,
addr,
TransportStateEvent.
STATE_CONNECTED,
null);
fireConnectionStateChanged(e);
}
}
catch (IOException iox) {
logger.warn(iox);
sk.cancel();
closeChannel(sk.channel());
if (entry != null) {
pending.remove(entry);
}
}
}
private TcpAddress writeData(SelectionKey sk, TcpAddress incomingAddress) {
SocketEntry entry = (SocketEntry) sk.attachment();
try {
SocketChannel sc = (SocketChannel) sk.channel();
incomingAddress =
new TcpAddress(sc.socket().getInetAddress(),
sc.socket().getPort());
if ((entry != null) && (!entry.hasMessage())) {
synchronized (pending) {
pending.remove(entry);
entry.removeRegistration(selector, SelectionKey.OP_WRITE);
}
}
if (entry != null) {
writeMessage(entry, sc);
}
}
catch (IOException iox) {
logger.warn(iox);
TransportStateEvent e =
new TransportStateEvent(DefaultTcpTransportMapping.this,
incomingAddress,
TransportStateEvent.
STATE_DISCONNECTED_REMOTELY,
iox);
fireConnectionStateChanged(e);
// make sure channel is closed properly:
closeChannel(sk.channel());
}
return incomingAddress;
}
private void closeChannel(SelectableChannel channel) {
try {
channel.close();
}
catch (IOException channelCloseException) {
logger.warn(channelCloseException);
}
}
private boolean readMessage(SelectionKey sk, SocketChannel readChannel,
TcpAddress incomingAddress) throws IOException {
SocketEntry entry = (SocketEntry) sk.attachment();
if (entry == null) {
// slow but in some cases needed:
entry = sockets.get(incomingAddress);
}
if (entry != null) {
// note that socket has been used
entry.used();
ByteBuffer readBuffer = entry.getReadBuffer();
if (readBuffer != null) {
int bytesRead = readChannel.read(readBuffer);
if (logger.isDebugEnabled()) {
logger.debug("Read " + bytesRead + " bytes from " + incomingAddress);
}
if ((bytesRead >= 0) &&
(readBuffer.hasRemaining() || (readBuffer.position() < messageLengthDecoder.getMinHeaderLength()))) {
entry.addRegistration(selector, SelectionKey.OP_READ);
}
else if (bytesRead < 0) {
socketClosedRemotely(sk, readChannel, incomingAddress);
}
else {
readSnmpMessagePayload(readChannel, incomingAddress, entry, readBuffer);
}
if (bytesRead != 0) {
entry.resetBusyLoops();
return true;
}
return false;
}
}
ByteBuffer byteBuffer = ByteBuffer.wrap(buf);
byteBuffer.limit(messageLengthDecoder.getMinHeaderLength());
if (!readChannel.isOpen()) {
sk.cancel();
if (logger.isDebugEnabled()) {
logger.debug("Read channel not open, no bytes read from " +
incomingAddress);
}
return false;
}
long bytesRead;
try {
bytesRead = readChannel.read(byteBuffer);
if (logger.isDebugEnabled()) {
logger.debug("Reading header " + bytesRead + " bytes from " +
incomingAddress);
}
}
catch (ClosedChannelException ccex) {
sk.cancel();
if (logger.isDebugEnabled()) {
logger.debug("Read channel not open, no bytes read from " +
incomingAddress);
}
return false;
}
if (byteBuffer.position() >= messageLengthDecoder.getMinHeaderLength()) {
readSnmpMessagePayload(readChannel, incomingAddress, entry, byteBuffer);
}
else if (bytesRead < 0) {
socketClosedRemotely(sk, readChannel, incomingAddress);
}
else if ((entry != null) && (bytesRead > 0)) {
addBufferToReadBuffer(entry, byteBuffer);
entry.addRegistration(selector, SelectionKey.OP_READ);
}
else {
if (logger.isDebugEnabled()) {
logger.debug("No socket entry found for incoming address "+incomingAddress+
" for incomplete message with length "+bytesRead);
}
}
if ((entry != null) && (bytesRead != 0)) {
entry.resetBusyLoops();
return true;
}
return false;
}
private void readSnmpMessagePayload(SocketChannel readChannel, TcpAddress incomingAddress,
SocketEntry entry, ByteBuffer byteBuffer) throws IOException {
MessageLength messageLength =
messageLengthDecoder.getMessageLength(ByteBuffer.wrap(byteBuffer.array()));
if (logger.isDebugEnabled()) {
logger.debug("Message length is "+messageLength);
}
if ((messageLength.getMessageLength() > getMaxInboundMessageSize()) ||
(messageLength.getMessageLength() <= 0)) {
logger.error("Received message length "+messageLength+
" is greater than inboundBufferSize "+
getMaxInboundMessageSize());
if (entry != null) {
Socket s = entry.getSocket();
if (s != null) {
s.close();
logger.info("Socket to " + entry.getPeerAddress() +
" closed due to an error");
}
}
}
else {
int messageSize = messageLength.getMessageLength();
if (byteBuffer.position() < messageSize) {
if (byteBuffer.capacity() < messageSize) {
if (logger.isDebugEnabled()) {
logger.debug("Extending message buffer size according to message length to "+messageSize);
}
// Enhance capacity to expected message size and replace existing (too short) read buffer
byte[] newBuffer = new byte[messageSize];
int len = byteBuffer.position();
byteBuffer.flip();
byteBuffer.get(newBuffer, 0, len);
byteBuffer = ByteBuffer.wrap(newBuffer);
byteBuffer.position(len);
if (entry != null) {
byteBuffer.limit(messageSize);
entry.setReadBuffer(byteBuffer);
}
}
else {
byteBuffer.limit(messageSize);
}
readChannel.read(byteBuffer);
}
long bytesRead = byteBuffer.position();
if (bytesRead >= messageSize) {
if (logger.isDebugEnabled()) {
logger.debug("Message completed with "+bytesRead+" bytes and "+byteBuffer.limit()+" buffer limit");
}
if (entry != null) {
entry.setReadBuffer(null);
}
dispatchMessage(incomingAddress, byteBuffer, bytesRead, entry);
}
else if ((entry != null) && (byteBuffer != entry.getReadBuffer())){
if (logger.isDebugEnabled()) {
logger.debug("Adding buffer content to read buffer of entry "+entry+", buffer "+byteBuffer);
}
addBufferToReadBuffer(entry, byteBuffer);
}
if (entry != null) {
entry.addRegistration(selector, SelectionKey.OP_READ);
}
}
}
private void dispatchMessage(TcpAddress incomingAddress,
ByteBuffer byteBuffer, long bytesRead,
Object sessionID) {
byteBuffer.flip();
if (logger.isDebugEnabled()) {
logger.debug("Received message from " + incomingAddress +
" with length " + bytesRead + ": " +
new OctetString(byteBuffer.array(), 0,
(int)bytesRead).toHexString());
}
ByteBuffer bis;
if (isAsyncMsgProcessingSupported()) {
byte[] bytes = new byte[(int)bytesRead];
System.arraycopy(byteBuffer.array(), 0, bytes, 0, (int)bytesRead);
bis = ByteBuffer.wrap(bytes);
}
else {
bis = ByteBuffer.wrap(byteBuffer.array(),
0, (int) bytesRead);
}
TransportStateReference stateReference =
new TransportStateReference(DefaultTcpTransportMapping.this, incomingAddress, null,
SecurityLevel.undefined, SecurityLevel.undefined,
false, sessionID);
fireProcessMessage(incomingAddress, bis, stateReference);
}
private void writeMessage(SocketEntry entry, SocketChannel sc) throws
IOException {
byte[] message = entry.nextMessage();
if (message != null) {
ByteBuffer buffer = ByteBuffer.wrap(message);
sc.write(buffer);
if (logger.isDebugEnabled()) {
logger.debug("Sent message with length " +
message.length + " to " +
entry.getPeerAddress() + ": " +
new OctetString(message).toHexString());
}
entry.addRegistration(selector, SelectionKey.OP_READ);
}
else {
entry.removeRegistration(selector, SelectionKey.OP_WRITE);
// Make sure that we did not clear a selection key that was concurrently
// added:
if (entry.hasMessage() && !entry.isRegistered(SelectionKey.OP_WRITE)) {
entry.addRegistration(selector, SelectionKey.OP_WRITE);
logger.debug("Waking up selector");
selector.wakeup();
}
}
}
public void close() {
stop = true;
WorkerTask st = server;
if (st != null) {
st.terminate();
}
}
public void terminate() {
stop = true;
if (logger.isDebugEnabled()) {
logger.debug("Terminated worker task: " + getClass().getName());
}
}
public void join() {
if (logger.isDebugEnabled()) {
logger.debug("Joining worker task: " + getClass().getName());
}
}
public void interrupt() {
stop = true;
if (logger.isDebugEnabled()) {
logger.debug("Interrupting worker task: " + getClass().getName());
}
selector.wakeup();
}
}
private void addBufferToReadBuffer(SocketEntry entry, ByteBuffer byteBuffer) {
if (logger.isDebugEnabled()) {
logger.debug("Adding data "+byteBuffer+" to read buffer "+entry.getReadBuffer());
}
int buflen = byteBuffer.position();
if (entry.getReadBuffer() != null) {
entry.getReadBuffer().put(byteBuffer.array(), 0, buflen);
}
else {
byte[] message = new byte[byteBuffer.limit()];
byteBuffer.flip();
byteBuffer.get(message, 0, buflen);
ByteBuffer newBuffer = ByteBuffer.wrap(message);
newBuffer.position(buflen);
entry.setReadBuffer(newBuffer);
}
}
private void socketClosedRemotely(SelectionKey sk, SocketChannel readChannel, TcpAddress incomingAddress) throws IOException {
logger.debug("Socket closed remotely");
sk.cancel();
readChannel.close();
TransportStateEvent e =
new TransportStateEvent(DefaultTcpTransportMapping.this,
incomingAddress,
TransportStateEvent.
STATE_DISCONNECTED_REMOTELY,
null);
fireConnectionStateChanged(e);
sockets.remove(incomingAddress);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy