-
Notifications
You must be signed in to change notification settings - Fork 1.1k
GH-3732 make mqttClient initialize lazily #3747
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please, find some review on the lines.
Thank you so far!
@@ -140,7 +141,7 @@ protected MqttMessageConverter getConverter() { | |||
} | |||
|
|||
@ManagedAttribute | |||
public String[] getTopic() { | |||
public String[] getTopicNames() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why did you rename the method?
I might be agree that your one has more sense, but for consistency with others we need to keep the old name.
See addTopic()
, removeTopic()
.
Even if we call it getTopics()
, we still need to keep the old one for backward compatibility.
Even if we deprecate it.
I mean the bad method name is not a strong justification to introduce a braking change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree, you are right. I will leave the name of the method unchanged, thanks
try { | ||
this.mqttClient.unsubscribe(topics).waitForCompletion(getCompletionTimeout()); | ||
this.mqttClient.disconnect().waitForCompletion(getCompletionTimeout()); | ||
} | ||
catch (MqttException ex) { | ||
} catch (MqttException ex) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's not correct code style.
Please, run ./gradlew clean :spring-integration-mqtt:check
to see all the Checkstyle violations.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, sure, I am sorry for violating the project code style, I will polish the PR before merge. Thanks!
@@ -187,33 +190,40 @@ protected void doStart() { | |||
@Override | |||
protected void doStop() { | |||
this.topicLock.lock(); | |||
String[] topics = getTopic(); | |||
initializeMqttAsyncClientIfRequired(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's something wrong in the stop()
logic.
if there is not client initialized why would one wants to initialize it form stopping?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you are right, in general. There is clearly, from a logical perspective no need to initialize mqttClient
in case we are done with this ChannelAdapter
, so I agree.
But we still need to avoid NullPointerException
, right? Falling with this exception will not be good, so maybe we can end up with the following scenario: we just check, whether mqttClient
is not null
, and if so, we will apply disconnection logic, otherwise we will just do nothing
logger.error(ex, "Failed to close 'MqttAsyncClient'"); | ||
} | ||
} | ||
|
||
@Override | ||
public void addTopic(String topic, int qos) { | ||
this.topicLock.lock(); | ||
initializeMqttAsyncClientIfRequired(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's not OK, too.
The addTopic()
could be called from the Spring context initialization phase. Therefore it is too aggressive to make a low-level resource connection.
I believe the logic here must be similar to the one in the MqttPahoMessageDrivenChannelAdapter
:
if (this.client != null && this.client.isConnected()) {
this.client.subscribe(topic, qos);
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, so I got the idea. We will just subscribe to the passed topic with passed QoS in case the mqttClient
have being initialized, otherwise we will just maybe print a warning, just to notify the user.
@artembilan I think we should also omit the super.addTopic(topic, qos)
invocation in the case of mqttClient
have not been initialized, in order to not to get in trouble to return with getTopic()
the name of the topic onto which we did not subscribed really.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@artembilan I have adjusted the PR after your comments, can you, please, take a look again?
@@ -187,33 +190,40 @@ protected void doStart() { | |||
@Override | |||
protected void doStop() { | |||
this.topicLock.lock(); | |||
String[] topics = getTopic(); | |||
initializeMqttAsyncClientIfRequired(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you are right, in general. There is clearly, from a logical perspective no need to initialize mqttClient
in case we are done with this ChannelAdapter
, so I agree.
But we still need to avoid NullPointerException
, right? Falling with this exception will not be good, so maybe we can end up with the following scenario: we just check, whether mqttClient
is not null
, and if so, we will apply disconnection logic, otherwise we will just do nothing
try { | ||
this.mqttClient.unsubscribe(topics).waitForCompletion(getCompletionTimeout()); | ||
this.mqttClient.disconnect().waitForCompletion(getCompletionTimeout()); | ||
} | ||
catch (MqttException ex) { | ||
} catch (MqttException ex) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, sure, I am sorry for violating the project code style, I will polish the PR before merge. Thanks!
logger.error(ex, "Failed to close 'MqttAsyncClient'"); | ||
} | ||
} | ||
|
||
@Override | ||
public void addTopic(String topic, int qos) { | ||
this.topicLock.lock(); | ||
initializeMqttAsyncClientIfRequired(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, so I got the idea. We will just subscribe to the passed topic with passed QoS in case the mqttClient
have being initialized, otherwise we will just maybe print a warning, just to notify the user.
@artembilan I think we should also omit the super.addTopic(topic, qos)
invocation in the case of mqttClient
have not been initialized, in order to not to get in trouble to return with getTopic()
the name of the topic onto which we did not subscribed really.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I also would like to see some simple unit test to verify the we don't have a reported in the issue NPE any more.
Thanks
this.mqttClient.subscribe(topic, qos).waitForCompletion(getCompletionTimeout()); | ||
super.addTopic(topic, qos); | ||
} else { | ||
logger.warn(String.format("An attempt to add topic : '%s' with QoS : '%s' without having mqtt client initialized or connected, the topic will not be added!", topic, qos)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think it is so bad to store topics (super.addTopic(topic, qos);
) for the future use.
Something similar what we have so far in the MqttPahoMessageDrivenChannelAdapter
.
So, please, change the logic over here to the old one and subscribe only if there is an mqttClient
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, ok, perhaps) I just retained a warning in case mqttClient
is not initialized
} | ||
} | ||
|
||
private void initializeMqttAsyncClient() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method becomes and obsolete right now, so better to restore the old code of the onInit()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's right, I have removed it
@artembilan I have supplied the RP with 2 simple unit tests and squashed commits into one, can you please review? Thanks |
That was not good, since now I have to review all your changes. Not problem for now: jut want to give you a hint how it would be easier to collaborate in the future. 😄 |
this.mqttClient.unsubscribe(topics).waitForCompletion(getCompletionTimeout()); | ||
this.mqttClient.disconnect().waitForCompletion(getCompletionTimeout()); | ||
} else { | ||
logger.warn("An attempt to stop mqtt client without having one initialized or connected"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still want you to remove this warn
.
The point is that component could not be started at all from the beginning for some reasons, but stop()
is called when we close our application context.
Or the stop might be called via some centralized logic.
And the main feature of the stop is to be idempotent: nothing to stop - just exit silently!
So, such a warning would be a noise in logs in some target applications.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree, that make sense, I will remove this warning, thank you!
if (mqttClient != null) { | ||
this.mqttClient.close(true); | ||
} | ||
} catch (MqttException ex) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please, run gradle :spring-integration-mqtt:check
to revise your Checkstyle violations and fix them, please.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I have fixed all of the Checkstyle violations
if (mqttClient != null && mqttClient.isConnected()) { | ||
this.mqttClient.subscribe(topic, qos).waitForCompletion(getCompletionTimeout()); | ||
} else { | ||
logger.warn(String.format("An attempt to add topic : '%s' with QoS : '%s' without having mqtt client initialized or connected", topic, qos)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This one has to be removed, too.
It is fully OK to add topics which could be subscribed later on, when start()
is called.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also fixed
if (mqttClient != null && mqttClient.isConnected()) { | ||
this.mqttClient.unsubscribe(topic).waitForCompletion(getCompletionTimeout()); | ||
} else { | ||
logger.warn(String.format("An attempt to remove topics : '%s' without having mqtt client initialized or connected", Arrays.toString(topic))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
DITTO
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also fixed
class Mqttv5PahoMessageDrivenChannelAdapterTest { | ||
|
||
@Test //GH-3732 | ||
public void testNoNpeIsNotThrownInCaseDoInitIsNotInvokedBeforeTopicAddition() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about just add these new tests into an existing Mqttv5BackToBackTests
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I did not noticed this test class, I will move the test methods into Mqttv5BackToBackTests
…ding or removing topic
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have fixed all of your suggestions and verified the code with Checkstyle, sorry for being annoying :)
this.mqttClient.unsubscribe(topics).waitForCompletion(getCompletionTimeout()); | ||
this.mqttClient.disconnect().waitForCompletion(getCompletionTimeout()); | ||
} else { | ||
logger.warn("An attempt to stop mqtt client without having one initialized or connected"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree, that make sense, I will remove this warning, thank you!
class Mqttv5PahoMessageDrivenChannelAdapterTest { | ||
|
||
@Test //GH-3732 | ||
public void testNoNpeIsNotThrownInCaseDoInitIsNotInvokedBeforeTopicAddition() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I did not noticed this test class, I will move the test methods into Mqttv5BackToBackTests
if (mqttClient != null) { | ||
this.mqttClient.close(true); | ||
} | ||
} catch (MqttException ex) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I have fixed all of the Checkstyle violations
if (mqttClient != null && mqttClient.isConnected()) { | ||
this.mqttClient.unsubscribe(topic).waitForCompletion(getCompletionTimeout()); | ||
} else { | ||
logger.warn(String.format("An attempt to remove topics : '%s' without having mqtt client initialized or connected", Arrays.toString(topic))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also fixed
if (mqttClient != null && mqttClient.isConnected()) { | ||
this.mqttClient.subscribe(topic, qos).waitForCompletion(getCompletionTimeout()); | ||
} else { | ||
logger.warn(String.format("An attempt to add topic : '%s' with QoS : '%s' without having mqtt client initialized or connected", topic, qos)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also fixed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pulling locally for the final review and possible merge...
Merged and cherry-picked to thank you very much for the contribution; looking forward for more! |
Fixing #3732 issue. Also simplified the
Topic#equals()
method.