Java tutorial
/* * Copyright 2002-2019 the original author or authors. * * 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 * * https://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.springframework.integration.ip.tcp.connection; import java.io.IOException; import java.io.UncheckedIOException; import java.net.InetSocketAddress; import java.nio.channels.CancelledKeyException; import java.nio.channels.ClosedChannelException; import java.nio.channels.ClosedSelectorException; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.SocketChannel; import java.util.Map; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.LinkedBlockingQueue; import org.springframework.scheduling.SchedulingAwareRunnable; import org.springframework.util.Assert; /** * A client connection factory that creates {@link TcpNioConnection}s. * * @author Gary Russell * @author Artem Bilan * * @since 2.0 * */ public class TcpNioClientConnectionFactory extends AbstractClientConnectionFactory implements SchedulingAwareRunnable { private final Map<SocketChannel, TcpNioConnection> channelMap = new ConcurrentHashMap<>(); private final BlockingQueue<SocketChannel> newChannels = new LinkedBlockingQueue<>(); private boolean usingDirectBuffers; private TcpNioConnectionSupport tcpNioConnectionSupport = new DefaultTcpNioConnectionSupport(); private volatile Selector selector; /** * Creates a TcpNioClientConnectionFactory for connections to the host and port. * @param host the host * @param port the port */ public TcpNioClientConnectionFactory(String host, int port) { super(host, port); } @Override protected void checkActive() { super.checkActive(); int n = 0; while (this.selector == null) { try { Thread.sleep(100); // NOSONAR magic number } catch (@SuppressWarnings("unused") InterruptedException e) { Thread.currentThread().interrupt(); } if (n++ > 600) { // NOSONAR magic number throw new UncheckedIOException(new IOException("Factory failed to start")); } } } @Override protected TcpConnectionSupport buildNewConnection() { try { SocketChannel socketChannel = SocketChannel.open(); setSocketAttributes(socketChannel.socket()); TcpNioConnection connection = this.tcpNioConnectionSupport.createNewConnection(socketChannel, false, isLookupHost(), getApplicationEventPublisher(), getComponentName()); connection.setUsingDirectBuffers(this.usingDirectBuffers); connection.setTaskExecutor(getTaskExecutor()); Integer sslHandshakeTimeout = getSslHandshakeTimeout(); if (sslHandshakeTimeout != null && connection instanceof TcpNioSSLConnection) { ((TcpNioSSLConnection) connection).setHandshakeTimeout(sslHandshakeTimeout); } TcpConnectionSupport wrappedConnection = wrapConnection(connection); initializeConnection(wrappedConnection, socketChannel.socket()); socketChannel.configureBlocking(false); socketChannel.connect(new InetSocketAddress(getHost(), getPort())); boolean connected = socketChannel.finishConnect(); long timeLeft = getConnectTimeout().toMillis(); while (!connected && timeLeft > 0) { Thread.sleep(50); // NOSONAR Magic # connected = socketChannel.finishConnect(); timeLeft -= 50; // NOSONAR Magic # } if (!connected) { throw new IOException("Not connected after connectTimeout"); } if (getSoTimeout() > 0) { connection.setLastRead(System.currentTimeMillis()); } this.channelMap.put(socketChannel, connection); this.newChannels.add(socketChannel); this.selector.wakeup(); return wrappedConnection; } catch (IOException e) { throw new UncheckedIOException(e); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new UncheckedIOException(new IOException(e)); } } /** * When set to true, connections created by this factory attempt * to use direct buffers where possible. * @param usingDirectBuffers The usingDirectBuffers to set. * @see java.nio.ByteBuffer */ public void setUsingDirectBuffers(boolean usingDirectBuffers) { this.usingDirectBuffers = usingDirectBuffers; } public void setTcpNioConnectionSupport(TcpNioConnectionSupport tcpNioSupport) { Assert.notNull(tcpNioSupport, "TcpNioSupport must not be null"); this.tcpNioConnectionSupport = tcpNioSupport; } @Override public boolean isLongLived() { return true; } @Override public void stop() { if (this.selector != null) { try { this.selector.close(); } catch (Exception e) { logger.error("Error closing selector", e); } } super.stop(); } @Override public void start() { synchronized (this.lifecycleMonitor) { if (!isActive()) { setActive(true); getTaskExecutor().execute(this); } } super.start(); } @Override public void run() { if (logger.isDebugEnabled()) { logger.debug("Read selector running for connections to " + getHost() + ":" + getPort()); } try { this.selector = Selector.open(); while (isActive()) { processSelectorWhileActive(); } } catch (ClosedSelectorException cse) { if (isActive()) { logger.error("Selector closed", cse); } } catch (Exception e) { logger.error("Exception in read selector thread", e); setActive(false); } if (logger.isDebugEnabled()) { logger.debug("Read selector exiting for connections to " + getHost() + ":" + getPort()); } } private void processSelectorWhileActive() throws IOException { SocketChannel newChannel; int soTimeout = getSoTimeout(); int selectionCount = 0; try { long timeout = soTimeout < 0 ? 0 : soTimeout; if (getDelayedReads().size() > 0 && (timeout == 0 || getReadDelay() < timeout)) { timeout = getReadDelay(); } selectionCount = this.selector.select(timeout); } catch (@SuppressWarnings("unused") CancelledKeyException cke) { logger.debug("CancelledKeyException during Selector.select()"); } while ((newChannel = this.newChannels.poll()) != null) { try { newChannel.register(this.selector, SelectionKey.OP_READ, this.channelMap.get(newChannel)); } catch (@SuppressWarnings("unused") ClosedChannelException cce) { logger.debug("Channel closed before registering with selector for reading"); } } processNioSelections(selectionCount, this.selector, null, this.channelMap); } /** * @return the usingDirectBuffers */ protected boolean isUsingDirectBuffers() { return this.usingDirectBuffers; } /** * @return the connections */ protected Map<SocketChannel, TcpNioConnection> getConnections() { return this.channelMap; } /** * @return the newChannels */ protected BlockingQueue<SocketChannel> getNewChannels() { return this.newChannels; } }