I am using CachingClientConnectionFactory, How can i keep the connections alive, its closing out after default remoteTimeOut elapses, Can I set the remoteTimeOut to LONG.MAX_VALUE?
Thanks
#Bean
public AbstractClientConnectionFactory clientConnectionFactory() {
TcpNioClientConnectionFactory tcpNioClientConnectionFactory = new TcpNioClientConnectionFactory(host, port);
tcpNioClientConnectionFactory.setUsingDirectBuffers(true);
tcpNioClientConnectionFactory.setApplicationEventPublisher(applicationEventPublisher);
return new CachingClientConnectionFactory(tcpNioClientConnectionFactory, connectionPoolSize);
}
#Bean
public MessageChannel outboundChannel() {
return new DirectChannel();
}
#Bean
#ServiceActivator(inputChannel = "outboundChannel")
public MessageHandler outboundGateway(AbstractClientConnectionFactory clientConnectionFactory) {
TcpOutboundGateway tcpOutboundGateway = new TcpOutboundGateway();
tcpOutboundGateway.setConnectionFactory(clientConnectionFactory);
tcpOutboundGateway.setRemoteTimeout(Long.MAX_VALUE);
tcpOutboundGateway.setRequestTimeout(5_000);
return tcpOutboundGateway;
}
Yes I want it to single thread, I want concurrent send to be blocked until the socket is available;
Yes; with that configuration, it will be single-threaded; concurrent requests will wait for up to requestTimeout to get access to the shared socket.
https://github.com/spring-projects/spring-integration/blob/4484c4da753096094e5b376411b94ac4ba2834c6/spring-integration-ip/src/main/java/org/springframework/integration/ip/tcp/TcpOutboundGateway.java#L217
try {
haveSemaphore = acquireSemaphoreIfNeeded(requestMessage);
connection = this.connectionFactory.getConnection();
...
The request that gets access to the socket then waits for up to replyTimeout for a reply. If it times out, the socket is closed, to avoid the next request getting this request's reply. The next request will get a new socket.
tcpOutboundGateway.setRemoteTimeout(Long.MAX_VALUE);
That should be reduced to something more reasonable otherwise you could block forever if, for some reason, the server doesn't reply (but keeps the socket open).
Related
I'm stuck on a seemingly simple task, but am out of ideas. I have a TcpInboundGateway attached to a TcpNetServerConnectionFactory that passes requests to a Service Activator. The Service Activator simply puts the message back on the Gateway's reply channel. I want the Gateway to return that message over the connection.
When I run the test, the message makes it to the Service Activator successfully. I know this because the Service Activator prints the message payload before returning it. I also know the Service Activator is putting the message on the right channel because I have an interceptor on that channel which also prints the message.
The problem seems to be that the Gateway isn't reading off of that channel, even though I set it in setReplyChannel(). I can also see this in the logs:
Adding {bridge:null} as a subscriber to the 'testResponseChannel' channel
which makes me suspect that the message is just getting sent to the null channel instead of being picked up by my Gateway.
Here's the configuration:
#Bean
public TcpNetServerConnectionFactory testServerFactory() {
TcpNetServerConnectionFactory testServerFactory = new TcpNetServerConnectionFactory(0);
testServerFactory.setSerializer(TcpCodecs.lengthHeader2());
testServerFactory.setDeserializer(TcpCodecs.lengthHeader2());
return testServerFactory;
}
#Bean
public DirectChannel testRequestChannel() {
return new DirectChannel();
}
#Bean
public DirectChannel testResponseChannel() {
DirectChannel testResponseChannel = new DirectChannel();
testResponseChannel.addInterceptor(channelInterceptor());
return testResponseChannel;
}
#Bean
public TcpInboundGateway gateway() {
TcpInboundGateway gateway = new TcpInboundGateway();
gateway.setConnectionFactory(testServerFactory());
gateway.setRequestChannel(testRequestChannel());
gateway.setReplyChannel(testResponseChannel());
return gateway;
}
#Bean
#ServiceActivator(inputChannel = "testRequestChannel", outputChannel = "testResponseChannel")
public EchoHandler echoHandler() {
return new EchoHandler();
}
Here's my POJO Service Activator:
public class EchoHandler {
public Message<String> echoMessage(Message<String> request) {
System.out.println(request.getPayload());
return request;
}
}
And here's the error, which happens right after the message passes through the interceptor:
Unexpected message - no endpoint registered with connection interceptor: localhost:6060:59848:3c6c3cff-c697-4fc9-b4e3-9ea14508cec7 - GenericMessage [payload=byte[3], headers={ip_tcp_remotePort=6060, ip_connectionId=localhost:6060:59848:3c6c3cff-c697-4fc9-b4e3-9ea14508cec7, ip_localInetAddress=/127.0.0.1, ip_address=127.0.0.1, id=93c75664-54db-c93e-ab3a-3e06b1e4b626, ip_hostname=localhost, timestamp=1556828832645}]
To react properly for the reply from the server, your client must be a request-response capable. For this purpose Spring Integration IP modules suggests a TcpOutboundGateway. This way a TcpListener is going to be registered on the TcpConnection and ready to parse and handle replies messages on the socket.
I am using Spring Redis template in a multi-threaded environment. One thread saves the data into Redis and other one (the scheduler) fetches the data from it. JedisConnectionFactory is used in redis template. Following is the code snippet for obtaining redis connection:
JedisConnectionFactory jedisConnectionFactory() {
JedisConnectionFactory jedisConnectionFactory = null;
try {
RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration(hostName,
port);
jedisConnectionFactory = new JedisConnectionFactory(redisStandaloneConfiguration);
} catch (RedisConnectionFailureException e) {
LOGGER.error("Connection break with redis " + e.getMessage());
}
return jedisConnectionFactory;
}
/**
* Redis template.
*
* #return the redis template
*/
#Bean
public RedisTemplate<String, Object> redisTemplate() {
final RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
template.setConnectionFactory(jedisConnectionFactory());
template.setValueSerializer(new GenericToStringSerializer<Object>(Object.class));
template.setEnableTransactionSupport(true);
return template;
}
The instance of redis template is obtained using constructor auto-wiring as follows:
#Autowired
public A(RedisTemplate<String, Object> redisTemplate) {
this.redisTemplate = redisTemplate;
}
I am getting an exception while fetching data from Redis using "findAll()" method of redis template:
org.springframework.data.redis.RedisConnectionFailureException: java.net.SocketException: Connection reset by peer: socket write error; nested exception is redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketException: Connection reset by peer: socket write error
Below are my findings:
connection reset by peer exception occurs when TCP socket is to be "closing" and your code to not have yet been notified. (the thread for findAll has not been notified for the closed connection).
Redis template is thread safe (only if connection pool is used) and handles connection management on its own. It may happen that when thread saved data into redis and closed the connection and during that only, the fetch operation occured and demanded data. In that case, the server might have issued RST command but fetch operation might have not obtained it.
Jedis pool config can be used; but there are depreciated methods in this that can later cause upgradation issues.
Please suggest best method to handle multi-threading with "JedisConnectionFactory", "RedisStandAloneConfiguration" and "RedisTemplate".
The cause of this problem was :
Redis Template is thread safe but only when it uses connection pooling; If connection pool is not used then the simultaneous connection calls result in RST signal from either (server / client) side and thus 'connection reset by peer' exception is thrown. SO, if we need to use Redis template then create a connection pool and set 'maxIdle' and 'maxTotal' for the pool config. Also, make sure that the system should not goes down (sleep) in any case.
Code that worked correctly:
#Bean
JedisConnectionFactory jedisConnectionFactory() {
JedisConnectionFactory jedisConnectionFactory = null;
try {
RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration(hostName,
port);
jedisConnectionFactory = new JedisConnectionFactory(redisStandaloneConfiguration);
jedisConnectionFactory.getPoolConfig().setMaxTotal(50);
jedisConnectionFactory.getPoolConfig().setMaxIdle(50);
} catch (RedisConnectionFailureException e) {
e.getMessage();
}
return jedisConnectionFactory;
}
#Bean
public RedisTemplate<String, Object> redisTemplate() {
final RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
template.setConnectionFactory(jedisConnectionFactory());
template.setValueSerializer(new GenericToStringSerializer<Object>(Object.class));
template.setEnableTransactionSupport(true);
return template;
}
I am using Spring's spring-integration-mqtt and i can connect to a single Mqtt server and can receive messages on subscribed topics , and now i want to make application which can connect to multiple Mqtt Servers and can receive data from every connection and i want to manage it as dynamic where i can add more Mqtt servers from database or text file.
a simple bean for single Mqtt connection for subscription is as follow
#Bean
public MessageProducer inbound() {
MqttPahoMessageDrivenChannelAdapter adapter2 =
new MqttPahoMessageDrivenChannelAdapter("tcp://192.168.100.1:1883","mqtt_virtual_received_sus_2",
"DATA/#", "LD/#","CONF/#","CONFIG/#");
adapter2.setCompletionTimeout(0);
adapter2.setConverter(new DefaultPahoMessageConverter());
adapter2.setQos(2);
adapter2.setOutputChannel(mqttInputChannel());
return adapter2;
}
above code creates a connection for the mqtt server and can receive messages and if i copy paste the same code twice for second server with different Mqtt ip address i can connect to both Mqtt Server as follows
#Bean
public MessageProducer inbound() {
MqttPahoMessageDrivenChannelAdapter adapter2 =
new MqttPahoMessageDrivenChannelAdapter("tcp://192.168.100.1:1883","mqtt_virtual_received_sus_2",
"DATA/#", "LD/#","CONF/#","CONFIG/#");
adapter2.setCompletionTimeout(0);
adapter2.setConverter(new DefaultPahoMessageConverter());
adapter2.setQos(2);
adapter2.setOutputChannel(mqttInputChannel());
return adapter2;
}
#Bean
public MessageProducer inbound2() {
MqttPahoMessageDrivenChannelAdapter adapter2 =
new MqttPahoMessageDrivenChannelAdapter("tcp://192.168.100.14:1883","mqtt_virtual_received_sus_1",
"DATA/#", "LD/#","CONF/#","CONFIG/#");
adapter2.setCompletionTimeout(0);
adapter2.setConverter(new DefaultPahoMessageConverter());
adapter2.setQos(2);
adapter2.setOutputChannel(mqttInputChannel());
return adapter2;
}
above code also works fine and i can receive message from both Mqtt Servers, but is there any way i can manage it dynamically like as follows, i change the bean's return type to list, but didn't worked:
#Bean
public List<MqttPahoMessageDrivenChannelAdapter> getAdapter () {
List<MqttPahoMessageDrivenChannelAdapter > logConfList=new ArrayList<MqttPahoMessageDrivenChannelAdapter>();
MqttPahoMessageDrivenChannelAdapter adapter2 =
new MqttPahoMessageDrivenChannelAdapter("tcp://192.168.100.1:1883","mqtt_virtual_received_sus_2",
"DATA/#", "LD/#","CONF/#","CONFIG/#");
adapter2.setCompletionTimeout(0);
adapter2.setConverter(new DefaultPahoMessageConverter());
adapter2.setQos(2);
adapter2.setOutputChannel(mqttInputChannel() );
MqttPahoMessageDrivenChannelAdapter adapter =
new MqttPahoMessageDrivenChannelAdapter("tcp://192.168.100.14:1883","mqtt_virtual_received_sus_1",
"DATA/#", "LD/#","CONF/#","CONFIG/#");
adapter.setCompletionTimeout(0);
adapter.setConverter(new DefaultPahoMessageConverter());
adapter.setQos(2);
adapter.setOutputChannel(mqttInputChannel() );
logConfList.add(adapter);
logConfList.add(adapter2);
return logConfList;
}
is there any way i can manage these beans dynamically, where i can fetch mqtt server details from text file and in a for loop or something i can manage multiple connections.
See Dynamic and runtime Integration Flows.
#Autowired
private IntegrationFlowContext flowContext;
private IntegrationFlowRegistration addAnAdapter(String uri, String clientId, MessageChannel channel,
String... topics) {
MqttPahoMessageDrivenChannelAdapter adapter = new MqttPahoMessageDrivenChannelAdapter(uri, clientId, topics);
// more adapter configuration
IntegrationFlow flow = IntegrationFlows.from(adapter)
.channel(channel)
.get();
return this.flowContext.registration(flow).register();
}
private void removeAdapter(IntegrationFlowRegistration flowReg) {
this.flowContext.remove(flowReg.getId());
}
I'm trying to make a chat system project (still incomplete) using java, but I need small help in forwarding the client's received message.
The server is working as multi-threading so many clients can connect to it, what I want is: (Assumed scenario) if 6 clients are connected to the server, then one of them send a message, it should be forwarded to all connected clients through the server.
here is the 2 codes..
Server side
import java.io.*;
import java.net.*;
public class ChatServer implements Runnable
{
Socket csocket;
ChatServer(Socket csocket){ this.csocket = csocket; }
public static void main(String[]args)throws Exception
{
ServerSocket sersock=new ServerSocket(3000);
System.out.println("Server ready for chatting");
while(true)
{
Socket sock =sersock.accept();
new Thread(new ChatServer(sock)).start();}
}
public void run()
{
try{
System.out.println(Thread.currentThread().getName() + ": HELLO");
BufferedReader keyRead=new BufferedReader(new InputStreamReader(System.in));
OutputStream ostream=csocket.getOutputStream();
PrintWriter pwrite=new PrintWriter(ostream, true);
InputStream istream=csocket.getInputStream();
BufferedReader receiveRead=new BufferedReader(new InputStreamReader(istream));
String receiveMessage, sendMessage;
while(true) {
if((receiveMessage=receiveRead.readLine())!=null)
{
System.out.print(Thread.currentThread().getName() + ": ");
if(receiveMessage.equals("QUIT"))
Thread.currentThread().stop();
else
System.out.println(receiveMessage);}
sendMessage=keyRead.readLine();
pwrite.println(sendMessage);
System.out.flush();}
} catch (IOException e){ System.out.println(e); }
}
}
client side
import java.io.*;
import java.net.*;
public class ChatClient
{
public static void main(String[]args)throws Exception
{
Socket sock =new Socket("localhost", 3000);
// reading from keyboard (keyRead object)
BufferedReader keyRead=new BufferedReader(new InputStreamReader(System.in));
// sending to client (pwrite object)
OutputStream ostream=sock.getOutputStream();
PrintWriter pwrite=new PrintWriter(ostream, true);
// receiving from server ( receiveRead object)
InputStream istream=sock.getInputStream();
BufferedReader receiveRead=new BufferedReader(new InputStreamReader(istream));
System.out.println("Start the chitchat, type and press Enter key");
String receiveMessage, sendMessage;
while(true)
{
sendMessage=keyRead.readLine();// keyboard reading
pwrite.println(sendMessage);// sending to server
System.out.flush();// flush the data
if((receiveMessage=receiveRead.readLine())!=null)//receive from server
{
System.out.println(receiveMessage);// displaying at DOS prompt
}
}
}
}
Thanks in advance.
I write these types of server applications frequently, as a way of connection client through a relay server when direct connections between them are not possible. The solution is simple, put each socket that you receive from the accept() function into a List or Tree of your choice.
List<Socket> connectionList = new ArrayList<Socket>();
...
Socket sock =sersock.accept();
connectionList.add(sock);
...
for (Socket connection : connectionList) {
connection.getOutputStream().write(msgBytes);
}
I am using HornetQ in distributed transaction environment with MDBs. I read from the JMS documentation that we should not create Connection instance frequently, rather we should reuse the connection and create JMS sessions as and when required. So I wrote a code which creates JMS connection and then reuse it. But I have encountered the following exception while reusing the JMS connection object.
Could not create a session: Only allowed one session per connection.
See the J2EE spec, e.g. J2EE1.4 Section 6.6
I read few blogs on this but they all are specific to seam framework.
Here is my code
public class DefaultService implements IMessageService {
private static final long serialVersionUID = 1L;
private static final Logger logger = LogManager.getLogger(DefaultService.class);
private static final String connectionFactoryJndiName = "java:/JmsXA";
private static volatile Connection connection = null;
private Session session = null;
#Override
public void sendMessage(String destinationStr, Serializable object) {
try {
Destination destination = jmsServiceLocator.getDestination(destinationStr);
ObjectMessage message = session.createObjectMessage();
message.setObject(object);
MessageProducer messageProducer = session.createProducer(destination);
messageProducer.send(destination, message);
messageProducer.close();
logger.trace("Sent JMS Messagae for: " + object.getClass().getName());
}
catch (NamingException e) {
throw new RuntimeException("Couldn't send jms message", e);
}
catch (JMSException e) {
throw new RuntimeException("Couldn't send jms message", e);
}
}
#Override
public void close() {
try {
if (session != null) {
session.close();
}
}
catch (Exception e) {
logger.error("Couldn't close session", e);
}
}
}
I am using JBoss EAP 6.
Did I miss any settings here?
On JCA connection (i.e. connection where you used the PooledConnectionFactory) you are supposed to create one Session only per connection. That is part of the EE specification. (It has always been).
This is because these connections are pooled and it would be impossible to put them back on the pool if you were using more than one session per connection.
If you switch for non pooled connection factories (the ones that are meant for remote clients) you would have it working the way you wanted but then you would miss pooling from the application server. EE components are usually short lived and opening / closing JMS Connections (any connection to be more precise) it's an expensive operation.