ReactiveException: java.lang.InterruptedException when I mark method with #HystrixCommand - java

I have following method:
#Timed(value = "my.request.timer", percentiles = {0.5, 0.95}, histogram = true)
public ResponseEntity<MyResponseDto> executeHttpCall(MyReq myReq) {
log.warn("!!!!! REAL METHOD!!!! for {}", myReq);
Mono<ResponseEntity<MyResponseDto>> responseEntityMono = webClient.post()
.bodyValue(myReq)
.retrieve()
.toEntity(MyResponseDto.class);
try {
return responseEntityMono.block();
} catch (Exception e) {
log.warn("EXCEPTION: ", e);
}
return null;
}
I wanted to add hystrix for that method so I got following:
#HystrixCommand(fallbackMethod = "nullResponse",
threadPoolProperties = {
#HystrixProperty(name = "coreSize", value = "1000"),
#HystrixProperty(name = "maxQueueSize", value = "7777"),
})
#Timed(value = "my.request.timer", percentiles = {0.5, 0.95}, histogram = true)
public ResponseEntity<MyResponseDto> executeHttpCall(MyReq myReq) {
log.warn("!!!!! REAL METHOD!!!! for {}", myReq);
Mono<ResponseEntity<MyResponseDto>> responseEntityMono = webClient.post()
.bodyValue(myReq)
.retrieve()
.toEntity(myResponseDto.class);
try {
return responseEntityMono.block();
} catch (Exception e) {
log.warn("EXCEPTION: ", e);
}
return null;
}
public ResponseEntity<MyResponseDto> nullResponse(MyReq myReq) {
log.warn("Fallback method invoked for {}", myReq);
fallbackMethodIvocationCount.increment();
return null;
}
It became a reason of error(it sometimes reproduces and sometimes - not)
2019-11-11 17:33:51.954 WARN 2996 --- [ConnectorImpl-9] b.m.a.p.MyDetectorAPIConnectorImpl : EXCEPTION:
2019-11-11 17:33:51.954 WARN 2996 --- [ConnectorImpl-9] b.m.a.p.MyDetectorAPIConnectorImpl : EXCEPTION:
reactor.core.Exceptions$ReactiveException: java.lang.InterruptedException
at reactor.core.Exceptions.propagate(Exceptions.java:336)
at reactor.core.publisher.BlockingSingleSubscriber.blockingGet(BlockingSingleSubscriber.java:85)
at reactor.core.publisher.Mono.block(Mono.java:1663)
at my.MyService.executeHttpCall(MyDetectorAPIConnectorImpl.java:89)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
What can be reason of this issue and how to fix it ?

It was because of hystrix default timeout. It is possible to disable this timeout to use following property:
hystrix.command.default.execution.timeout.enabled: false

Related

RetrySynchronizationManager.getContext() is null while writing Unit test cases

Java 17, SpringBoot application. I tried to implement a Retry logic. Functionally, the code is working perfectly, but when I'm trying to write a JUnit test for the same, it fails as RetrySynchronizationManager.getContext() is null.
Method:
#Retryable(maxAttemptsExpression = "${retry.maxRetry}",
backoff = #Backoff(delayExpression = "${retry.maxInterval}",
multiplierExpression = "${retry.delayMultiplier}", maxDelayExpression = "${retry.maxDelay}"))
public Session connect() {
retryCountConnect = RetrySynchronizationManager.getContext().getRetryCount();
Session sshSession = null;
try {
sshSession = sshSessionPool.getSession();
sshSession.connect();
return sshSession;
} catch (Exception e) {
log.error("Exception occurred during SshSftp connect, retry count:{}, error details:{}",
retryCountConnect, ExceptionUtils.getStackTrace(e));
throw new RuntimeException(e);
}
}
Test case:
class ConnectionTest {
#Mock
Session sshSession;
#Mock
SshSessionPool sshSessionPool;
#Mock
MockedStatic<BasicSshSessionPool> basicSshSessionPoolMockedStatic;
#Mock
MockedStatic<AopContext> aopContext;
Channel channel;
SshSftpConnection sshSftpConnection;
#BeforeEach
#SneakyThrows
void setUp() {
channel = Mockito.mock(ChannelExec.class);
MockitoAnnotations.openMocks(this);
basicSshSessionPoolMockedStatic.when(() -> BasicSshSessionPool.create(anyString(), anyString(), anyString(), anyInt())).thenReturn(sshSessionPool);
sshSftpConnection = spy(new SshSftpConnection("host", "username", "password", 22));
when(sshSessionPool.getSession()).thenReturn(sshSession);
when(sshSession.openChannel(anyString())).thenReturn(channel);
aopContext.when(AopContext::currentProxy).thenReturn(sshSftpConnection);
}
#AfterEach
void cleanUp() {
aopContext.close();
basicSshSessionPoolMockedStatic.close();
}
#Test
void connect() throws Exception {
doReturn(sshSession).when(sshSessionPool).getSession();
Session actual = sshSftpConnection.connect();
verify(sshSession, times(1)).connect();
assertEquals(sshSession, actual);
}
}
Error:
Cannot invoke "org.springframework.retry.RetryContext.getRetryCount()" because the return value of "org.springframework.retry.support.RetrySynchronizationManager.getContext()" is null
java.lang.NullPointerException: Cannot invoke "org.springframework.retry.RetryContext.getRetryCount()" because the return value of "org.springframework.retry.support.RetrySynchronizationManager.getContext()" is null
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
Any recommendations would be appreciated

Getting a NullPointerException During Initialization of Interface

I write a maven project named "error project". Its aim is to handle exceptions in other projects. It utilizes another maven project "configuration manager". The ultimate aim of configuration manager is to obtain database credentials from some xml files. In this project, it has "IExceptionService", it is proposed that other projects write their exceptions through this interface. This interface's concrete class is assigned at runtime by "DependencyResolver" class. The assignment has been done in "resolve" method as such:
#SuppressWarnings("unchecked")
public static <T> T resolve(Class<T> classType, HashMap<DependencyResolverParameter,String> parameterMap )
{
try {
if(classType == null)
throw new ArgumanBosHatasi("classType is null");
if(parameterMap == null)
throw new ArgumanBosHatasi(HashMap.class.toString());
assert classType != null : "classType == null";
if(classType.equals(IErrorRepository.class))
return (T)getIErrorRepository();
else if(classType.equals(IErrorFile.class))
return (T)getIErrorFile();
else if(classType.equals(ErrorBusinessRule.class))
return (T)getErrorBusinessRule();
else if(classType.equals(IErrorService.class))
{
try {
return (T)getIErrorService(parameterMap);
} catch (UygulamaHatasi applicationException) {
throw applicationException; }
}
else
throw new UnsupportedOperationException("Unknown class type.");
} catch (Exception exception) {
throw exception;
}
}
private static IErrorRepository getIErrorRepository() {
try
{
if(errorDatabase != null)
return errorDatabase;
KonfigurasyonYoneticisi yonetici = new KonfigurasyonYoneticisi();
String sifreKodu = "HATASIFRE";
String schema = yonetici.okuAyar("HATASCHEMA");
String tabloAdi = yonetici.okuAyar("HATATABLOSU");
String veritabaniUrl = yonetici.okuAyar("HATAVERITABANIURL"); //Here is line 92 in DependencyResolver
String kullaniciAdi = yonetici.okuAyar("HATAKULLANICIADI");
if(SozceIsleri.bosMu(sifreKodu))
throw new BasarisizIslemHatasi("sifreKodu boş geliyor.");
if(SozceIsleri.bosMu(schema))
throw new BasarisizIslemHatasi("schema boş geliyor.");
if(SozceIsleri.bosMu(tabloAdi))
throw new BasarisizIslemHatasi("tabloAdi boş geliyor.");
if(SozceIsleri.bosMu(veritabaniUrl))
throw new BasarisizIslemHatasi("veritabaniUrl boş geliyor.");
if(SozceIsleri.bosMu(kullaniciAdi))
throw new BasarisizIslemHatasi("kullaniciAdi boş geliyor.");
assert SozceIsleri.doluMu(sifreKodu) : "SozceIsleri.bosMu(sifreKodu)";
assert SozceIsleri.doluMu(schema) : "SozceIsleri.bosMu(schema)";
assert SozceIsleri.doluMu(tabloAdi) : "SozceIsleri.bosMu(tabloAdi)";
assert SozceIsleri.doluMu(veritabaniUrl) : "SozceIsleri.bosMu(veritabaniUrl)";
assert SozceIsleri.doluMu(kullaniciAdi) : "SozceIsleri.bosMu(kullaniciAdi)";
oracle.jdbc.OracleDriver oracleDriver = new oracle.jdbc.OracleDriver();
lock();
errorDatabase = new HataRepository(oracleDriver, veritabaniUrl,kullaniciAdi,sifreKodu,schema,tabloAdi);
openLock();
if(errorDatabase == null)
throw new BosIsaretciHatasi(MesajIsleri.<IErrorRepository>getirBosGeliyorMesaji(IErrorRepository.class));
return errorDatabase;
} catch (BosIsaretciHatasi | BasarisizIslemHatasi hata) {
throw hata;
}finally {
openLock();
}
}
"DependencyResolverParameter" is an enum and it only has the value "UYGULAMAANAHTAR". (APPLICATION KEY)
I call this method like that:
int applicationKey = 4;
HashMap<DependencyResolverParameter,String> map = new HashMap<DependencyResolverParameter,String>();
map.put(DependencyResolverParameter.APPLICATIONKEY, Integer.toString(applicationKey));
IExceptionService service = DependencyResolver.<IExceptionService>resolve(IExceptionService.class, map);
I define all necessary dependencies within the project. In the code above, the "service" variable is not null as expected and it writes trial error successfully whenever I write it inside a main method as below:
public static void main(String[] args) {
int applicationKey = 4;
HashMap<DependencyResolverParameter,String> map = new HashMap<DependencyResolverParameter,String>();
map.put(DependencyResolverParameter.APPLICATIONKEY, Integer.toString(applicationKey));
IExceptionService servis = DependencyResolver.<IExceptionService>resolve(IExceptionService.class, map);
System.out.println(String.format("is it null? %b", servis == null));
servis.report(new Exception("trial error. Do not care."));
}
However, when I write the code inside JUnit4 test I am getting "NullPointerException" as below:
#Test
public void getHataService_001() {
try
{
int applicationKey= 4;
HashMap<DependencyResolverParameter,String> map = new HashMap<DependencyResolverParameter,String>();
map.put(DependencyResolverParameter.APPLICATIONKEY, Integer.toString(applicationKey));
IExceptionService servis = DependencyResolver.<IExceptionService>resolve(IExceptionService.class, map);
assertNotNull(servis);
} catch (Exception exception) {
fail(exception.getMessage());
}
}
How can I solve that problem? Thanks in advance.
From the stacktrace, I deduce that there is a problem of reading settings from configuration class.
junit dependency :
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
</dependency>
stacktrace:
m:java.lang.NullPointerExceptionlm:nullst:hata.businesslayer.dependencyresolvers.DependencyResolver.resolve(DependencyResolver.java:92)
kisi.core.crosscuttingconcern.exceptionhandling.HataIsleri.getHataService(HataIsleri.java:229)
kisi.core.crosscuttingconcern.exceptionhandling.HataIsleri.raporlaHata(HataIsleri.java:75)
test.hata.HataServisTesti.getHataService_001(HataServisTesti.java:62)
java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
java.base/java.lang.reflect.Method.invoke(Method.java:567)org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
2020/07/09 14:41:34
m:java.lang.NullPointerException
lm:nullst:hata.businesslayer.dependencyresolvers.DependencyResolver.resolve(DependencyResolver.java:92)
test.hata.HataServisTesti.getHataService_001(HataServisTesti.java:56)
java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
java.base/java.lang.reflect.Method.invoke(Method.java:567)
org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)2020/07/09 14:41:34
It is because of an apache maven project inside a project. When I remove the apache maven project, it works.

Download Attachments with Spring Integration and POP3

I have a Spring Boot project that is leveraging Spring Integration. My goal is to to poll a POP3 mail server regularly, and download any attachments associated with those messages. My relevant Spring Config looks like this:
#Configuration
public class MailIntegrationConfig {
#Value("${path.output.temp}")
private String outPath;
#Bean
public MessageChannel mailChannel() {
return new DirectChannel();
}
#Bean
#InboundChannelAdapter(value = "mailChannel", poller = #Poller(fixedDelay = "16000"))
public MessageSource<Object> fileReadingMessageSource() {
var receiver = new Pop3MailReceiver("pop3s://user:passwordexample.com/INBOX");
var mailProperties = new Properties();
mailProperties.setProperty("mail.pop3.port", "995");
mailProperties.put("mail.pop3.ssl.enable", true);
receiver.setShouldDeleteMessages(false);
receiver.setMaxFetchSize(10);
receiver.setJavaMailProperties(mailProperties);
// receiver.setHeaderMapper(new DefaultMailHeaderMapper());
var source = new MailReceivingMessageSource(receiver);
return source;
}
#Bean
#ServiceActivator(inputChannel = "mailChannel")
public MessageHandler popMessageHandler() {
return new MailReceivingMessageHandler(outPath);
}
}
My MailReceivingMessageHandler class (partial)
public class MailReceivingMessageHandler extends AbstractMessageHandler {
private String outDir;
public MailReceivingMessageHandler(String outDir) {
var outPath = new File(outDir);
if (!outPath.exists()) {
throw new IllegalArgumentException(String.format("%s does not exist.", outDir));
}
this.outDir = outDir;
}
#Override
protected void handleMessageInternal(org.springframework.messaging.Message<?> message) {
Object payload = message.getPayload();
if (!(payload instanceof Message)) {
throw new IllegalArgumentException(
"Unable to create MailMessage from payload type [" + message.getPayload().getClass().getName()
+ "], " + "expected MimeMessage, MailMessage, byte array or String.");
}
try {
var msg = (Message) payload;
System.out.println(String.format("Headers [%s] Subject [%s]. Content-Type [%s].", msg.getAllHeaders(),
msg.getSubject(), msg.getContentType()));
this.handleMessage(msg);
} catch (IOException | MessagingException e) {
e.printStackTrace();
}
}
private void handleMessage(Message msg) throws MessagingException, IOException {
var cType = msg.getContentType();
if (cType.contains(MediaType.TEXT_PLAIN_VALUE)) {
handleText((String) msg.getContent());
} else if (cType.contains(MediaType.MULTIPART_MIXED_VALUE)) {
handleMultipart((Multipart) msg.getContent());
}
}
// See
// https://stackoverflow.com/questions/1748183/download-attachments-using-java-mail
private void handleMultipart(Multipart msgContent) throws MessagingException, IOException {
var mCount = msgContent.getCount();
for (var i = 0; i < mCount; i++) {
this.processAttachments(msgContent.getBodyPart(i));
}
}
private void processAttachments(BodyPart part) throws IOException, MessagingException {
var content = part.getContent();
if (content instanceof InputStream || content instanceof String) {
if (Part.ATTACHMENT.equalsIgnoreCase(part.getDisposition()) || !part.getFileName().isBlank()) {
var fName = String.format("%s.%s", UUID.randomUUID().toString(),
FilenameUtils.getExtension(part.getFileName()));
FileUtils.copyInputStreamToFile(part.getInputStream(), new File(outDir + File.separator + fName));
}
if (content instanceof Multipart) {
Multipart multipart = (Multipart) content;
for (int i = 0; i < multipart.getCount(); i++) {
var bodyPart = multipart.getBodyPart(i);
processAttachments(bodyPart);
}
}
}
}
}
Whenever I run my code using the config above, I receive the following error:
javax.mail.MessagingException: No inputstream from datasource;
nested exception is:
java.lang.IllegalStateException: Folder is not Open
at javax.mail.internet.MimeMultipart.parse(MimeMultipart.java:576)
at javax.mail.internet.MimeMultipart.getCount(MimeMultipart.java:312)
at com.midamcorp.data.mail.MailReceivingMessageHandler.handleMultipart(MailReceivingMessageHandler.java:70)
at com.midamcorp.data.mail.MailReceivingMessageHandler.handleMessage(MailReceivingMessageHandler.java:58)
at com.midamcorp.data.mail.MailReceivingMessageHandler.handleMessageInternal(MailReceivingMessageHandler.java:44)
at org.springframework.integration.handler.AbstractMessageHandler.handleMessage(AbstractMessageHandler.java:62)
at org.springframework.integration.handler.ReplyProducingMessageHandlerWrapper.handleRequestMessage(ReplyProducingMessageHandlerWrapper.java:58)
at org.springframework.integration.handler.AbstractReplyProducingMessageHandler.handleMessageInternal(AbstractReplyProducingMessageHandler.java:134)
at org.springframework.integration.handler.AbstractMessageHandler.handleMessage(AbstractMessageHandler.java:62)
at org.springframework.integration.dispatcher.AbstractDispatcher.tryOptimizedDispatch(AbstractDispatcher.java:115)
at org.springframework.integration.dispatcher.UnicastingDispatcher.doDispatch(UnicastingDispatcher.java:133)
at org.springframework.integration.dispatcher.UnicastingDispatcher.dispatch(UnicastingDispatcher.java:106)
at org.springframework.integration.channel.AbstractSubscribableChannel.doSend(AbstractSubscribableChannel.java:72)
at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:570)
at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:520)
at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:187)
at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:166)
at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:47)
at org.springframework.messaging.core.AbstractMessageSendingTemplate.send(AbstractMessageSendingTemplate.java:109)
at org.springframework.integration.endpoint.SourcePollingChannelAdapter.handleMessage(SourcePollingChannelAdapter.java:196)
at org.springframework.integration.endpoint.AbstractPollingEndpoint.messageReceived(AbstractPollingEndpoint.java:444)
at org.springframework.integration.endpoint.AbstractPollingEndpoint.doPoll(AbstractPollingEndpoint.java:428)
at org.springframework.integration.endpoint.AbstractPollingEndpoint.pollForMessage(AbstractPollingEndpoint.java:376)
at org.springframework.integration.endpoint.AbstractPollingEndpoint.lambda$null$3(AbstractPollingEndpoint.java:323)
at org.springframework.integration.util.ErrorHandlingTaskExecutor.lambda$execute$0(ErrorHandlingTaskExecutor.java:57)
at org.springframework.core.task.SyncTaskExecutor.execute(SyncTaskExecutor.java:50)
at org.springframework.integration.util.ErrorHandlingTaskExecutor.execute(ErrorHandlingTaskExecutor.java:55)
at org.springframework.integration.endpoint.AbstractPollingEndpoint.lambda$createPoller$4(AbstractPollingEndpoint.java:320)
at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54)
at org.springframework.scheduling.concurrent.ReschedulingRunnable.run(ReschedulingRunnable.java:93)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: java.lang.IllegalStateException: Folder is not Open
at com.sun.mail.pop3.POP3Folder.checkOpen(POP3Folder.java:562)
at com.sun.mail.pop3.POP3Folder.getProtocol(POP3Folder.java:592)
at com.sun.mail.pop3.POP3Message.getRawStream(POP3Message.java:154)
at com.sun.mail.pop3.POP3Message.getContentStream(POP3Message.java:251)
at javax.mail.internet.MimePartDataSource.getInputStream(MimePartDataSource.java:78)
at javax.mail.internet.MimeMultipart.parse(MimeMultipart.java:570)
... 35 more
Obviously, the root cause is clear - the POP3 folder is closed. I have seen solutions that would likely be able to handle when just the Java mail classes are used, but none with Spring Integration. My question is how does one properly control when a folder is open or closed using Spring Integration Mail? I realize the Pop3MailReceiver class has a .setAutoCloseFolder() method. Based on the Spring Docs, I assume I need to set that, along something like the following to my handler:
Closeable closeableResource = StaticMessageHeaderAccessor.getCloseableResource(message);
if (closeableResource != null) {
closeableResource.close();
}
However, if I set autoCloseFolder to false, it does not appear as if the message even ever "hits" my handler, so unfortunately being able to close the resource does not even matter at this point. That is, when autoClose is set to false, the 'handleMessageInternal()' method in my handler class is never reached even though there are indeed message on the POP3 server. Instead I just get a bunch of logs like this:
2020-06-26 15:26:54.523 INFO 15348 --- [ scheduling-1] o.s.integration.mail.Pop3MailReceiver : attempting to receive mail from folder [INBOX]
What am I missing?
Thanks.

How to get the error message in Controller or Route after handling exception in errorHandler?

I had to customized Sftp Inbound default handler LoggingHandler and using my own CustomizedErrorHandler which extends ErrorHandler. But I can't return any message to my controller after handling exceptions.
I was researching couple of days and I found nothing to show my customized message to my UI using Controller. Below are some code snippet from my CustomizedErrorHandler, SftpInboundConfiguration.
SftpInboundConfiguration
public IntegrationFlow fileFlow() {
SftpInboundChannelAdapterSpec spec = Sftp
.inboundAdapter(getSftpSessionFactory())
.preserveTimestamp(true)
.remoteDirectory(getSourceLocation())
.autoCreateLocalDirectory(true)
.deleteRemoteFiles(false)
.localDirectory(new File(getDestinationLocation()));
return IntegrationFlows
.from(spec, e -> e.id(BEAN_ID)
.autoStartup(false)
.poller(sftpPoller())
)
.channel(sftpReceiverChannel())
.handle(sftpInboundMessageHandler())
.get();
}
... ... ...
public PollerMetadata sftpPoller() {
PollerMetadata pollerMetadata = new PollerMetadata();
List<Advice> adviceChain = new ArrayList<>();
pollerMetadata.setErrorHandler(customErrorMessageHandler());
pollerMetadata.setTrigger(new PeriodicTrigger(5000));
return pollerMetadata;
}
... ... ...
private CustomErrorMessageHandler customErrorMessageHandler() {
return new CustomErrorMessageHandler(
controlChannel(),
BEAN_ID
);
}
CustomErrorMessageHandler
public class CustomErrorMessageHandler implements ErrorHandler {
private final MessageChannel CONTROL_CHANNEL;
private final String BEAN_ID;
public CustomErrorMessageHandler(
MessageChannel controlChannel,
String beanID
) {
this.CONTROL_CHANNEL = controlChannel;
this.BEAN_ID = beanID;
}
public void handleError(#NotNull Throwable throwable) {
final Throwable rootCause = ExceptionUtils.getRootCause(throwable);
if (rootCause instanceof MessagingException) {
log.error("MessagingException : {} ", rootCause.getMessage());
} else if (rootCause instanceof SftpException) {
log.error("SftpException : {}", rootCause.getMessage());
} ... ... ...
else {
log.error("Unknown : Cause : {} , Error : {}",
rootCause, rootCause.getMessage());
}
log.info("Stopping SFTP Inbound");
boolean is_stopped = CONTROL_CHANNEL.send(
new GenericMessage<>("#" + BEAN_ID + ".stop()"));
if (is_stopped) {
log.info("SFTP Inbound Stopped.");
} else {
log.info("SFTP Inbound Stop Failed.");
}
}
}
Now I want to save some customized message from if-else statements and need to show it in UI. Is there any way to save the message and show it using Route or Controller ?
Don't customize the error handler, use poller.errorChannel("myErrorChannel") instead.
Then add an error channel flow
#Bean
IntegrationFlow errors() {
return IntegrationFLows.from("myErrorChannel")
.handle(...)
...
.get();
The message sent to the handler is an ErrorMessage with a MessagingException payload, with cause and failedMessage which was the message at the point of the failure and originalMessage which is the original message emitted by the adapter.
After handling the exception, you can simply call a method on your controller to tell it the state.

How do i send a single message to multiple ip?

When ip contains a json-type,
Among the current connection to factory.getConnectionIds() to find the corresponding IP.
Then set the header to send the logic during development.
Through factory.getConnectionIds() found the IP list that is currently connected, I set up a header. but unable to find outbound socket error is occured.
What is the cause?
integration config is...
#Bean
public TcpReceivingChannelAdapter sslAdapter() {
TcpReceivingChannelAdapter adapter = new TcpReceivingChannelAdapter();
adapter.setConnectionFactory(sslServerFactory());
adapter.setOutputChannel(inputWithSSL());
return adapter;
}
#Bean
public TcpSendingMessageHandler sslHandler() {
TcpSendingMessageHandler handler = new TcpSendingMessageHandler();
handler.setConnectionFactory(sslServerFactory());
return handler;
}
#Bean
public AbstractConnectionFactory sslServerFactory() {
int port = Integer.parseInt(inboundPort);
TcpNioServerConnectionFactory factory = new TcpNioServerConnectionFactory(port);
factory.setBacklog(BACKLOG);
factory.setTaskExecutor(taskSchedulerWithSSL());
factory.setLookupHost(false);
factory.setSerializer(echoSerializer);
factory.setDeserializer(echoSerializer);
factory.setTcpNioConnectionSupport(tcpNioSSLConnectionSupport());
// Nagle's algorithm disabled
factory.setSoTcpNoDelay(true);
return factory;
}
#Bean
public IntegrationFlow flowForReceiveSslMessage() {
return IntegrationFlows
.from(sslAdapter)
.<byte[], Boolean>route(
p -> (short) 0 == ByteBuffer.wrap(p, 0, BYTE_LENGTH_OF_SHORT).getShort(),
m -> m.channelMapping(TRUE, INPUT_WITH_SSL_JSON)
.channelMapping(FALSE, INPUT_WITH_SSL_ECHO)).get();
}
#Bean
public IntegrationFlow flowForExtractingSslJson() {
return IntegrationFlows
.from(inputWithSslJson())
.handle(INBOUND_SERVICE, EXTRACT_PAYLOAD_AS_JSON)
.<Map<String, Object>, String>route(
p -> (String) p.get(REQUEST),
m -> m.channelMapping(LOGIN, INPUT_WITH_SSL_LOGIN)
.channelMapping(LOGOUT, INPUT_WITH_SSL_LOGOUT)
.channelMapping(POLICY, INPUT_WITH_SSL_POLICY)
.channelMapping(PUSH_TARGET, INPUT_WITH_SSL_PUSH_TARGET).get();
}
#Bean
public IntegrationFlow flowForHandlingSslNotifyPolicyUpdate() {
return IntegrationFlows.from(inputWithSslPushTarget()).handle(POLICY_SERVICE, RESPONSE_POLICY_UPDATE)
.split(POLICY_SERVICE, SPLIT_MESSAGES)
.channel(outputWithSslJsonBytesToClient()).get();
}
#Bean
public IntegrationFlow flowForConvertingSslJsonToBytesAndSendClient() {
return IntegrationFlows.from(outputWithSslJsonBytesToClient())
.transform(new ObjectToJsonTransformer())
.handle(INBOUND_SERVICE, ATTACH_HEADER_BY_STRING).handle(sslHandler).get();
}
#Bean
public MessageChannel outputWithSsl() {
return MessageChannels.queue(POOL_SIZE).get();
}
#Bean
public MessageChannel inputWithSslJson() {
return MessageChannels.queue(POOL_SIZE).get();
}
#Bean
public MessageChannel inputWithSslPushTarget() {
return MessageChannels.queue(POOL_SIZE).get();
}
#Bean
public MessageChannel outputWithSslJsonBytesToClient() {
return MessageChannels.queue(POOL_SIZE).get();
}
RESPONSE_POLICY_UPDATE and SPLIT_MESSAGES is...
#Override
public Object responsePolicyUpdate(Object payload) throws Exception {
log.debug("notify policy update debug : {}", payload);
Map<String, Object> params = initParam(payload);
Map<String, Object> result = initResult(params);
result.put(RESPONSE, PUSH_TARGET);
result.put(RESULT, SUCCESS);
result.put(REASON, 0);
return result;
}
#Splitter
#Override
#SuppressWarnings("unchecked")
public List<Message<String>> splitMessages(Object payload) throws Exception {
log.debug("split messages debug : {}", payload);
Map<String, Object> params = initParam(payload);
List<String> pushTargetList = (List<String>) params.get(PUSH_TARGET_LIST); // pushTargetList is ip list.
List<Message<String>> messageList = new ArrayList<Message<String>>();
String[] conArray = new String[4];
List<String> sslConnectionIds = sslServerFactory.getOpenConnectionIds();
int sslPort = sslServerFactory.getPort();
for (String con : sslConnectionIds) {
log.debug("## con ip : {}", con);
conArray = con.split(":");
for (String pushTargetIP : pushTargetList) {
if (conArray[0].equals(pushTargetIP)) {
Message<String> message = MessageBuilder.withPayload(params.toString())
.setHeader("ip_connectionId", con).build();
messageList.add(message);
break;
}
}
}
return messageList;
}
debug log is...
The first line is the current connection list.
2016-07-05 14:30:14.664 DEBUG 56092 --- [sk-scheduler-10] c.m.j.policy.service.PolicyServiceImpl : ## con ip : 192.168.3.57:62370:5443:cdeb011d-91f5-46c4-abc9-b68ba13624b3
2016-07-05 14:30:14.672 DEBUG 56092 --- [ask-scheduler-1] o.s.i.ip.tcp.TcpSendingMessageHandler : plainHandler received message: GenericMessage [payload=byte[246], headers={sequenceNumber=1, json__TypeId__=class java.lang.String, sequenceSize=1, ip_connectionId=192.168.3.57:62370:5443:cdeb011d-91f5-46c4-abc9-b68ba13624b3, correlationId=fae71250-bf47-3f64-6ad3-1ce22ef69464, id=c6c097f0-9efb-f0a5-4240-924e06879b7f, contentType=application/json, timestamp=1467696614672}]
2016-07-05 14:30:14.672 ERROR 56092 --- [ask-scheduler-1] o.s.i.ip.tcp.TcpSendingMessageHandler : Unable to find outbound socket for GenericMessage [payload=byte[246], headers={sequenceNumber=1, json__TypeId__=class java.lang.String, sequenceSize=1, ip_connectionId=192.168.3.57:62370:5443:cdeb011d-91f5-46c4-abc9-b68ba13624b3, correlationId=fae71250-bf47-3f64-6ad3-1ce22ef69464, id=c6c097f0-9efb-f0a5-4240-924e06879b7f, contentType=application/json, timestamp=1467696614672}]
2016-07-05 14:30:14.673 DEBUG 56092 --- [ask-scheduler-1] o.s.i.channel.PublishSubscribeChannel : preSend on channel 'errorChannel', message: ErrorMessage [payload=org.springframework.messaging.MessageHandlingException: Unable to find outbound socket, headers={id=273f4477-52cf-645b-d157-e22dc7cc781a, timestamp=1467696614673}]
2016-07-05 14:30:14.673 DEBUG 56092 --- [ask-scheduler-1] o.s.integration.handler.LoggingHandler : (inner bean)#6dc2279c received message: ErrorMessage [payload=org.springframework.messaging.MessageHandlingException: Unable to find outbound socket, headers={id=273f4477-52cf-645b-d157-e22dc7cc781a, timestamp=1467696614673}]
2016-07-05 14:30:14.675 ERROR 56092 --- [ask-scheduler-1] o.s.integration.handler.LoggingHandler : org.springframework.messaging.MessageHandlingException: Unable to find outbound socket
at org.springframework.integration.ip.tcp.TcpSendingMessageHandler.handleMessageInternal(TcpSendingMessageHandler.java:113)
at org.springframework.integration.handler.AbstractMessageHandler.handleMessage(AbstractMessageHandler.java:78)
at
...
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
Thanks Gary.
As you say, an error occurs in the method of handleMessageInternal TcpSendingMessageHandler class.
Get connectionId is...
2016-07-06 10:04:28.704 DEBUG 30144 --- [ask-scheduler-4] c.m.j.policy.service.PolicyServiceImpl : ## con ip : 192.168.3.57:53759:5443:bf93680b-13fe-401b-a1eb-5545917f404a
connectionId is not null. But, the result of connections.get (connectionId) is null.
This should not be understood.
This is the TcpSendingMessageHandler class...
/**
* Writes the message payload to the underlying socket, using the specified
* message format.
* #see org.springframework.messaging.MessageHandler#handleMessage(org.springframework.messaging.Message)
*/
#Override
public void handleMessageInternal(final Message<?> message) throws
MessageHandlingException {
if (this.serverConnectionFactory != null) {
// We don't own the connection, we are asynchronously replying
Object connectionId = message.getHeaders().get(IpHeaders.CONNECTION_ID);
TcpConnection connection = null;
if (connectionId != null) {
connection = connections.get(connectionId);
}
if (connection != null) {
try {
connection.send(message);
}
catch (Exception e) {
logger.error("Error sending message", e);
connection.close();
if (e instanceof MessageHandlingException) {
throw (MessageHandlingException) e;
}
else {
throw new MessageHandlingException(message, "Error sending message", e);
}
}
}
else {
logger.error("Unable to find outbound socket for " + message);
throw new MessageHandlingException(message, "Unable to find outbound socket");
}
return;
}
else {
// we own the connection
try {
doWrite(message);
}
catch (MessageHandlingException e) {
// retry - socket may have closed
if (e.getCause() instanceof IOException) {
if (logger.isDebugEnabled()) {
logger.debug("Fail on first write attempt", e);
}
doWrite(message);
}
else {
throw e;
}
}
}
}
This is Message<String> list...
The value obtained by factory.getOpenConnectionIds() method to get into the ip_connectionId.
Why not find a outboud socket?
GenericMessage [payload={result=success, reason=0, response=pushTarget}, headers={ip_connectionId=192.168.3.57:58187:5443:37702eaf-0bbc-44a1-8763-65e841a2f480, id=a1b80cc4-3f56-1b80-9c59-57be98b1031e, timestamp=1467783978378}]
GenericMessage [payload={result=success, reason=0, response=pushTarget}, headers={ip_connectionId=192.168.3.40:53161:5443:693c394c-d3dd-42a3-95ce-692a39a8b603, id=bb49ea99-5e3b-eccf-df3b-7ce03b4bbf73, timestamp=1467783978378}]

Categories