openhab-core
openhab-core copied to clipboard
[updater] A component for self-updating openHAB via the UI
This is a first attempt at creating a core module which supports self- updating of OpenHAB via the UI.
This PR is made available to allow OpenHAB maintainers to review the code and give feedback.
It is only a proof of concept (or not). It is not yet intended to be merged at this time.
Instructions: build the jar, drop it in /addons, and then go to Developer Tools | Api Explorer | Update
Signed-off-by: Andrew Fiddian-Green [email protected]
This pull request has been mentioned on openHAB Community. There might be relevant details there:
https://community.openhab.org/t/addon-to-update-oh-thoughts-anyone/121576/11
Dear All, The first reviewers focussed on the UI of this module. There is nothing wrong with that, but IMHO the real focus should be on the functionality rather than the UI. In particular I would suggest (indeed request) some feedback on the following items..
- The
BaseUpdaterclass, in particular the way it builds the update scripts and injects them into the operating system shell. - The classes that extend from
BaseUpdaterfor specific OS; theWindowsUpdaterandDebianUpdaterare tested, but the others (Mac, Yum, PacMan, Portage, etc.) need someone who uses those systems. - The update script templates associated with the respective OS updaters
DebianUpdater.txt,WindowsXX.txtetc. etc.
Hi @andrewfg,
I just had a closer look in order to provide some feedback.
- I see that you have put a lot of work into a UI, which is actually pretty unexpected for me here. When I suggested moving the feature to openHAB core as part of the REST API, I meant exactly this: Make the feature available through the REST API and nothing more. Naturally, the Main UI can then be enhanced to consume this new REST resource, just as it does for all the others. This especially make sense if you spin the idea further: The REST resource could provide info on whether a new update is available and the Main UI could present such infos to the user. It simply does not make sense to have that as a separate servlet where the user has to log in independently. And specifically, UIs do not belong into openhab-core. My suggestion is therefore to remove the UI parts and focus on the REST resource in this PR.
- The right place to add new REST resources is in a subpackage of
org.openhab.core.io.rest. As a bundle name, I'd hence suggest to useorg.openhab.core.io.rest.update. - The resource should definitely only be made available for
Role.ADMIN, but not forRole.USER - All classes should be in an internal package as I do not see the need to export any of them from the bundle.
- Please fix the spelling from
OpenHabor (OpenHAB) toopenHABthroughout your code.
Once you have refactored to this point, I'll be happy to do an in-depth review.
@kaikreuzer many thanks for the feedback. I will move/rename the package as you suggest. There is only one Java class which is UI related - namely the ServletUI class (component); as there is currently no way to really test things without some contribution from others on the UI side, I suggest to leave that class present for the time being. Or?? perhaps move it to "test"...
The REST resource could provide info on whether a new update is available
PS it does that already..
there is currently no way to really test things without some contribution from others on the UI side
Can't it be easily tested by using the developer tools of the Main UI? This makes it imho pretty simple to test REST resources and that should be just fine for testing the feature.
Remove the UI parts and focus on the REST resource in this PR.
Ok. Done.
The right place is in a subpackage of org.openhab.core.io.rest .. org.openhab.core.io.rest.update.
Ok. Done.
The resource should definitely only be made available for Role.ADMIN, but not for Role.USER
Not entirely. There is a REST GET which allows both USER's and ADMIN's to see if there is an update available. And a REST POST which allows only ADMIN's to execute an update.
All classes should be in an internal package
Ok. Done.
Please fix the spelling from OpenHab or (OpenHAB) to openHAB
Ok. Done. Exception: Java class and field names that must be camel case..

I'll have to connect with the debugger in order to see what is happening
Not sure if a debugger will help you much here. The Java code is creating and starting a shell script that stops OH, does the update, and then re starts OH again. So the Java debugger has no way to see what may be going wrong in that shell script.
EDIT depending on OS, the shell script file is called ‘self-updater.sh’ (or .cmd) and shell Stdout / Stderr may be written to ‘self-updater.log’ (or .txt). Normally these ‘self-updater.*’ files will be auto deleted when OH restarts; but if the log level is DEBUG those files will persist across restarts, so you can see what happened..
Thanks for your updates, @andrewfg! Just to manage expectations: This won't make it into 3.1 as we will need more testing and the according support in the Main UI. I therefore will focus the next days on stuff that is relevant for 3.1 and come back to this PR right after that. So please excuse me being silent for a little while, you are not forgotten!
please excuse me being silent for a little while
@kaikreuzer no problem; as I have only Windows and Debian, we will anyway need people to contribute for the other OS parts..
@kaikreuzer just pushed the 3.1.0.RC1 version today, and since this is a rare occurrence, below is (just for the record) the JSON response reported by the updater just now.
It compares the actual running version 3.1.0.M5 against the potential future version "3.1.0.RC1" and judges the latter to be a valid update for the MILESTONE branch i.e. it returns newVersionAvailable = true -- which is correct.
EDIT: btw the update process (Pi4 / Debian) worked too :)
{
"actualVersion": {
"versionName": "3.1.0.M5",
"versionType": "MILESTONE"
},
"latestVersionCount": 3,
"latestVersions": [
{
"versionName": "3.1.0",
"versionType": "STABLE"
},
{
"versionName": "3.1.0.RC1",
"versionType": "MILESTONE"
},
{
"versionName": "3.2.0.202106272039",
"versionType": "SNAPSHOT"
}
],
"targetNewVersionType": "MILESTONE",
"newVersionAvailable": true
}
Trying to test the current version of the PR, I got a NPE directly at startup:
22:06:33.265 [ERROR] [rnal.restresource.UpdaterRestResource] - bundle org.openhab.core.io.rest.update:3.2.0.202107012002 (235)[org.openhab.core.io.rest.update.internal.restresource.UpdaterRestResource(7)] : Error during instantiation of the implementation object
java.lang.reflect.InvocationTargetException: null
at jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) ~[?:?]
at jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) ~[?:?]
at jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) ~[?:?]
at java.lang.reflect.Constructor.newInstance(Constructor.java:490) ~[?:?]
at org.apache.felix.scr.impl.inject.internal.ComponentConstructorImpl.newInstance(ComponentConstructorImpl.java:316) ~[?:?]
at org.apache.felix.scr.impl.manager.SingleComponentManager.createImplementationObject(SingleComponentManager.java:286) ~[?:?]
at org.apache.felix.scr.impl.manager.SingleComponentManager.createComponent(SingleComponentManager.java:115) ~[?:?]
at org.apache.felix.scr.impl.manager.SingleComponentManager.getService(SingleComponentManager.java:1000) ~[?:?]
at org.apache.felix.scr.impl.manager.SingleComponentManager.getServiceInternal(SingleComponentManager.java:973) ~[?:?]
at org.apache.felix.scr.impl.manager.SingleComponentManager.getService(SingleComponentManager.java:918) ~[?:?]
at org.eclipse.osgi.internal.serviceregistry.ServiceFactoryUse$1.run(ServiceFactoryUse.java:216) ~[org.eclipse.osgi-3.16.200.jar:?]
at java.security.AccessController.doPrivileged(Native Method) ~[?:?]
at org.eclipse.osgi.internal.serviceregistry.ServiceFactoryUse.factoryGetService(ServiceFactoryUse.java:213) ~[org.eclipse.osgi-3.16.200.jar:?]
at org.eclipse.osgi.internal.serviceregistry.ServiceFactoryUse.getService(ServiceFactoryUse.java:114) ~[org.eclipse.osgi-3.16.200.jar:?]
at org.eclipse.osgi.internal.serviceregistry.ServiceUse.newServiceObject(ServiceUse.java:99) ~[org.eclipse.osgi-3.16.200.jar:?]
at org.eclipse.osgi.internal.serviceregistry.ServiceConsumer$1.getService(ServiceConsumer.java:33) ~[org.eclipse.osgi-3.16.200.jar:?]
at org.eclipse.osgi.internal.serviceregistry.ServiceRegistrationImpl.getService(ServiceRegistrationImpl.java:547) ~[org.eclipse.osgi-3.16.200.jar:?]
at org.eclipse.osgi.internal.serviceregistry.ServiceObjectsImpl.getService(ServiceObjectsImpl.java:92) ~[org.eclipse.osgi-3.16.200.jar:?]
at org.apache.aries.jax.rs.whiteboard.internal.utils.Utils.lambda$null$7(Utils.java:206) ~[?:?]
at org.apache.aries.component.dsl.OSGi.lambda$null$68(OSGi.java:674) ~[?:?]
at org.apache.aries.component.dsl.Publisher.apply(Publisher.java:28) ~[?:?]
at org.apache.aries.component.dsl.OSGi.lambda$null$75(OSGi.java:715) ~[?:?]
at org.apache.aries.component.dsl.internal.JustOSGiImpl.lambda$new$2(JustOSGiImpl.java:47) ~[?:?]
at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
at org.apache.aries.component.dsl.OSGi.lambda$recoverWith$76(OSGi.java:711) ~[?:?]
at org.apache.aries.component.dsl.internal.OSGiImpl.lambda$create$0(OSGiImpl.java:39) ~[?:?]
at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
at org.apache.aries.component.dsl.OSGi.lambda$flatMap$69(OSGi.java:674) ~[?:?]
at org.apache.aries.component.dsl.internal.OSGiImpl.lambda$create$0(OSGiImpl.java:39) ~[?:?]
at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
at org.apache.aries.component.dsl.OSGi.lambda$null$68(OSGi.java:674) ~[?:?]
at org.apache.aries.component.dsl.internal.JustOSGiImpl.lambda$new$2(JustOSGiImpl.java:47) ~[?:?]
at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
at org.apache.aries.component.dsl.internal.BundleContextOSGiImpl.lambda$new$0(BundleContextOSGiImpl.java:29) ~[?:?]
at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
at org.apache.aries.component.dsl.OSGi.lambda$flatMap$69(OSGi.java:674) ~[?:?]
at org.apache.aries.component.dsl.internal.OSGiImpl.lambda$create$0(OSGiImpl.java:39) ~[?:?]
at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
at org.apache.aries.component.dsl.internal.ChangeContextOSGiImpl.lambda$new$0(ChangeContextOSGiImpl.java:31) ~[?:?]
at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
at org.apache.aries.component.dsl.OSGi.lambda$recoverWith$76(OSGi.java:711) ~[?:?]
at org.apache.aries.component.dsl.internal.OSGiImpl.lambda$create$0(OSGiImpl.java:39) ~[?:?]
at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
at org.apache.aries.component.dsl.OSGi.lambda$flatMap$69(OSGi.java:674) ~[?:?]
at org.apache.aries.component.dsl.internal.OSGiImpl.lambda$create$0(OSGiImpl.java:39) ~[?:?]
at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
at org.apache.aries.component.dsl.OSGi.lambda$effects$65(OSGi.java:596) ~[?:?]
at org.apache.aries.component.dsl.internal.OSGiImpl.lambda$create$0(OSGiImpl.java:39) ~[?:?]
at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
at org.apache.aries.component.dsl.OSGi.lambda$effects$65(OSGi.java:596) ~[?:?]
at org.apache.aries.component.dsl.internal.OSGiImpl.lambda$create$0(OSGiImpl.java:39) ~[?:?]
at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
at org.apache.aries.component.dsl.OSGi.lambda$effects$65(OSGi.java:596) ~[?:?]
at org.apache.aries.component.dsl.internal.OSGiImpl.lambda$create$0(OSGiImpl.java:39) ~[?:?]
at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
at org.apache.aries.component.dsl.OSGi.lambda$null$68(OSGi.java:674) ~[?:?]
at org.apache.aries.component.dsl.OSGi.lambda$null$64(OSGi.java:602) ~[?:?]
at org.apache.aries.component.dsl.internal.JustOSGiImpl.lambda$new$2(JustOSGiImpl.java:47) ~[?:?]
at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
at org.apache.aries.component.dsl.OSGi.lambda$null$68(OSGi.java:674) ~[?:?]
at org.apache.aries.component.dsl.OSGi.lambda$null$64(OSGi.java:602) ~[?:?]
at org.apache.aries.component.dsl.Publisher.apply(Publisher.java:28) ~[?:?]
at org.apache.aries.component.dsl.OSGi.lambda$null$29(OSGi.java:296) ~[?:?]
at org.apache.aries.component.dsl.internal.UpdateSupport.deferPublication(UpdateSupport.java:40) ~[?:?]
at org.apache.aries.component.dsl.OSGi.lambda$null$32(OSGi.java:295) ~[?:?]
at org.apache.aries.component.dsl.OSGi.lambda$null$64(OSGi.java:602) ~[?:?]
at org.apache.aries.jax.rs.whiteboard.internal.ServiceReferenceFilteredPublisher.publishIfMatched(ServiceReferenceFilteredPublisher.java:55) ~[?:?]
at org.apache.aries.jax.rs.whiteboard.internal.ServiceReferenceRegistry.register(ServiceReferenceRegistry.java:92) ~[?:?]
at org.apache.aries.jax.rs.whiteboard.internal.cxf.CxfJaxrsServiceRegistrator.registerExtension(CxfJaxrsServiceRegistrator.java:327) ~[?:?]
at org.apache.aries.jax.rs.whiteboard.internal.Whiteboard.lambda$null$63(Whiteboard.java:962) ~[?:?]
at org.apache.aries.component.dsl.OSGi.lambda$null$64(OSGi.java:599) ~[?:?]
at org.apache.aries.component.dsl.OSGi.lambda$null$64(OSGi.java:602) ~[?:?]
at org.apache.aries.component.dsl.OSGi.lambda$null$64(OSGi.java:602) ~[?:?]
at org.apache.aries.component.dsl.OSGi.lambda$null$64(OSGi.java:602) ~[?:?]
at org.apache.aries.component.dsl.Publisher.apply(Publisher.java:28) ~[?:?]
at org.apache.aries.component.dsl.OSGi.lambda$null$75(OSGi.java:715) ~[?:?]
at org.apache.aries.component.dsl.OSGi.lambda$null$64(OSGi.java:602) ~[?:?]
at org.apache.aries.component.dsl.OSGi.lambda$null$64(OSGi.java:602) ~[?:?]
at org.apache.aries.component.dsl.internal.JustOSGiImpl.lambda$new$2(JustOSGiImpl.java:47) ~[?:?]
at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
at org.apache.aries.component.dsl.OSGi.lambda$effects$65(OSGi.java:596) ~[?:?]
at org.apache.aries.component.dsl.internal.OSGiImpl.lambda$create$0(OSGiImpl.java:39) ~[?:?]
at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
at org.apache.aries.component.dsl.OSGi.lambda$effects$65(OSGi.java:596) ~[?:?]
at org.apache.aries.component.dsl.internal.OSGiImpl.lambda$create$0(OSGiImpl.java:39) ~[?:?]
at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
at org.apache.aries.component.dsl.OSGi.lambda$null$68(OSGi.java:674) ~[?:?]
at org.apache.aries.component.dsl.Publisher.apply(Publisher.java:28) ~[?:?]
at org.apache.aries.component.dsl.OSGi.lambda$null$75(OSGi.java:715) ~[?:?]
at org.apache.aries.component.dsl.internal.JustOSGiImpl.lambda$new$2(JustOSGiImpl.java:47) ~[?:?]
at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
at org.apache.aries.component.dsl.OSGi.lambda$recoverWith$76(OSGi.java:711) ~[?:?]
at org.apache.aries.component.dsl.internal.OSGiImpl.lambda$create$0(OSGiImpl.java:39) ~[?:?]
at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
at org.apache.aries.component.dsl.OSGi.lambda$flatMap$69(OSGi.java:674) ~[?:?]
at org.apache.aries.component.dsl.internal.OSGiImpl.lambda$create$0(OSGiImpl.java:39) ~[?:?]
at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
at org.apache.aries.component.dsl.OSGi.lambda$null$68(OSGi.java:674) ~[?:?]
at org.apache.aries.component.dsl.internal.JustOSGiImpl.lambda$new$2(JustOSGiImpl.java:47) ~[?:?]
at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
at org.apache.aries.component.dsl.internal.BundleContextOSGiImpl.lambda$new$0(BundleContextOSGiImpl.java:29) ~[?:?]
at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
at org.apache.aries.component.dsl.OSGi.lambda$flatMap$69(OSGi.java:674) ~[?:?]
at org.apache.aries.component.dsl.internal.OSGiImpl.lambda$create$0(OSGiImpl.java:39) ~[?:?]
at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
at org.apache.aries.component.dsl.internal.ChangeContextOSGiImpl.lambda$new$0(ChangeContextOSGiImpl.java:31) ~[?:?]
at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
at org.apache.aries.component.dsl.OSGi.lambda$recoverWith$76(OSGi.java:711) ~[?:?]
at org.apache.aries.component.dsl.internal.OSGiImpl.lambda$create$0(OSGiImpl.java:39) ~[?:?]
at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
at org.apache.aries.component.dsl.OSGi.lambda$effects$65(OSGi.java:596) ~[?:?]
at org.apache.aries.component.dsl.internal.OSGiImpl.lambda$create$0(OSGiImpl.java:39) ~[?:?]
at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
at org.apache.aries.component.dsl.OSGi.lambda$effects$65(OSGi.java:596) ~[?:?]
at org.apache.aries.component.dsl.internal.OSGiImpl.lambda$create$0(OSGiImpl.java:39) ~[?:?]
at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
at org.apache.aries.component.dsl.OSGi.lambda$effects$65(OSGi.java:596) ~[?:?]
at org.apache.aries.component.dsl.internal.OSGiImpl.lambda$create$0(OSGiImpl.java:39) ~[?:?]
at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
at org.apache.aries.component.dsl.OSGi.lambda$effects$65(OSGi.java:596) ~[?:?]
at org.apache.aries.component.dsl.internal.OSGiImpl.lambda$create$0(OSGiImpl.java:39) ~[?:?]
at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
at org.apache.aries.component.dsl.OSGi.lambda$null$68(OSGi.java:674) ~[?:?]
at org.apache.aries.component.dsl.internal.JustOSGiImpl.lambda$new$2(JustOSGiImpl.java:47) ~[?:?]
at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
at org.apache.aries.component.dsl.OSGi.lambda$flatMap$69(OSGi.java:674) ~[?:?]
at org.apache.aries.component.dsl.internal.OSGiImpl.lambda$create$0(OSGiImpl.java:39) ~[?:?]
at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
at org.apache.aries.component.dsl.OSGi.lambda$null$68(OSGi.java:674) ~[?:?]
at org.apache.aries.component.dsl.internal.JustOSGiImpl.lambda$new$2(JustOSGiImpl.java:47) ~[?:?]
at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
at org.apache.aries.component.dsl.OSGi.lambda$flatMap$69(OSGi.java:674) ~[?:?]
at org.apache.aries.component.dsl.internal.OSGiImpl.lambda$create$0(OSGiImpl.java:39) ~[?:?]
at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
at org.apache.aries.component.dsl.OSGi.lambda$null$68(OSGi.java:674) ~[?:?]
at org.apache.aries.component.dsl.OSGi.lambda$null$64(OSGi.java:602) ~[?:?]
at org.apache.aries.jax.rs.whiteboard.internal.FilteredPublisher.publishIfMatched(FilteredPublisher.java:51) ~[?:?]
at org.apache.aries.jax.rs.whiteboard.internal.Registry.lambda$waitForService$1(Registry.java:69) ~[?:?]
at org.apache.aries.component.dsl.internal.OSGiImpl.lambda$create$0(OSGiImpl.java:39) ~[?:?]
at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
at org.apache.aries.component.dsl.OSGi.lambda$effects$65(OSGi.java:596) ~[?:?]
at org.apache.aries.component.dsl.internal.OSGiImpl.lambda$create$0(OSGiImpl.java:39) ~[?:?]
at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
at org.apache.aries.component.dsl.OSGi.lambda$null$68(OSGi.java:674) ~[?:?]
at org.apache.aries.component.dsl.internal.EffectsOSGi.lambda$new$1(EffectsOSGi.java:35) ~[?:?]
at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
at org.apache.aries.component.dsl.OSGi.lambda$flatMap$69(OSGi.java:674) ~[?:?]
at org.apache.aries.component.dsl.internal.OSGiImpl.lambda$create$0(OSGiImpl.java:39) ~[?:?]
at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
at org.apache.aries.component.dsl.OSGi.lambda$null$68(OSGi.java:674) ~[?:?]
at org.apache.aries.component.dsl.internal.JustOSGiImpl.lambda$new$2(JustOSGiImpl.java:47) ~[?:?]
at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
at org.apache.aries.component.dsl.OSGi.lambda$flatMap$69(OSGi.java:674) ~[?:?]
at org.apache.aries.component.dsl.internal.OSGiImpl.lambda$create$0(OSGiImpl.java:39) ~[?:?]
at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
at org.apache.aries.component.dsl.OSGi.lambda$flatMap$69(OSGi.java:674) ~[?:?]
at org.apache.aries.component.dsl.internal.OSGiImpl.lambda$create$0(OSGiImpl.java:39) ~[?:?]
at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
at org.apache.aries.component.dsl.OSGi.lambda$null$68(OSGi.java:674) ~[?:?]
at org.apache.aries.component.dsl.internal.JustOSGiImpl.lambda$new$2(JustOSGiImpl.java:47) ~[?:?]
at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
at org.apache.aries.component.dsl.OSGi.lambda$null$68(OSGi.java:674) ~[?:?]
at org.apache.aries.component.dsl.internal.JustOSGiImpl.lambda$new$2(JustOSGiImpl.java:47) ~[?:?]
at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
at org.apache.aries.component.dsl.OSGi.lambda$null$68(OSGi.java:674) ~[?:?]
at org.apache.aries.component.dsl.Publisher.apply(Publisher.java:28) ~[?:?]
at org.apache.aries.component.dsl.OSGi.lambda$null$66(OSGi.java:663) ~[?:?]
at org.apache.aries.component.dsl.internal.Pad.publish(Pad.java:59) ~[?:?]
at org.apache.aries.component.dsl.internal.DistributeOSGiImpl.lambda$null$1(DistributeOSGiImpl.java:51) ~[?:?]
at org.apache.aries.component.dsl.OSGi.lambda$null$64(OSGi.java:602) ~[?:?]
at org.apache.aries.component.dsl.Publisher.apply(Publisher.java:28) ~[?:?]
at org.apache.aries.component.dsl.internal.HighestRankingOSGi.lambda$null$1(HighestRankingOSGi.java:62) ~[?:?]
at org.apache.aries.component.dsl.internal.Pad.publish(Pad.java:59) ~[?:?]
at org.apache.aries.component.dsl.OSGi.lambda$null$79(OSGi.java:739) ~[?:?]
at org.apache.aries.component.dsl.internal.JustOSGiImpl.lambda$new$2(JustOSGiImpl.java:47) ~[?:?]
at org.apache.aries.component.dsl.internal.OSGiImpl.run(OSGiImpl.java:50) ~[?:?]
at org.apache.aries.component.dsl.OSGi.lambda$null$80(OSGi.java:732) ~[?:?]
at org.apache.aries.component.dsl.OSGi.lambda$null$64(OSGi.java:602) ~[?:?]
at org.apache.aries.component.dsl.Publisher.apply(Publisher.java:28) ~[?:?]
at org.apache.aries.component.dsl.OSGi.lambda$null$66(OSGi.java:663) ~[?:?]
at org.apache.aries.component.dsl.Publisher.apply(Publisher.java:28) ~[?:?]
at org.apache.aries.component.dsl.internal.ServiceReferenceOSGi$DefaultServiceTrackerCustomizer.addingService(ServiceReferenceOSGi.java:74) ~[?:?]
at org.apache.aries.component.dsl.internal.ServiceReferenceOSGi$DefaultServiceTrackerCustomizer.addingService(ServiceReferenceOSGi.java:56) ~[?:?]
at org.osgi.util.tracker.ServiceTracker$Tracked.customizerAdding(ServiceTracker.java:943) ~[osgi.core-7.0.0.jar:?]
at org.osgi.util.tracker.ServiceTracker$Tracked.customizerAdding(ServiceTracker.java:871) ~[osgi.core-7.0.0.jar:?]
at org.osgi.util.tracker.AbstractTracked.trackAdding(AbstractTracked.java:256) ~[osgi.core-7.0.0.jar:?]
at org.osgi.util.tracker.AbstractTracked.track(AbstractTracked.java:229) ~[osgi.core-7.0.0.jar:?]
at org.osgi.util.tracker.ServiceTracker$Tracked.serviceChanged(ServiceTracker.java:903) ~[osgi.core-7.0.0.jar:?]
at org.eclipse.osgi.internal.serviceregistry.FilteredServiceListener.serviceChanged(FilteredServiceListener.java:120) ~[org.eclipse.osgi-3.16.200.jar:?]
at org.eclipse.osgi.internal.framework.BundleContextImpl.dispatchEvent(BundleContextImpl.java:957) ~[org.eclipse.osgi-3.16.200.jar:?]
at org.eclipse.osgi.framework.eventmgr.EventManager.dispatchEvent(EventManager.java:234) ~[org.eclipse.osgi-3.16.200.jar:?]
at org.eclipse.osgi.framework.eventmgr.ListenerQueue.dispatchEventSynchronous(ListenerQueue.java:151) ~[org.eclipse.osgi-3.16.200.jar:?]
at org.eclipse.osgi.internal.serviceregistry.ServiceRegistry.publishServiceEventPrivileged(ServiceRegistry.java:936) ~[org.eclipse.osgi-3.16.200.jar:?]
at org.eclipse.osgi.internal.serviceregistry.ServiceRegistry.publishServiceEvent(ServiceRegistry.java:873) ~[org.eclipse.osgi-3.16.200.jar:?]
at org.eclipse.osgi.internal.serviceregistry.ServiceRegistrationImpl.register(ServiceRegistrationImpl.java:141) ~[org.eclipse.osgi-3.16.200.jar:?]
at org.eclipse.osgi.internal.serviceregistry.ServiceRegistry.registerService(ServiceRegistry.java:261) ~[org.eclipse.osgi-3.16.200.jar:?]
at org.eclipse.osgi.internal.framework.BundleContextImpl.registerService(BundleContextImpl.java:496) ~[org.eclipse.osgi-3.16.200.jar:?]
at org.apache.felix.scr.impl.manager.AbstractComponentManager$3.register(AbstractComponentManager.java:929) ~[?:?]
at org.apache.felix.scr.impl.manager.AbstractComponentManager$3.register(AbstractComponentManager.java:915) ~[?:?]
at org.apache.felix.scr.impl.manager.RegistrationManager.changeRegistration(RegistrationManager.java:133) ~[?:?]
at org.apache.felix.scr.impl.manager.AbstractComponentManager.registerService(AbstractComponentManager.java:984) ~[?:?]
at org.apache.felix.scr.impl.manager.AbstractComponentManager.activateInternal(AbstractComponentManager.java:752) ~[?:?]
at org.apache.felix.scr.impl.manager.AbstractComponentManager.enableInternal(AbstractComponentManager.java:674) ~[?:?]
at org.apache.felix.scr.impl.manager.AbstractComponentManager.enable(AbstractComponentManager.java:437) ~[?:?]
at org.apache.felix.scr.impl.manager.ConfigurableComponentHolder.enableComponents(ConfigurableComponentHolder.java:667) ~[?:?]
at org.apache.felix.scr.impl.BundleComponentActivator.initialEnable(BundleComponentActivator.java:305) ~[?:?]
at org.apache.felix.scr.impl.Activator.loadComponents(Activator.java:554) ~[?:?]
at org.apache.felix.scr.impl.Activator.access$200(Activator.java:70) ~[?:?]
at org.apache.felix.scr.impl.Activator$ScrExtension.start(Activator.java:421) ~[?:?]
at org.apache.felix.scr.impl.AbstractExtender.createExtension(AbstractExtender.java:196) ~[?:?]
at org.apache.felix.scr.impl.AbstractExtender.modifiedBundle(AbstractExtender.java:169) ~[?:?]
at org.apache.felix.scr.impl.AbstractExtender.modifiedBundle(AbstractExtender.java:49) ~[?:?]
at org.osgi.util.tracker.BundleTracker$Tracked.customizerModified(BundleTracker.java:488) ~[osgi.core-7.0.0.jar:?]
at org.osgi.util.tracker.BundleTracker$Tracked.customizerModified(BundleTracker.java:420) ~[osgi.core-7.0.0.jar:?]
at org.osgi.util.tracker.AbstractTracked.track(AbstractTracked.java:232) ~[osgi.core-7.0.0.jar:?]
at org.osgi.util.tracker.BundleTracker$Tracked.bundleChanged(BundleTracker.java:450) ~[osgi.core-7.0.0.jar:?]
at org.eclipse.osgi.internal.framework.BundleContextImpl.dispatchEvent(BundleContextImpl.java:945) ~[org.eclipse.osgi-3.16.200.jar:?]
at org.eclipse.osgi.framework.eventmgr.EventManager.dispatchEvent(EventManager.java:234) ~[org.eclipse.osgi-3.16.200.jar:?]
at org.eclipse.osgi.framework.eventmgr.ListenerQueue.dispatchEventSynchronous(ListenerQueue.java:151) ~[org.eclipse.osgi-3.16.200.jar:?]
at org.eclipse.osgi.internal.framework.EquinoxEventPublisher.publishBundleEventPrivileged(EquinoxEventPublisher.java:232) ~[org.eclipse.osgi-3.16.200.jar:?]
at org.eclipse.osgi.internal.framework.EquinoxEventPublisher.publishBundleEvent(EquinoxEventPublisher.java:138) ~[org.eclipse.osgi-3.16.200.jar:?]
at org.eclipse.osgi.internal.framework.EquinoxEventPublisher.publishBundleEvent(EquinoxEventPublisher.java:130) ~[org.eclipse.osgi-3.16.200.jar:?]
at org.eclipse.osgi.internal.framework.EquinoxContainerAdaptor.publishModuleEvent(EquinoxContainerAdaptor.java:217) ~[org.eclipse.osgi-3.16.200.jar:?]
at org.eclipse.osgi.container.Module.publishEvent(Module.java:499) ~[org.eclipse.osgi-3.16.200.jar:?]
at org.eclipse.osgi.container.Module.start(Module.java:486) ~[org.eclipse.osgi-3.16.200.jar:?]
at org.eclipse.osgi.internal.framework.EquinoxBundle.start(EquinoxBundle.java:440) ~[org.eclipse.osgi-3.16.200.jar:?]
at org.eclipse.osgi.internal.framework.EquinoxBundle.start(EquinoxBundle.java:459) ~[org.eclipse.osgi-3.16.200.jar:?]
at org.apache.karaf.features.internal.service.BundleInstallSupportImpl.startBundle(BundleInstallSupportImpl.java:165) ~[?:?]
at org.apache.karaf.features.internal.service.FeaturesServiceImpl.startBundle(FeaturesServiceImpl.java:1160) ~[?:?]
at org.apache.karaf.features.internal.service.Deployer.deploy(Deployer.java:1041) ~[?:?]
at org.apache.karaf.features.internal.service.FeaturesServiceImpl.doProvision(FeaturesServiceImpl.java:1069) ~[?:?]
at org.apache.karaf.features.internal.service.FeaturesServiceImpl.lambda$doProvisionInThread$13(FeaturesServiceImpl.java:1004) ~[?:?]
at java.util.concurrent.FutureTask.run(FutureTask.java:264) [?:?]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) [?:?]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) [?:?]
at java.lang.Thread.run(Thread.java:834) [?:?]
Caused by: java.lang.NullPointerException
at org.openhab.core.io.rest.update.internal.updaters.MacUpdater.initializeExtendedPlaceholders(MacUpdater.java:46) ~[?:?]
at org.openhab.core.io.rest.update.internal.updaters.BaseUpdater.<init>(BaseUpdater.java:151) ~[?:?]
at org.openhab.core.io.rest.update.internal.updaters.MacUpdater.<init>(MacUpdater.java:26) ~[?:?]
at org.openhab.core.io.rest.update.internal.factory.UpdaterFactory.newUpdater(UpdaterFactory.java:95) ~[?:?]
at org.openhab.core.io.rest.update.internal.restresource.UpdaterRestResource.<init>(UpdaterRestResource.java:76) ~[?:?]
... 236 more
22:06:33.320 [WARN ] [ard.internal.AriesJaxrsServiceRuntime] - Resource from reference CachingServiceReference {
cachedProperties={osgi.jaxrs.application.select=(osgi.jaxrs.name=openhab), osgi.jaxrs.name=update, osgi.jaxrs.extension.select=(osgi.jaxrs.media.type=application/json), osgi.jaxrs.whiteboard.target=null (cached)}
serviceReference={org.openhab.core.io.rest.RESTResource}={osgi.jaxrs.resource=true, service.id=144, service.bundleid=235, service.scope=bundle, osgi.jaxrs.application.select=(osgi.jaxrs.name=openhab), osgi.jaxrs.name=update, osgi.jaxrs.extension.select=(osgi.jaxrs.media.type=application/json), component.name=org.openhab.core.io.rest.update.internal.restresource.UpdaterRestResource, component.id=7}
} can't be got
Oops. Sorry about the NPE in the MacUpdater (as mentioned, I don't have a Mac, do that code was untested). I am taking a week's vacation, so I will get back to you with a fix in about 10 days time.
org.openhab.core.io.rest.update.internal.updaters.MacUpdater.initializeExtendedPlaceholders(MacUpdater.java:46)
Hmm. It looks like logger is still null at line 46; which is not expected..
@kaikreuzer I have made some changes based on your recent feedback. However unfortunately by changing the version from 3.1.0-SNAPSHOT to 3.2.0-SNAPSHOT this PR seems to have "acquired" the 370 or so files that changed when 3.1.0 was released. It is now a mess, and I don't know how to fix that.
unfortunately by changing the version from 3.1.0-SNAPSHOT to 3.2.0-SNAPSHOT this PR seems to have "acquired" the 370 or so files that changed when 3.1.0 was released
@kaikreuzer the above-mentioned issue has now been resolved :)
https://github.com/openhab/openhab-core/pull/2372#issuecomment-872517867
@kaikreuzer resolved
@kaikreuzer => ping!
Just to report that this did successfully install 3.2.M2 today (see screenshot below)..

Cool! Will look at it soon®, promised, @andrewfg!
This pull request has been mentioned on openHAB Community. There might be relevant details there:
https://community.openhab.org/t/validation-api-usage/126439/1
Hey @andrewfg! Very sorry that it took so long, but finally I now had a closer look at it and also tested it on macOS. The code of the PR looks pretty clean, thanks for the good job on it!
Unfortunately, I didn't manage to get it working on macOS. I made a couple of fixes (like also setting the executable flag on the script, calling it as the provided user and not as root, etc.), but the execution of the single steps always fails as the commands expect to be run in some other folder. Manually calling it during my tests it behaved differently than when being called from the JVM and I couldn't really figure out how to fix it.
Thinking about it, I also wasn't sure how the script should actually know, how to correctly start the instance. Some people run it through "bin/karaf start", others might use "bin/start", I am running it within screen, others might do nohup or special service wrappers. I somehow doubt that there's a solution that will just work and not be very fragile. I have the same concerns for Windows users - the current script simply starts the instance, but what if people run it as a Windows service as described here? On Linux, the code assumes that openHAB is installed through the package manager of the distro. But what if it is manually installed or used as a snap in a docker container?
The more I think about it, the less convinced I am that we can get this a stable feature. Happy to discuss how to proceed - also inviting the other @openhab/core-maintainers.
The more I think about it, the less convinced I am that we can get this a stable feature.
We might just only want to provide APIs here in core, so the UI knows if an update mechanism is available, and what parameters are used (password, available versions). Then the actual update script(s) and files that supply the parameters can move to distro projects. It would also allow users to have a way to plug-in their own update mechanism if they use something of their own. The available versions also depends on what installation method is used. E.g. versions aren't always immediately available for all platforms/installation methods (Synology, snap, etc).
might just only want to provide APIs here in core, so the UI knows if an update mechanism is available, and what parameters are used (password, available versions). Then the actual update script(s) and files that supply the parameters can move to distro projects.
This is essentially what I did already. There is a main API with a REST GET to read the current update state, and a POST to start the update process. And then for each type of installation / distro there are just two distro specific elements — namely i) a small Java class that extends/ overrides some methods in the main updater class, and ii) a text file that contains the update command script.
I have only written these two elements for OpenHabian an Windows so far..
IMHO the core should not know anything about package specific upgrade commands. It should only know that there is a script (or class) that can do this and what kind of parameters it expects that can then be provided using the UI. I.e. it uses some abstraction where the package adds a file or property to indicate it can do the update using what parameters. Then the actual upgrade scripts move to either openhab-distro, openhab-linuxpkg, openhab-syno-spk, openhab-snap etc where they can be maintained and which already have all the package specific implementation details.
the core should not know anything about package specific upgrade commands. It should only know that there is a script (or class) that can do this and what kind of parameters it expects that can then be provided using the UI
Indeed. (That is what I am doing). The scripts contain placeholders for parameters like target version, target version type, password etc. and the core replaces the placeholders in the scripts.
This pull request has been mentioned on openHAB Community. There might be relevant details there:
https://community.openhab.org/t/upgrading-system-via-gui/138453/13
@andrewfg What is the state here?
What is the state here?
I think it is 'sleeping' .. @kaikreuzer / @wborn WDYT? .. if you agree we can close it, or make it WIP ..