/*
* Copyright 2002-2016 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
*
* 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.springframework.integration.ip.tcp;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.atomic.AtomicInteger;
import org.springframework.integration.context.OrderlyShutdownCapable;
import org.springframework.integration.endpoint.MessageProducerSupport;
import org.springframework.integration.ip.IpHeaders;
import org.springframework.integration.ip.tcp.connection.AbstractClientConnectionFactory;
import org.springframework.integration.ip.tcp.connection.AbstractConnectionFactory;
import org.springframework.integration.ip.tcp.connection.AbstractServerConnectionFactory;
import org.springframework.integration.ip.tcp.connection.ClientModeCapable;
import org.springframework.integration.ip.tcp.connection.ClientModeConnectionManager;
import org.springframework.integration.ip.tcp.connection.ConnectionFactory;
import org.springframework.integration.ip.tcp.connection.TcpListener;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.ErrorMessage;
import org.springframework.util.Assert;
/**
* Tcp inbound channel adapter using a TcpConnection to
* receive data - if the connection factory is a server
* factory, this Listener owns the connections. If it is
* a client factory, the sender owns the connection.
*
* @author Gary Russell
* @since 2.0
*
*/
public class TcpReceivingChannelAdapter
extends MessageProducerSupport implements TcpListener, ClientModeCapable, OrderlyShutdownCapable {
private AbstractConnectionFactory clientConnectionFactory;
private AbstractConnectionFactory serverConnectionFactory;
private volatile boolean isSingleUse;
private volatile boolean isClientMode;
private volatile long retryInterval = 60000;
private volatile ScheduledFuture<?> scheduledFuture;
private volatile ClientModeConnectionManager clientModeConnectionManager;
private volatile boolean active;
private volatile boolean shuttingDown;
private final AtomicInteger activeCount = new AtomicInteger();
@Override
public boolean onMessage(Message<?> message) {
boolean isErrorMessage = message instanceof ErrorMessage;
try {
if (this.shuttingDown) {
if (logger.isInfoEnabled()) {
logger.info("Inbound message ignored; shutting down; " + message.toString());
}
}
else {
if (isErrorMessage) {
/*
* Socket errors are sent here so they can be conveyed to any waiting thread.
* There's not one here; simply ignore.
*/
return false;
}
this.activeCount.incrementAndGet();
try {
sendMessage(message);
}
finally {
this.activeCount.decrementAndGet();
}
}
return false;
}
finally {
String connectionId = (String) message.getHeaders().get(IpHeaders.CONNECTION_ID);
if (connectionId != null && !isErrorMessage && this.isSingleUse) {
if (this.serverConnectionFactory != null) {
// if there's no collaborating outbound adapter, close immediately, otherwise
// it will close after sending the reply.
if (this.serverConnectionFactory.getSender() == null) {
this.serverConnectionFactory.closeConnection(connectionId);
}
}
else {
this.clientConnectionFactory.closeConnection(connectionId);
}
}
}
}
@Override
protected void onInit() {
super.onInit();
if (this.isClientMode) {
Assert.notNull(this.clientConnectionFactory,
"For client-mode, connection factory must be type='client'");
Assert.isTrue(!this.clientConnectionFactory.isSingleUse(),
"For client-mode, connection factory must have single-use='false'");
}
}
@Override // protected by super#lifecycleLock
protected void doStart() {
super.doStart();
if (!this.active) {
this.active = true;
this.shuttingDown = false;
if (this.serverConnectionFactory != null) {
this.serverConnectionFactory.start();
}
if (this.clientConnectionFactory != null) {
this.clientConnectionFactory.start();
}
if (this.isClientMode) {
ClientModeConnectionManager manager = new ClientModeConnectionManager(
this.clientConnectionFactory);
this.clientModeConnectionManager = manager;
Assert.state(this.getTaskScheduler() != null, "Client mode requires a task scheduler");
this.scheduledFuture = this.getTaskScheduler().scheduleAtFixedRate(manager, this.retryInterval);
}
}
}
@Override // protected by super#lifecycleLock
protected void doStop() {
super.doStop();
if (this.active) {
this.active = false;
if (this.scheduledFuture != null) {
this.scheduledFuture.cancel(true);
}
this.clientModeConnectionManager = null;
if (this.clientConnectionFactory != null) {
this.clientConnectionFactory.stop();
}
if (this.serverConnectionFactory != null) {
this.serverConnectionFactory.stop();
}
}
}
/**
* Sets the client or server connection factory; for this (an inbound adapter), if
* the factory is a client connection factory, the sockets are owned by a sending
* channel adapter and this adapter is used to receive replies.
*
* @param connectionFactory the connectionFactory to set
*/
public void setConnectionFactory(AbstractConnectionFactory connectionFactory) {
if (connectionFactory instanceof AbstractClientConnectionFactory) {
this.clientConnectionFactory = connectionFactory;
}
else {
this.serverConnectionFactory = connectionFactory;
}
connectionFactory.registerListener(this);
this.isSingleUse = connectionFactory.isSingleUse();
}
public boolean isListening() {
if (this.serverConnectionFactory == null) {
return false;
}
if (this.serverConnectionFactory instanceof AbstractServerConnectionFactory) {
return ((AbstractServerConnectionFactory) this.serverConnectionFactory).isListening();
}
return false;
}
@Override
public String getComponentType() {
return "ip:tcp-inbound-channel-adapter";
}
/**
* @return the clientConnectionFactory
*/
protected ConnectionFactory getClientConnectionFactory() {
return this.clientConnectionFactory;
}
/**
* @return the serverConnectionFactory
*/
protected ConnectionFactory getServerConnectionFactory() {
return this.serverConnectionFactory;
}
/**
* @return the isClientMode
*/
@Override
public boolean isClientMode() {
return this.isClientMode;
}
/**
* @param isClientMode
* the isClientMode to set
*/
public void setClientMode(boolean isClientMode) {
this.isClientMode = isClientMode;
}
/**
* @return the retryInterval
*/
public long getRetryInterval() {
return this.retryInterval;
}
/**
* @param retryInterval
* the retryInterval to set
*/
public void setRetryInterval(long retryInterval) {
this.retryInterval = retryInterval;
}
@Override
public boolean isClientModeConnected() {
if (this.isClientMode && this.clientModeConnectionManager != null) {
return this.clientModeConnectionManager.isConnected();
}
else {
return false;
}
}
@Override
public void retryConnection() {
if (this.active && this.isClientMode && this.clientModeConnectionManager != null) {
this.clientModeConnectionManager.run();
}
}
@Override
public int beforeShutdown() {
this.shuttingDown = true;
return this.activeCount.get();
}
@Override
public int afterShutdown() {
this.stop();
return this.activeCount.get();
}
}