Skip to content
This repository has been archived by the owner on May 7, 2020. It is now read-only.

MqttService: Remove all deprecated methods, rename close to stop. #4173

Merged
merged 1 commit into from
Mar 29, 2018

Conversation

davidgraeff
Copy link
Contributor

@davidgraeff davidgraeff commented Sep 1, 2017

This PR prepares the io.transport.mqtt bundle for the discussed MQTT architecture.

  • Remove name from MqttBrokerConnection. This is only necessary for the MqttService.
    Consumers of MqttBrokerConnection should not be bothered with this detail.
  • The MqttService requires a brokerID for addConnection() and removeConnection() and the observer interface adds the brokerID next to the connection as well, to compensate for the removed name.
  • Remove textualConfigured flag. Again this is not interesting for anything outside MqttService.
  • Don't parse configuration parameters ourselves, but use a configuration values class instead.
  • Use Nullable where it makes sense.
  • The code was modernized
  • ESH compatible implementation of the service factory pattern including a config description for GUIs
  • Add MqttService.setLastWill method. Fixes Unable to use MQTT Last Will and Testament with dynamic data #5162

API break:

  • All deprecated methods are removed.
  • MqttBrokerConnection.close -> stop
  • MqttBrokerConnection.getURL() -> getHost(), getPort(), isSecure().
  • MqttBrokerConnection constructor changed

Config interface break:

  • There is no "url" anymore, it is now "host"+"port"+"secure"
  • "retain" is now "retainMessages"
  • "user" is now "username"
  • "pass" is now "password"
  • "lw" is now "lwtTopic"+"lwtMessage"+"lwtQos"+"lwtRetain"
  • "clientid" is now "clientID"
  • Uses OSGI service factories.

Documentation

  • Add more public class documentation especially for MqttService and its purpose.

Fixes #4170

Signed-off-by: David Gräff david.graeff@web.de

@kaikreuzer
Copy link
Contributor

Oops, merge conflicts...

@davidgraeff davidgraeff force-pushed the mqttservice branch 2 times, most recently from 370b6cf to 64d48dd Compare September 1, 2017 19:20
Copy link
Contributor

@maggu2810 maggu2810 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is it necessary that the host, port, secure flag for a broker connection to be changed after construction?
Wouldn't it be simpler to assign a host and port on construction and create another broker connection object if the host or port should be changed?
Immutable members are thread safe.
What is the behavior if host is changed while connected, what is a reconnect is done etc.
Isn't it common practice that such stuff is only set on construction (e.g. similar to the Java socket)?
Same for the other stuff, too.
If connection parameters that are used on connection establishment only could be changed all the time... it doesn't make the implementation and knowledge what is applied at which done easier.

@davidgraeff
Copy link
Contributor Author

davidgraeff commented Sep 4, 2017

I agree. The changes would be:

  • Make host, port, secure, clientID final
  • The port parameter could also be of type "@nullable Integer" to distinguish between a not set port and a given port number.
  • The clientID must be set in the constructor. Null is allowed, an ID will be generated in that case.

Everything else (qos, reconnect time etc) aren't connection parameters but take effect immediately for all following published messages.

@davidgraeff
Copy link
Contributor Author

I changed the PR accordingly. I'm just not sure about username and password. Those are technically speaking not connection parameters and can change between stop/start cycles and are optional.

throw new ConfigurationException("MQTT Broker property 'name' is not provided");
}
brokerID = brokerID.toLowerCase();
public synchronized MqttBrokerConnection addBrokerConnection(@NonNull String brokerID,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kaikreuzer Do I remember correctly that you allowed @Nullable on return types to enable checks for the consumer code?
If it is allowed I would prefer to add it here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, Nullable is the java standard. I agree that it sometimes makes sense to add it, as a way to document parameters, for instance Integer: A consumer might feel like he always needs to provide a value, even if null would be absolutely acceptable.

But for return types I would say Nullable is not reasonable.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please read #4080

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nah a twenty page long discussion doesn't help :) A link or quote of the developer guidelines would convince me of course :D

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There isn't a developer guidelines about the usage of nullness annotation. At least we didn't fully agree.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At least everything that has been agreed on, should be put in the developer guidelines. Should I open an issue?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think so. I have added a summary here:
https://github.com/eclipse/smarthome/wiki/Type-annotations

synchronized (brokerConnections) {
return brokerConnections.get(brokerName.toLowerCase());
}
public synchronized MqttBrokerConnection getBrokerConnection(String brokerName) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See other comment about Nullable on return type.

@davidgraeff davidgraeff force-pushed the mqttservice branch 4 times, most recently from 5ca6321 to a8d6746 Compare September 8, 2017 09:40
@kaikreuzer
Copy link
Contributor

@maggu2810 Was this PR now ok for you?
If so, what might be left is to adapt the use of null annotations to our new guideline (i.e. using @NonNullByDefault on the class level.

return url;
@Nullable
public Integer getPort() {
return port;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the constructor you state.

* @param port A port or null to select the default port for a secure or insecure connection

So I would assume that a port is choosen now and we could return the explicit set one, the insecure or secure default.
I am pretty sure that a connection uses a port ;)

@maggu2810
Copy link
Contributor

@maggu2810 Was this PR now ok for you?

If the MQTT transport and reconnect is still working (using the non-deprecated methods), all is okay for me. 😉
Didn't test it myself.

@maggu2810
Copy link
Contributor

maggu2810 commented Sep 22, 2017

Parts of the nullness fixes could be taken from here: ae1a8ac

@davidgraeff
Copy link
Contributor Author

  • I adapted the PR including the Nullness fixes.
  • I removed the custom nested configuration parser, as we agreed in Support hierarchical configuration & exclusive cfg files #4178, that a central place to parse nested configuration is a good idea, and I expect the "Configuration" class to handle the details for me.

At the moment that means, this service will not be configurable without changes to the "Configuration" class.

@davidgraeff
Copy link
Contributor Author

davidgraeff commented Mar 15, 2018

Because my other Mqtt PRs are based on the API changes, introduced here, I will resurrect this PR. I will also adapt it to the service multi-config concept.

@htreu
Copy link
Contributor

htreu commented Mar 16, 2018

@davidgraeff ping me when ready for review.

@davidgraeff
Copy link
Contributor Author

@htreu Code-wise it is complete. But travis failed because of another issue, therefore I do not know if if will pass the static code check yet.

Could you have a look at the MqttBrokerConnectionServiceInstance file, if I'm using the service-factory in a right way?
https://github.com/eclipse/smarthome/pull/4173/files#diff-9365221333b769c30777cfc2914e2001R39

@htreu
Copy link
Contributor

htreu commented Mar 16, 2018

Travis seems to be unrelated.

For the MqttBrokerConnectionServiceInstance please have a look at the MagicMultiInstanceService and the MagicMultiInstanceServiceMarker classes.

  • MagicMultiInstanceService is the implementation of the actual service which can be configured multiple times.
  • MagicMultiInstanceServiceMarker is a marker class which mainly carries the relevant information about the service to Paper UI.

The ConfigurableService.SERVICE_PROPERTY_DESCRIPTION_URI + "=test:multipleMagic" refers to the ESH-INF/config/multipleConfig.xml file where the configuration for the service is described.

Once you have everything setup correctly Paper UI should allow you to add/edit/remove instances of your service.

@davidgraeff
Copy link
Contributor Author

@htreu If have adjusted the PR. Could you have a look?

Copy link
Contributor

@htreu htreu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @davidgraeff good to see this progressing. Please find my comments inline.

public @Nullable String password;
public @Nullable String clientID;
// MQTT parameters
public BigDecimal qos = BigDecimal.valueOf(MqttBrokerConnection.DEFAULT_QOS);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When configuring a new service instance from Paper UI this fails with:

10:36:02.939 WARN  o.e.s.c.core.Configuration[:108] - Could not set field value for field 'qos': Can not set java.math.BigDecimal field org.eclipse.smarthome.io.transport.mqtt.MqttBrokerConnectionConfig.qos to java.lang.String
java.lang.IllegalArgumentException: Can not set java.math.BigDecimal field org.eclipse.smarthome.io.transport.mqtt.MqttBrokerConnectionConfig.qos to java.lang.String
	at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:167)
	at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:171)
	at sun.reflect.UnsafeObjectFieldAccessorImpl.set(UnsafeObjectFieldAccessorImpl.java:81)
	at java.lang.reflect.Field.set(Field.java:764)
	at org.apache.commons.lang.reflect.FieldUtils.writeField(FieldUtils.java:523)
	at org.apache.commons.lang.reflect.FieldUtils.writeField(FieldUtils.java:500)
	at org.apache.commons.lang.reflect.FieldUtils.writeField(FieldUtils.java:560)
	at org.eclipse.smarthome.config.core.Configuration.as(Configuration.java:105)
	at org.eclipse.smarthome.io.transport.mqtt.internal.MqttBrokerConnectionServiceInstance.modified(MqttBrokerConnectionServiceInstance.java:76)
	at org.eclipse.smarthome.io.transport.mqtt.internal.MqttBrokerConnectionServiceInstance.activate(MqttBrokerConnectionServiceInstance.java:107)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

I'm not aware of the configuration conversion logic, but maybe Integer will do?

public Boolean retainMessages = false;
public @Nullable String lastWill;
/** Keepalive in seconds */
public @Nullable BigDecimal keepAlive;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here as for qos.

* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
* Copyright (c) 2014-2017 by the respective copyright holders.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just run mvn license:format on your sources again.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately I do have to correct license headers myself. mvn license:format only corrects xml headers for me after 10min of downloading various files (it is time for gradle!). Therefore, If you see another spot, please let the know.

logger.warn("Ignore invalid broker connection configuration: {}", config);
return;
}
assert brokerID != null;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should already be handled by StringUtils.isBlank(brokerID)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct, but at least my eclipse will issue a warning. I'll investigate.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The static analyser, used by eclipse, is not recognizing the result of StringUtils.isBlank. The assert is unfortunately necessary here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove it and ignore the warning. Or add an extra null check to the StringUtils check.

assert brokerID != null;

// Add connection and make sure it succeeded
assert mqttService != null;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is already checked by line 71 (null check against config and mqttservice).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True, but eclipse is issuing a warning. I guess that's because mqttService is a field variable and the static analyser doesn't guarantee that no concurrent thread or side effect of a method call changes the field. I would leave it for now.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove and ignore/refactor

* @author David Graeff - Initial contribution
*/
@Component(immediate = true, service = MqttBrokerConnectionServiceInstanceMarker.class, property = {
Constants.SERVICE_PID + "=org.eclipse.smarthome.magicMultiInstance",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

copy&paste error?

public final @Nullable String lwtTopic;
public final byte @Nullable [] lwtMessage;
public final @Nullable Integer lwtQos;
public final @Nullable Boolean lwtRetain;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please review the latest changes @maggu2810 did on the LWT configuration. It is now possible to either pass the encoded String with topic:message:qos:retained or provide all values in separate fields (see #5231).

<description>Hostname or IP of the broker</description>
<default></default>
</parameter>
<parameter name="host" type="integer" required="false">
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

name should be host


/**
* Return the brokerID of this connection. This is either the name or host:port(:s).
* This method might return null, if none of the parameters is set.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The method does actually not return null but the empty String is nothing is configured.

@davidgraeff
Copy link
Contributor Author

@htreu Issues addressed

Copy link
Contributor

@htreu htreu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!
Some more comment inline.

Also: Again I get the following exception:

java.lang.IllegalArgumentException: Can not set java.lang.Integer field org.eclipse.smarthome.io.transport.mqtt.MqttBrokerConnectionConfig.lwtQos to java.lang.String

<advanced>true</advanced>
</parameter-group>

<parameter name="name" type="text" required="false" groupName="group_connection">
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please run auto formatter once.

<default>false</default>
</parameter>


Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

empty line

e.getMessage(), e.getReasonCode(),
(e.getCause() == null ? "Unknown" : e.getCause().getMessage()));
public synchronized void connectionLost(@Nullable Throwable exception) {
assert exception != null;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

exception being null seems to be a legit case. Its way too hard to throw errors here.
We do not use assert in ESH at all: Assertions need to be switched on during compilation what we don't do. Thus they will be removed from the resulting class files. And in this particular case it is hiding a NPE when exception actually is null.
Please review other occurrences and replace them by throwing IllegalArgumentException or IllegalStateException.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I checked with the documentation. All parameters on this interface are meant to be non null. The annotations are just missing and assert is basically used to compensate. I have added some comments. I do know that assert's are not compiled into bytecode in our case, it is more of a compiler and IDE hint.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assertions need to be switched on during compilation what we don't do.

That's not correct.
assertions could be enabled at runtime to use additional checks (e.g. in the development phase) that are not enabled default

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah I see (http://etutorials.org/Programming/Java+performance+tuning/Chapter+6.+Exceptions+Assertions+Casts+and+Variables/6.2+Assertions/). So it actually imposes a runtime overhead in contrast to C/C++. I'll use local variables then to get rid of the warnings.

this.secure = secure;
if (clientId == null) {
clientId = MqttClient.generateClientId();
assert clientId != null;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please remove or refactor

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some asserts can not be removed in my eclipse setup, which was done by oomph and the official installer. I get errors like "Null type mismatch (type annotations): required '@nonnull String' but this expression has type '@nullable String'. The problem severity defaults to "error" for those null checks.

logger.warn("Ignore invalid broker connection configuration: {}", config);
return;
}
assert brokerID != null;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove it and ignore the warning. Or add an extra null check to the StringUtils check.

assert brokerID != null;

// Add connection and make sure it succeeded
assert mqttService != null;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove and ignore/refactor

return;
}
// Start connection
assert connection != null;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove and ignore/refactor

@davidgraeff
Copy link
Contributor Author

I have no idea, why you get "java.lang.IllegalArgumentException: Can not set java.lang.Integer field org.eclipse.smarthome.io.transport.mqtt.MqttBrokerConnectionConfig.lwtQos to java.lang.String" to be honest. The conf desc xml says <parameter name="lwtQos" type="integer" ...> and it is defined as public Integer lwtQos in MqttBrokerConnectionConfig.

@htreu
Copy link
Contributor

htreu commented Mar 20, 2018

I don't understand the exception either. Does it work for you?
Also: This time Travis really hangs at MQTT transport with

[ERROR] Failed to execute goal org.eclipse.tycho:tycho-compiler-plugin:1.1.0:compile (default-compile) on project org.eclipse.smarthome.io.transport.mqtt: Compilation failure: Compilation failure: 
[ERROR] /home/travis/build/eclipse/smarthome/bundles/io/org.eclipse.smarthome.io.transport.mqtt/src/main/java/org/eclipse/smarthome/io/transport/mqtt/MqttService.java:[47] 
[ERROR] 	@Component(immediate = true, service = { MqttService.class }, configurationPid = {
[ERROR]         "org.eclipse.smarthome.mqtt" }, property = "service.pid=org.eclipse.smarthome.mqtt")
[ERROR] 	                                                                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[ERROR] Type mismatch: cannot convert from String[] to String

@maggu2810
Copy link
Contributor

See: #4625

@davidgraeff
Copy link
Contributor Author

davidgraeff commented Mar 20, 2018

The reason for the IllegalArgumentException is this:
Some time ago numbers in service configuration maps were dispatched as BigDecimal objects. Now they are Strings instead. We use config/core/Configuration for parsing the map and it fails on line 89ff, because it still expects BigDecimals:

if (value != null && value instanceof BigDecimal && !typeName.equals("BigDecimal")) {
       BigDecimal bdValue = (BigDecimal) value;
     if (typeName.equalsIgnoreCase("Float")) {
         value = bdValue.floatValue();
     } else if (typeName.equalsIgnoreCase("Double")) {
         value = bdValue.doubleValue();
     } else if (typeName.equalsIgnoreCase("Long")) {
         value = bdValue.longValue();
     } else if (typeName.equalsIgnoreCase("Integer") || typeName.equalsIgnoreCase("int")) {
         value = bdValue.intValue();
     }
}

Configuration need to be changed to also accept Strings

@davidgraeff
Copy link
Contributor Author

I have addressed the Configuration class shortcoming here: #5262

Copy link
Contributor

@htreu htreu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are some cosmetics left. And the question, if lwt should also be configurable by a single line as in the past (see inline comment).

<description>Broker username</description>
<default />
</parameter>
<parameter name="password" type="text" required="false" groupName="group_connection">
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please give this parameter the context password.

}

@Override
public void deliveryComplete(IMqttDeliveryToken token) {
public void deliveryComplete(@Nullable IMqttDeliveryToken token_) {
IMqttDeliveryToken token = (@NonNull IMqttDeliveryToken) token_;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this looks like an accident waiting to happen :-) Please simply null-check here.

final List<MqttMessageSubscriber> consumerList = entry.getValue();

public void messageArrived(@Nullable String topic_, @Nullable MqttMessage message_) {
byte[] payload = ((@NonNull MqttMessage) message_).getPayload();
Copy link
Contributor

@htreu htreu Mar 27, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, I get what you are doing here. Instead of defining the methods args @Nullable and do the cast here you may add @NonNullByDefault({}) at ClientCallbacks class level. This will "switch off" null annotation checks in this class. It hurts the eyes a little less then casting @Nullabel to @NonNull.

/** Keepalive in seconds */
public @Nullable Integer keepAlive;
// Last will parameters
public @Nullable String lwtTopic;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now the "old" configuration of lwt is not supported anymore. With #5231 @maggu2810 made sure both options could be used.
I thinks its fine that the UI (config description) only provides the single fields. But text based configuration may depend on the single line config. wdyt?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR is a API brake, code-wise as well as configuration-wise. Remember that we used the dot syntax before to express multiple mqtt instances and we use osgi service factories with a config file each now. Any user of this service (which I doubt there are any, because we provide no means to use Mqtt really) need to migrate anyway ^^.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@maggu2810 fine for you?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is okay for me to drop the old way to configure the LWT.

I am pretty sure there are guys the use this service. There has been one that inspired me to implement that change (to support a more generic topic and payload) and I am using that service, too. Already two of them. 😉 But as @davidgraeff also pointed out, changes in its usage needs to be done regardless if we keep this field or not, so I would prefer to remove old stuff and stop maintaining it on this change and all the other ones that will come some time.

@davidgraeff
Copy link
Contributor Author

@htreu Done

@htreu
Copy link
Contributor

htreu commented Mar 28, 2018

Strange: Travis complains about

[ERROR] /home/travis/build/eclipse/smarthome/bundles/io/org.eclipse.smarthome.io.transport.mqtt/src/main/java/org/eclipse/smarthome/io/transport/mqtt/MqttService.java:[48] 
[ERROR] 	@Component(immediate = true, service = { MqttService.class }, configurationPid = {
[ERROR]         "org.eclipse.smarthome.mqtt" }, property = { Constants.SERVICE_PID + "=org.eclipse.smarthome.mqtt" })
[ERROR] 	                                                                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[ERROR] Type mismatch: cannot convert from String[] to String

Locally it does compile fine. Update: No, it does not :-)

@davidgraeff
Copy link
Contributor Author

davidgraeff commented Mar 28, 2018

Hm, but why. A fast lookup on google let me find this example: @Component(property={EventConstants.EVENT_TOPIC+"=osgi/enroute/*"}). This is what we do here.

And another hm. It does of course compile... in eclipse. That's the main reason btw why google migrated for Android from eclipe to IDEA+gradle, to always use one build system.

@htreu
Copy link
Contributor

htreu commented Mar 28, 2018

My guess is, that configurationPid is the parameter in question (although it is in fact defined as String[] in @Component). I have no clue why we see this error.
Please give it a try.

@maggu2810
Copy link
Contributor

That artifact is used for the annotations on a Maven build (see pom.xml):

    <dependency>
      <groupId>org.apache.felix</groupId>
      <artifactId>org.apache.felix.scr.ds-annotations</artifactId>
      <version>1.2.10</version>
    </dependency>

This artifact itself uses the org.osgi.service.component.annotations package of

    <dependency>
      <groupId>org.osgi</groupId>
      <artifactId>org.osgi.compendium</artifactId>
      <version>4.3.0</version>
    </dependency>

Have a look at Release 4 Version 4.3 - Annotation Type Component: configurationPid needs to be a String, not an array.
That differs from Release 6 - Annotation Type Component: configurationPid is a String[] (and settings one value for an array with one element is supported in annotations AFAIK)

@htreu
Copy link
Contributor

htreu commented Mar 28, 2018

Thanks @maggu2810. I always searched our org.eclipse.osgi.services-3.6.0 dependency where String[] is defined. Where do we define the OSGi DS Annotation version to be used? Or: who is right? the maven dependency or the IDE dependency?

and settings one value for an array with one element is supported in annotations AFAIK

Yes, I just observed that in the annotation processor sources.

@maggu2810
Copy link
Contributor

maggu2810 commented Mar 28, 2018

The development guidelines states: "The minimum OSGi framework version supported is OSGi R4.2, no newer features must be used."

The OSGi stuff that is available in the Eclipse IDE provides much more recent versions, so in this case the Maven one is "more correct" as it uses an older version of the annotations.

Another example: In the Eclipse IDE you can also annotate a field in a component using @Reference but this is supported with starting with OSGi R6. Also in this case you will succeed in the IDE but fail with Maven.

@htreu
Copy link
Contributor

htreu commented Mar 28, 2018

Thanks. And to complete the picture (since you referenced R4V4.3) we can use these because no runtime support is needed (I see no *.annotation package in the 4.2 API).
Would it be an option to exchange the org.eclipse annotations by the org.osgi annotations in the IDE (sorry if this had been discussed before...)?

@maggu2810
Copy link
Contributor

exchange the org.eclipse annotations by the org.osgi annotations in the IDE

Which one ("org.eclipse annotations") do you mean?
Isn't the problem that the IDE is using the org.osgi annotations in a more recent version that the Maven build which is caused by the stuff that is present in the Target Platform?

@davidgraeff
Copy link
Contributor Author

Guys :) I changed the configurationpid to a simple string and would like to go on with this PR.

sjsf pushed a commit to sjsf/smarthome that referenced this pull request Mar 28, 2018
* eclipse-archived#4173 changes some APIs, adapt to those.
* Remove the broker connection thing, we are going to use the one defined in eclipse-archived#3839
* Remove all discovery stuff

Signed-off-by: David Gräff <david.graeff@web.de>
@htreu
Copy link
Contributor

htreu commented Mar 28, 2018

@davidgraeff agreed :-)
Once again Travis is taking the lead:

[ERROR] Failed to execute goal org.eclipse.tycho:tycho-compiler-plugin:1.1.0:compile (default-compile) on project org.eclipse.smarthome.io.transport.mqtt: Compilation failure: Compilation failure: 
[ERROR] /home/travis/build/eclipse/smarthome/bundles/io/org.eclipse.smarthome.io.transport.mqtt/src/main/java/org/eclipse/smarthome/io/transport/mqtt/MqttService.java:[135] 
[ERROR] 	connection = new MqttBrokerConnection(config.host, config.port, config.secure, config.clientID);
[ERROR] 	                                      ^^^^^^^^^^^
[ERROR] Null type mismatch (type annotations): required '@NonNull String' but this expression has type '@Nullable String'

Again strange that the IDE misses to show the error.

@davidgraeff
Copy link
Contributor Author

Because the IDE has more background knowledge. Look at the code in question, especially
config.host != null:

if (StringUtils.isNotBlank(config.host) && config.host != null) {
        connection = new MqttBrokerConnection(config.host, config.port, config.secure, config.clientID);
        brokerConnections.put(brokerID, connection);
} 

@htreu
Copy link
Contributor

htreu commented Mar 29, 2018

come on, don't let travis win!
Now tests fail due to unresolved dependencies. Following imports are missing:

org.mockito.invocation,
org.mockito.stubbing

After correcting this there is also one test left failing:

java.lang.AssertionError: Name getter expected:<1883> but was:<null>
	at org.junit.Assert.fail(Assert.java:88)
	at org.junit.Assert.failNotEquals(Assert.java:834)
	at org.junit.Assert.assertEquals(Assert.java:118)
	at org.eclipse.smarthome.io.transport.mqtt.MqttBrokerConnectionTest.setterGetterTests(MqttBrokerConnectionTest.java:169)

You now always initialise the port. Now the test assumption is false.

…more.

* Remove name from MqttBrokerConnection. This is only necessary for the MqttService.
  Consumers of MqttBrokerConnection should not be bothered with that detail.
* Remove textConfigured flag. Again this is not interesting for anything outside MqttService.
* Don't parse configuration parameters ourself, but use a configuration values class instead.
* Adapt to Nullable usage.
* Mqtt: Introduce service factory service MqttBrokerConnectionServiceInstance
* ESH compatible implementation of the service factory pattern including a config description for GUIs
* Add MqttService.setLastWill method. Fixes eclipse-archived#5162

Signed-off-by: David Graeff <david.graeff@web.de>
@davidgraeff
Copy link
Contributor Author

@htreu Tests corrected. And squashed.

Copy link
Contributor

@htreu htreu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Travis is happy and so am I :-)
Thanks @davidgraeff.

@htreu htreu merged commit 325efe3 into eclipse-archived:master Mar 29, 2018
@htreu htreu added this to the 0.10.0 milestone Oct 30, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Unable to use MQTT Last Will and Testament with dynamic data MqttService API break
4 participants