org.snmp4j.transport.AbstractConnectionOrientedTransportMapping Maven / Gradle / Ivy
/*_############################################################################
_##
_## SNMP4J - AbstractConnectionOrientedTransportMapping.java
_##
_## Copyright (C) 2003-2024 Frank Fock (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 org.snmp4j.log.LogAdapter;
import org.snmp4j.log.LogFactory;
import org.snmp4j.smi.Address;
import org.snmp4j.smi.TcpAddress;
import org.snmp4j.util.WorkerTask;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
/**
* The {@link AbstractConnectionOrientedTransportMapping} implements generic functions for a connection
* oriented transport mapping for server and client connections.
*
* @param the address type.
* @param the socket entry type.
* @author Frank Fock
* @since 3.7.0
*/
public abstract class AbstractConnectionOrientedTransportMapping>
extends AbstractTransportMapping implements ConnectionOrientedTransportMapping {
private static final LogAdapter logger = LogFactory.getLogger(AbstractConnectionOrientedTransportMapping.class);
protected boolean serverEnabled = false;
protected Map sockets = new ConcurrentHashMap<>();
private int maxBusyLoops = DefaultTcpTransportMapping.DEFAULT_MAX_BUSY_LOOPS;
protected AbstractConnectionOrientedTransportMapping() {
}
protected synchronized void timeoutSocket(AbstractSocketEntry entry) {
if ((connectionTimeout > 0) && (getSocketCleaner() != null)) {
SocketTimeout socketTimeout = new SocketTimeout(this, entry);
entry.setSocketTimeout(socketTimeout);
getSocketCleaner().schedule(socketTimeout, connectionTimeout);
}
}
/**
* 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;
}
/**
* Sets optional server socket options. The default implementation does
* nothing.
*
* @param serverSocket
* the {@link ServerSocket} to apply additional non-default options.
*/
protected void setSocketOptions(ServerSocket serverSocket) {
}
@Override
public boolean isServerEnabled() {
return serverEnabled;
}
@Override
public void setServerEnabled(boolean serverEnabled) {
this.serverEnabled = serverEnabled;
}
protected void closeSockets(Map sockets) {
List closedSockets = new LinkedList<>();
for (S entry : sockets.values()) {
SocketChannel sc = entry.getSocketChannel();
if (sc != null) {
try {
try {
sc.socket().close();
} catch (UnsupportedOperationException unsupportedOperationException) {
// unix domain socket
sc.close();
}
if (logger.isDebugEnabled()) {
logger.debug("Socket channel to " + entry.getPeerAddress() + " closed");
}
TransportStateEvent e =
new TransportStateEvent(this, entry.getPeerAddress(),
TransportStateEvent.STATE_CLOSED, null);
fireConnectionStateChanged(e);
closedSockets.add(entry.getPeerAddress());
} catch (IOException iox) {
// ignore
logger.debug(iox);
}
}
}
for (A remoteAddress : closedSockets) {
sockets.remove(remoteAddress);
}
}
/**
* 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 {@code true} if the connection has been closed and
* {@code 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(A remoteAddress) throws IOException {
if (logger.isDebugEnabled()) {
logger.debug("Closing socket for peer address " + remoteAddress);
}
AbstractSocketEntry entry = sockets.remove(remoteAddress);
if (entry != null) {
if (entry.getSocketTimeout() != null) {
entry.getSocketTimeout().cancel();
}
SocketChannel sc = entry.getSocketChannel();
if (sc != null) {
try {
sc.socket().close();
} catch (UnsupportedOperationException unsupportedOperationException) {
sc.close();
if (logger.isDebugEnabled()) {
logger.debug("Closed socket channel for peer address " + remoteAddress);
}
}
if (logger.isInfoEnabled()) {
logger.info("Socket to " + entry.getPeerAddress() + " closed");
}
TransportStateEvent e =
new TransportStateEvent(this, entry.getPeerAddress(),
TransportStateEvent.STATE_CLOSED, null);
fireConnectionStateChanged(e);
}
return true;
}
return false;
}
/**
* Closes all open sockets and stops the internal server thread that
* processes messages.
*/
public void close() throws IOException {
for (S entry : sockets.values()) {
entry.closeSession();
TransportStateEvent e =
new TransportStateEvent(this, entry.getPeerAddress(), TransportStateEvent.STATE_CLOSED,
null);
fireConnectionStateChanged(e);
}
WorkerTask st = listenWorkerTask;
listenWorkerTask = null;
if (st != null) {
st.terminate();
st.interrupt();
try {
st.join();
} catch (InterruptedException ex) {
logger.warn(ex);
}
closeSockets(sockets);
if (getSocketCleaner() != null) {
getSocketCleaner().cancel();
}
socketCleaner = null;
}
}
public abstract void wakeupServerSelector();
/**
* Gets an unmodifiable map of the {@link AbstractSocketEntry} instances associated with this transport mapping.
* @return
* an unmodifiable map from {@link Address} to {@link AbstractSocketEntry}.
* @since 3.7.0
*/
public Map getSockets() {
return Collections.unmodifiableMap(sockets);
}
protected void cancelNonServerSelectionKey(SelectionKey sk) {
if (!sk.isAcceptable()) {
sk.cancel();
}
}
protected int getMaxBusyLoops() {
return maxBusyLoops;
}
protected void setMaxBusyLoops(int maxBusyLoops) {
this.maxBusyLoops = maxBusyLoops;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy