openhab-scripting 4.46.2 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (281) hide show
  1. checksums.yaml +4 -4
  2. data/lib/openhab/core/actions/audio.rb +47 -0
  3. data/lib/openhab/core/actions/ephemeris.rb +39 -0
  4. data/lib/openhab/core/actions/exec.rb +51 -0
  5. data/lib/openhab/core/actions/http.rb +80 -0
  6. data/lib/openhab/core/actions/ping.rb +30 -0
  7. data/lib/openhab/core/actions/transformation.rb +32 -0
  8. data/lib/openhab/core/actions/voice.rb +36 -0
  9. data/lib/openhab/core/actions.rb +82 -0
  10. data/lib/openhab/core/dependency_tracking.rb +34 -0
  11. data/lib/openhab/core/dto/item_channel_link.rb +33 -0
  12. data/lib/openhab/core/dto/thing.rb +27 -0
  13. data/lib/openhab/core/dto.rb +11 -0
  14. data/lib/openhab/core/entity_lookup.rb +152 -70
  15. data/lib/openhab/core/events/abstract_event.rb +18 -0
  16. data/lib/openhab/core/events/abstract_item_registry_event.rb +36 -0
  17. data/lib/openhab/core/events/abstract_thing_registry_event.rb +40 -0
  18. data/lib/openhab/core/events/item_command_event.rb +78 -0
  19. data/lib/openhab/core/events/item_event.rb +22 -0
  20. data/lib/openhab/core/events/item_state_changed_event.rb +75 -0
  21. data/lib/openhab/core/events/item_state_event.rb +79 -0
  22. data/lib/openhab/core/events/thing_status_info_event.rb +55 -0
  23. data/lib/openhab/core/events.rb +10 -0
  24. data/lib/openhab/core/items/accepted_data_types.rb +29 -0
  25. data/lib/openhab/core/items/color_item.rb +52 -0
  26. data/lib/openhab/core/items/contact_item.rb +52 -0
  27. data/lib/openhab/core/items/date_time_item.rb +59 -0
  28. data/lib/openhab/core/items/dimmer_item.rb +148 -0
  29. data/lib/openhab/core/items/generic_item.rb +292 -0
  30. data/lib/openhab/core/items/group_item.rb +176 -0
  31. data/lib/openhab/{dsl → core}/items/image_item.rb +35 -29
  32. data/lib/openhab/core/items/item.rb +273 -0
  33. data/lib/openhab/core/items/location_item.rb +34 -0
  34. data/lib/openhab/core/items/metadata/hash.rb +433 -0
  35. data/lib/openhab/core/items/metadata/namespace_hash.rb +475 -0
  36. data/lib/openhab/core/items/metadata/provider.rb +48 -0
  37. data/lib/openhab/core/items/metadata.rb +11 -0
  38. data/lib/openhab/core/items/number_item.rb +62 -0
  39. data/lib/openhab/core/items/numeric_item.rb +22 -0
  40. data/lib/openhab/core/items/persistence.rb +416 -0
  41. data/lib/openhab/core/items/player_item.rb +66 -0
  42. data/lib/openhab/core/items/provider.rb +44 -0
  43. data/lib/openhab/core/items/proxy.rb +136 -0
  44. data/lib/openhab/core/items/registry.rb +86 -0
  45. data/lib/openhab/core/items/rollershutter_item.rb +68 -0
  46. data/lib/openhab/core/items/semantics/enumerable.rb +177 -0
  47. data/lib/openhab/core/items/semantics.rb +473 -0
  48. data/lib/openhab/core/items/state_storage.rb +53 -0
  49. data/lib/openhab/core/items/string_item.rb +28 -0
  50. data/lib/openhab/core/items/switch_item.rb +78 -0
  51. data/lib/openhab/core/items.rb +108 -0
  52. data/lib/openhab/{dsl → core}/lazy_array.rb +9 -3
  53. data/lib/openhab/core/profile_factory.rb +132 -0
  54. data/lib/openhab/core/provider.rb +230 -0
  55. data/lib/openhab/core/proxy.rb +130 -0
  56. data/lib/openhab/core/registry.rb +40 -0
  57. data/lib/openhab/core/rules/module.rb +26 -0
  58. data/lib/openhab/core/rules/provider.rb +25 -0
  59. data/lib/openhab/core/rules/registry.rb +76 -0
  60. data/lib/openhab/core/rules/rule.rb +150 -0
  61. data/lib/openhab/core/rules.rb +25 -0
  62. data/lib/openhab/core/script_handling.rb +78 -20
  63. data/lib/openhab/core/things/channel.rb +48 -0
  64. data/lib/openhab/core/things/channel_uid.rb +51 -0
  65. data/lib/openhab/core/things/item_channel_link.rb +33 -0
  66. data/lib/openhab/core/things/links/provider.rb +78 -0
  67. data/lib/openhab/core/things/profile_callback.rb +52 -0
  68. data/lib/openhab/core/things/provider.rb +29 -0
  69. data/lib/openhab/core/things/proxy.rb +87 -0
  70. data/lib/openhab/core/things/registry.rb +73 -0
  71. data/lib/openhab/core/things/thing.rb +194 -0
  72. data/lib/openhab/core/things.rb +22 -0
  73. data/lib/openhab/core/timer.rb +148 -0
  74. data/lib/openhab/{dsl → core}/types/comparable_type.rb +5 -3
  75. data/lib/openhab/{dsl → core}/types/date_time_type.rb +55 -127
  76. data/lib/openhab/{dsl → core}/types/decimal_type.rb +50 -48
  77. data/lib/openhab/{dsl → core}/types/hsb_type.rb +35 -83
  78. data/lib/openhab/core/types/increase_decrease_type.rb +34 -0
  79. data/lib/openhab/core/types/next_previous_type.rb +34 -0
  80. data/lib/openhab/{dsl → core}/types/numeric_type.rb +20 -7
  81. data/lib/openhab/core/types/on_off_type.rb +46 -0
  82. data/lib/openhab/core/types/open_closed_type.rb +41 -0
  83. data/lib/openhab/{dsl → core}/types/percent_type.rb +19 -20
  84. data/lib/openhab/core/types/play_pause_type.rb +38 -0
  85. data/lib/openhab/core/types/point_type.rb +117 -0
  86. data/lib/openhab/core/types/quantity_type.rb +325 -0
  87. data/lib/openhab/core/types/raw_type.rb +26 -0
  88. data/lib/openhab/core/types/refresh_type.rb +27 -0
  89. data/lib/openhab/core/types/rewind_fastforward_type.rb +38 -0
  90. data/lib/openhab/core/types/stop_move_type.rb +34 -0
  91. data/lib/openhab/{dsl → core}/types/string_type.rb +17 -28
  92. data/lib/openhab/{dsl → core}/types/type.rb +42 -40
  93. data/lib/openhab/core/types/un_def_type.rb +38 -0
  94. data/lib/openhab/core/types/up_down_type.rb +50 -0
  95. data/lib/openhab/core/types.rb +82 -0
  96. data/lib/openhab/{dsl → core}/uid.rb +4 -23
  97. data/lib/openhab/core/value_cache.rb +188 -0
  98. data/lib/openhab/core.rb +98 -0
  99. data/lib/openhab/core_ext/between.rb +32 -0
  100. data/lib/openhab/core_ext/ephemeris.rb +53 -0
  101. data/lib/openhab/core_ext/java/class.rb +34 -0
  102. data/lib/openhab/core_ext/java/duration.rb +142 -0
  103. data/lib/openhab/core_ext/java/list.rb +436 -0
  104. data/lib/openhab/core_ext/java/local_date.rb +104 -0
  105. data/lib/openhab/core_ext/java/local_time.rb +118 -0
  106. data/lib/openhab/core_ext/java/map.rb +66 -0
  107. data/lib/openhab/core_ext/java/month.rb +71 -0
  108. data/lib/openhab/core_ext/java/month_day.rb +119 -0
  109. data/lib/openhab/core_ext/java/period.rb +103 -0
  110. data/lib/openhab/core_ext/java/temporal_amount.rb +34 -0
  111. data/lib/openhab/core_ext/java/time.rb +62 -0
  112. data/lib/openhab/core_ext/java/unit.rb +15 -0
  113. data/lib/openhab/core_ext/java/zoned_date_time.rb +213 -0
  114. data/lib/openhab/core_ext/ruby/array.rb +21 -0
  115. data/lib/openhab/core_ext/ruby/date.rb +96 -0
  116. data/lib/openhab/core_ext/ruby/date_time.rb +55 -0
  117. data/lib/openhab/core_ext/ruby/module.rb +15 -0
  118. data/lib/openhab/core_ext/ruby/numeric.rb +195 -0
  119. data/lib/openhab/core_ext/ruby/range.rb +70 -0
  120. data/lib/openhab/core_ext/ruby/symbol.rb +7 -0
  121. data/lib/openhab/core_ext/ruby/time.rb +108 -0
  122. data/lib/openhab/core_ext.rb +18 -0
  123. data/lib/openhab/dsl/debouncer.rb +259 -0
  124. data/lib/openhab/dsl/events/watch_event.rb +18 -0
  125. data/lib/openhab/dsl/events.rb +9 -0
  126. data/lib/openhab/dsl/gems.rb +1 -1
  127. data/lib/openhab/dsl/items/builder.rb +578 -0
  128. data/lib/openhab/dsl/items/ensure.rb +73 -82
  129. data/lib/openhab/dsl/items/timed_command.rb +214 -159
  130. data/lib/openhab/dsl/rules/automation_rule.rb +126 -115
  131. data/lib/openhab/dsl/rules/builder.rb +1935 -0
  132. data/lib/openhab/dsl/rules/guard.rb +51 -114
  133. data/lib/openhab/dsl/rules/name_inference.rb +66 -25
  134. data/lib/openhab/dsl/rules/property.rb +48 -75
  135. data/lib/openhab/dsl/rules/rule_triggers.rb +22 -27
  136. data/lib/openhab/dsl/rules/terse.rb +58 -14
  137. data/lib/openhab/dsl/rules/triggers/changed.rb +48 -94
  138. data/lib/openhab/dsl/rules/triggers/channel.rb +9 -40
  139. data/lib/openhab/dsl/rules/triggers/command.rb +14 -63
  140. data/lib/openhab/dsl/rules/triggers/conditions/duration.rb +34 -69
  141. data/lib/openhab/dsl/rules/triggers/conditions/proc.rb +6 -14
  142. data/lib/openhab/dsl/rules/triggers/cron/cron.rb +48 -82
  143. data/lib/openhab/dsl/rules/triggers/cron/cron_handler.rb +30 -47
  144. data/lib/openhab/dsl/rules/triggers/trigger.rb +7 -28
  145. data/lib/openhab/dsl/rules/triggers/updated.rb +21 -45
  146. data/lib/openhab/dsl/rules/triggers/watch/watch_handler.rb +257 -102
  147. data/lib/openhab/dsl/rules/triggers.rb +12 -0
  148. data/lib/openhab/dsl/rules.rb +8 -0
  149. data/lib/openhab/dsl/things/builder.rb +299 -0
  150. data/lib/openhab/{core → dsl}/thread_local.rb +27 -17
  151. data/lib/openhab/dsl/timer_manager.rb +204 -0
  152. data/lib/openhab/dsl/version.rb +9 -0
  153. data/lib/openhab/dsl.rb +979 -0
  154. data/lib/openhab/log.rb +355 -0
  155. data/lib/openhab/osgi.rb +68 -0
  156. data/lib/openhab/rspec/configuration.rb +56 -0
  157. data/lib/openhab/rspec/example_group.rb +132 -0
  158. data/lib/openhab/rspec/helpers.rb +458 -0
  159. data/lib/openhab/rspec/hooks.rb +113 -0
  160. data/lib/openhab/rspec/jruby.rb +46 -0
  161. data/lib/openhab/rspec/karaf.rb +851 -0
  162. data/lib/openhab/rspec/mocks/bundle_install_support.rb +25 -0
  163. data/lib/openhab/rspec/mocks/bundle_resolver.rb +30 -0
  164. data/lib/openhab/rspec/mocks/event_admin.rb +146 -0
  165. data/lib/openhab/rspec/mocks/instance_method_stasher.rb +22 -0
  166. data/lib/openhab/rspec/mocks/persistence_service.rb +155 -0
  167. data/lib/openhab/rspec/mocks/safe_caller.rb +40 -0
  168. data/lib/openhab/rspec/mocks/space.rb +23 -0
  169. data/lib/openhab/rspec/mocks/synchronous_executor.rb +63 -0
  170. data/lib/openhab/rspec/mocks/thing_handler.rb +76 -0
  171. data/lib/openhab/rspec/mocks/timer.rb +134 -0
  172. data/lib/openhab/rspec/openhab/core/actions.rb +38 -0
  173. data/lib/openhab/rspec/openhab/core/items/proxy.rb +15 -0
  174. data/lib/openhab/rspec/openhab/core/things/proxy.rb +27 -0
  175. data/lib/openhab/rspec/shell.rb +31 -0
  176. data/lib/openhab/rspec/suspend_rules.rb +50 -0
  177. data/lib/openhab/rspec.rb +26 -0
  178. data/lib/openhab/yard/base_helper.rb +19 -0
  179. data/lib/openhab/yard/cli/stats.rb +23 -0
  180. data/lib/openhab/yard/code_objects/group_object.rb +23 -0
  181. data/lib/openhab/yard/code_objects/java/base.rb +31 -0
  182. data/lib/openhab/yard/code_objects/java/class_object.rb +11 -0
  183. data/lib/openhab/yard/code_objects/java/field_object.rb +15 -0
  184. data/lib/openhab/yard/code_objects/java/interface_object.rb +15 -0
  185. data/lib/openhab/yard/code_objects/java/package_object.rb +11 -0
  186. data/lib/openhab/yard/code_objects/java/proxy.rb +23 -0
  187. data/lib/openhab/yard/coderay.rb +17 -0
  188. data/lib/openhab/yard/handlers/jruby/base.rb +58 -0
  189. data/lib/openhab/yard/handlers/jruby/class_handler.rb +18 -0
  190. data/lib/openhab/yard/handlers/jruby/constant_handler.rb +18 -0
  191. data/lib/openhab/yard/handlers/jruby/java_import_handler.rb +30 -0
  192. data/lib/openhab/yard/handlers/jruby/mixin_handler.rb +23 -0
  193. data/lib/openhab/yard/html_helper.rb +78 -0
  194. data/lib/openhab/yard/markdown_helper.rb +148 -0
  195. data/lib/openhab/yard/tags/constant_directive.rb +20 -0
  196. data/lib/openhab/yard/tags/group_directive.rb +24 -0
  197. data/lib/openhab/yard/tags/library.rb +3 -0
  198. data/lib/openhab/yard.rb +38 -0
  199. metadata +475 -106
  200. data/lib/openhab/core/item_proxy.rb +0 -29
  201. data/lib/openhab/core/load_path.rb +0 -19
  202. data/lib/openhab/core/openhab_setup.rb +0 -29
  203. data/lib/openhab/core/osgi.rb +0 -58
  204. data/lib/openhab/core/services.rb +0 -24
  205. data/lib/openhab/dsl/actions.rb +0 -114
  206. data/lib/openhab/dsl/between.rb +0 -25
  207. data/lib/openhab/dsl/channel.rb +0 -43
  208. data/lib/openhab/dsl/dsl.rb +0 -59
  209. data/lib/openhab/dsl/group.rb +0 -54
  210. data/lib/openhab/dsl/imports.rb +0 -21
  211. data/lib/openhab/dsl/items/color_item.rb +0 -76
  212. data/lib/openhab/dsl/items/comparable_item.rb +0 -62
  213. data/lib/openhab/dsl/items/contact_item.rb +0 -41
  214. data/lib/openhab/dsl/items/date_time_item.rb +0 -65
  215. data/lib/openhab/dsl/items/dimmer_item.rb +0 -65
  216. data/lib/openhab/dsl/items/generic_item.rb +0 -229
  217. data/lib/openhab/dsl/items/group_item.rb +0 -127
  218. data/lib/openhab/dsl/items/item_equality.rb +0 -59
  219. data/lib/openhab/dsl/items/item_registry.rb +0 -54
  220. data/lib/openhab/dsl/items/items.rb +0 -109
  221. data/lib/openhab/dsl/items/location_item.rb +0 -59
  222. data/lib/openhab/dsl/items/metadata.rb +0 -326
  223. data/lib/openhab/dsl/items/number_item.rb +0 -17
  224. data/lib/openhab/dsl/items/numeric_item.rb +0 -87
  225. data/lib/openhab/dsl/items/persistence.rb +0 -307
  226. data/lib/openhab/dsl/items/player_item.rb +0 -58
  227. data/lib/openhab/dsl/items/rollershutter_item.rb +0 -51
  228. data/lib/openhab/dsl/items/semantics/enumerable.rb +0 -91
  229. data/lib/openhab/dsl/items/semantics.rb +0 -227
  230. data/lib/openhab/dsl/items/string_item.rb +0 -51
  231. data/lib/openhab/dsl/items/switch_item.rb +0 -70
  232. data/lib/openhab/dsl/monkey_patch/actions/actions.rb +0 -4
  233. data/lib/openhab/dsl/monkey_patch/actions/script_thing_actions.rb +0 -39
  234. data/lib/openhab/dsl/monkey_patch/events/events.rb +0 -7
  235. data/lib/openhab/dsl/monkey_patch/events/item_command.rb +0 -85
  236. data/lib/openhab/dsl/monkey_patch/events/item_event.rb +0 -28
  237. data/lib/openhab/dsl/monkey_patch/events/item_state.rb +0 -61
  238. data/lib/openhab/dsl/monkey_patch/events/item_state_changed.rb +0 -60
  239. data/lib/openhab/dsl/monkey_patch/events/thing_status_info.rb +0 -33
  240. data/lib/openhab/dsl/monkey_patch/java/java.rb +0 -4
  241. data/lib/openhab/dsl/monkey_patch/java/local_time.rb +0 -44
  242. data/lib/openhab/dsl/monkey_patch/java/time_extensions.rb +0 -50
  243. data/lib/openhab/dsl/monkey_patch/java/zoned_date_time.rb +0 -45
  244. data/lib/openhab/dsl/monkey_patch/ruby/number.rb +0 -104
  245. data/lib/openhab/dsl/monkey_patch/ruby/ruby.rb +0 -6
  246. data/lib/openhab/dsl/monkey_patch/ruby/string.rb +0 -47
  247. data/lib/openhab/dsl/monkey_patch/ruby/time.rb +0 -61
  248. data/lib/openhab/dsl/openhab.rb +0 -30
  249. data/lib/openhab/dsl/persistence.rb +0 -27
  250. data/lib/openhab/dsl/rules/item_event.rb +0 -19
  251. data/lib/openhab/dsl/rules/rule.rb +0 -160
  252. data/lib/openhab/dsl/rules/rule_config.rb +0 -147
  253. data/lib/openhab/dsl/rules/triggers/generic.rb +0 -31
  254. data/lib/openhab/dsl/rules/triggers/triggers.rb +0 -11
  255. data/lib/openhab/dsl/rules/triggers/watch/watch.rb +0 -81
  256. data/lib/openhab/dsl/states.rb +0 -89
  257. data/lib/openhab/dsl/things.rb +0 -147
  258. data/lib/openhab/dsl/time/month_day.rb +0 -180
  259. data/lib/openhab/dsl/time/time_of_day.rb +0 -235
  260. data/lib/openhab/dsl/timers/manager.rb +0 -119
  261. data/lib/openhab/dsl/timers/reentrant_timer.rb +0 -38
  262. data/lib/openhab/dsl/timers/timer.rb +0 -132
  263. data/lib/openhab/dsl/timers.rb +0 -77
  264. data/lib/openhab/dsl/types/increase_decrease_type.rb +0 -23
  265. data/lib/openhab/dsl/types/next_previous_type.rb +0 -23
  266. data/lib/openhab/dsl/types/on_off_type.rb +0 -28
  267. data/lib/openhab/dsl/types/open_closed_type.rb +0 -29
  268. data/lib/openhab/dsl/types/play_pause_type.rb +0 -27
  269. data/lib/openhab/dsl/types/point_type.rb +0 -180
  270. data/lib/openhab/dsl/types/quantity_type.rb +0 -265
  271. data/lib/openhab/dsl/types/refresh_type.rb +0 -18
  272. data/lib/openhab/dsl/types/rewind_fastforward_type.rb +0 -33
  273. data/lib/openhab/dsl/types/stop_move_type.rb +0 -23
  274. data/lib/openhab/dsl/types/types.rb +0 -83
  275. data/lib/openhab/dsl/types/un_def_type.rb +0 -22
  276. data/lib/openhab/dsl/types/up_down_type.rb +0 -32
  277. data/lib/openhab/dsl/units.rb +0 -45
  278. data/lib/openhab/log/configuration.rb +0 -21
  279. data/lib/openhab/log/logger.rb +0 -282
  280. data/lib/openhab/version.rb +0 -9
  281. data/lib/openhab.rb +0 -36
@@ -0,0 +1,851 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "fileutils"
4
+ require "set"
5
+ require "shellwords"
6
+ require "time"
7
+
8
+ require_relative "jruby"
9
+ require_relative "shell"
10
+
11
+ module OpenHAB
12
+ module RSpec
13
+ # @!visibility private
14
+ class Karaf
15
+ class ScriptExtensionManagerWrapper
16
+ def initialize(manager)
17
+ @manager = manager
18
+ end
19
+
20
+ def get(type)
21
+ @manager.get(type, "jruby")
22
+ end
23
+
24
+ def default_presets
25
+ @manager.default_presets
26
+ end
27
+
28
+ def import_preset(preset)
29
+ @manager.find_preset(preset, "rspec")
30
+ end
31
+ end
32
+ private_constant :ScriptExtensionManagerWrapper
33
+
34
+ attr_reader :path
35
+ attr_accessor :include_bindings, :include_jsondb, :private_confdir, :use_root_instance
36
+
37
+ def initialize(path = nil)
38
+ @path = path
39
+ @include_bindings = true
40
+ @include_jsondb = true
41
+ @private_confdir = false
42
+ @use_root_instance = false
43
+ end
44
+
45
+ def launch
46
+ raise ArgumentError, "Path must be supplied if use_root_instance is false" unless path || use_root_instance
47
+
48
+ @path = oh_home if use_root_instance
49
+
50
+ load_boot_jars
51
+ set_env
52
+ set_java_properties
53
+ set_java_properties_from_env
54
+ unless use_root_instance
55
+ redirect_instances
56
+ create_instance
57
+ end
58
+ start_instance
59
+ end
60
+
61
+ private
62
+
63
+ # create a private instances configuration
64
+ def redirect_instances
65
+ # this is normally done directly in bin/karaf with a -D JAVA_OPT
66
+ orig_instances = "#{java.lang.System.get_property("karaf.data")}/tmp/instances"
67
+
68
+ instances_path = "#{path}/instances"
69
+ java.lang.System.set_property("karaf.instances", instances_path)
70
+ FileUtils.mkdir_p(instances_path)
71
+
72
+ new_instance_properties = "#{instances_path}/instance.properties"
73
+ return if File.exist?(new_instance_properties) && File.stat(new_instance_properties).size != 0
74
+
75
+ FileUtils.cp("#{orig_instances}/instance.properties", new_instance_properties)
76
+ end
77
+
78
+ def create_instance
79
+ find_karaf_instance_jar
80
+ # OSGi isn't up yet, so have to create the service directly
81
+ service = org.apache.karaf.instance.core.internal.InstanceServiceImpl.new
82
+ settings = org.apache.karaf.instance.core.InstanceSettings.new(0, 0, 0, path, nil, nil, nil)
83
+ root_instance = service.instances.find(&:root?)
84
+ raise ArgumentError "No root instance found to clone... has openHAB run yet?" unless root_instance
85
+
86
+ return if service.get_instance("rspec")
87
+
88
+ begin
89
+ service.clone_instance(root_instance.name, "rspec", settings, false)
90
+ rescue java.lang.NullPointerException
91
+ retry if fix_rmi_registry_npe
92
+ raise
93
+ end
94
+ ensure
95
+ extra_loaders = ::JRuby.runtime.instance_config.extra_loaders
96
+ loader = extra_loaders.find { |l| l.class_loader == @karaf_instance_loader }
97
+ extra_loaders.remove(loader)
98
+ end
99
+
100
+ def start_instance
101
+ unless use_root_instance
102
+ # these are all from karaf.instances's startup code with
103
+ # the exception of not having data be a subdir
104
+ java.lang.System.set_property("karaf.base", path)
105
+ java.lang.System.set_property("karaf.data", path)
106
+ java.lang.System.set_property("karaf.etc", "#{path}/etc")
107
+ java.lang.System.set_property("karaf.log", "#{path}/logs")
108
+ java.lang.System.set_property("java.io.tmpdir", "#{path}/tmp")
109
+ java.lang.System.set_property("karaf.startLocalConsole", "false")
110
+ java.lang.System.set_property("karaf.startRemoteShell", "false")
111
+ # set in bin/setenv to OPENHAB_USERDATA; need to move it
112
+ java.lang.System.set_property("felix.cm.dir", felix_cm)
113
+ # not handled by karaf instances
114
+ java.lang.System.set_property("openhab.userdata", path)
115
+ @oh_userdata = nil
116
+ java.lang.System.set_property("openhab.logdir", "#{path}/logs")
117
+ end
118
+ cleanup_instance
119
+ # we don't need a shutdown socket
120
+ java.lang.System.set_property("karaf.shutdown.port", "-1")
121
+ # ensure we're not logging to stdout
122
+ java.util.logging.LogManager.log_manager.reset
123
+
124
+ # launch it! (don't use Main.main; it will wait for it to be
125
+ # shut down externally)
126
+ @all_bundles_continue = nil
127
+ @class_loaders = Set.new
128
+ @main = org.apache.karaf.main.Main.new([])
129
+ launch_karaf
130
+ at_exit do
131
+ @main.destroy
132
+ # OSGi/openHAB leave a ton of threads around. Kill ourselves ASAP
133
+ code = if $!.nil? || ($!.is_a?(SystemExit) && $!.success?)
134
+ 0
135
+ elsif $!.is_a?(SystemExit)
136
+ $!.status
137
+ else
138
+ puts $!.inspect
139
+ 1
140
+ end
141
+ exit!(code)
142
+ end
143
+
144
+ set_up_bundle_listener
145
+ wait_for_start
146
+ Mocks::SynchronousExecutor.instance.main_thread = Thread.current
147
+ set_jruby_script_presets
148
+ @main
149
+ end
150
+
151
+ def launch_karaf
152
+ # we need to access internals, since we're reproducing much of Main.launch
153
+ klass = org.apache.karaf.main.Main
154
+ klass.field_accessor :classLoader, :activatorManager
155
+ klass.field_writer :framework
156
+ klass.field_reader :LOG
157
+ org.apache.karaf.main.ConfigProperties.field_reader :props, :defaultBundleStartlevel, :karafEtc,
158
+ :defaultStartLevel
159
+ klass.class_eval do
160
+ def send_private(method_name, *args)
161
+ method_name = method_name.to_s
162
+ method = self.class.java_class.declared_methods.find { |m| m.name == method_name }
163
+ method.accessible = true
164
+ method.invoke(self, *args)
165
+ end
166
+
167
+ def launch_simple
168
+ self.config = org.apache.karaf.main.ConfigProperties.new
169
+ config.perform_init
170
+ log4j_config_path = "#{java.lang.System.get_property("karaf.etc")}/org.ops4j.pax.logging.cfg"
171
+ org.apache.karaf.main.util.BootstrapLogManager.set_properties(config.props, log4j_config_path)
172
+ org.apache.karaf.main.util.BootstrapLogManager.configure_logger(self.class.LOG)
173
+
174
+ bundle_dirs = send_private(:getBundleRepos)
175
+ resolver = org.apache.karaf.main.util.SimpleMavenResolver.new(bundle_dirs)
176
+ self.classLoader = send_private(:createClassLoader, resolver)
177
+ factory = send_private(:loadFrameworkFactory, classLoader)
178
+ self.framework = factory.new_framework(config.props)
179
+
180
+ send_private(:setLogger)
181
+
182
+ framework.init
183
+ framework.start
184
+
185
+ sl = framework.adapt(org.osgi.framework.startlevel.FrameworkStartLevel.java_class)
186
+ sl.initial_bundle_start_level = config.defaultBundleStartlevel
187
+
188
+ if framework.bundle_context.bundles.length == 1
189
+ self.class.LOG.info("Installing and starting initial bundles")
190
+ startup_props_file = java.io.File.new(config.karafEtc, self.class::STARTUP_PROPERTIES_FILE_NAME)
191
+ bundles = read_bundles_from_startup_properties(startup_props_file)
192
+ send_private(:installAndStartBundles, resolver, framework.bundle_context, bundles)
193
+ self.class.LOG.info("All initial bundles installed and set to start")
194
+ end
195
+
196
+ server_info = org.apache.karaf.main.ServerInfoImpl.new(args, config)
197
+ framework.bundle_context.register_service(org.apache.karaf.info.ServerInfo.java_class, server_info, nil)
198
+
199
+ self.activatorManager = org.apache.karaf.main.KarafActivatorManager.new(classLoader, framework)
200
+
201
+ # let the caller register services now that the framework is up,
202
+ # but nothing is running yet
203
+ yield framework.bundle_context
204
+
205
+ set_start_level(config.defaultStartLevel)
206
+ end
207
+ end
208
+
209
+ @main.launch_simple do
210
+ # hook up the OSGi class loader manually
211
+ add_class_loader(@main.framework)
212
+
213
+ @framework = @main.framework
214
+ @bundle_context = @main.framework.bundle_context
215
+
216
+ # prevent entirely blocked bundles from starting at all
217
+ @main.framework.bundle_context.bundles.each do |b|
218
+ sl = b.adapt(org.osgi.framework.startlevel.BundleStartLevel.java_class)
219
+ if (start_level = START_LEVEL_OVERRIDES[b.symbolic_name])
220
+ sl.start_level = start_level
221
+ end
222
+ sl.start_level = @main.config.defaultStartLevel + 1 if blocked_bundle?(b)
223
+ end
224
+
225
+ prune_startlevels
226
+
227
+ set_up_service_listener
228
+ # replace event infrastructure with synchronous versions
229
+ wait_for_service("org.osgi.service.event.EventAdmin") do |service|
230
+ next if defined?(Mocks::EventAdmin) && service.is_a?(Mocks::EventAdmin)
231
+
232
+ require_relative "mocks/event_admin"
233
+ ea = Mocks::EventAdmin.new(@bundle_context)
234
+ bundle = org.osgi.framework.FrameworkUtil.get_bundle(service.java_class)
235
+ # we need to register it as if from the regular eventadmin bundle so other bundles
236
+ # can properly find it
237
+ bundle.bundle_context.register_service(
238
+ org.osgi.service.event.EventAdmin.java_class,
239
+ ea,
240
+ java.util.Hashtable.new(org.osgi.framework.Constants::SERVICE_RANKING => 1.to_java(:int))
241
+ )
242
+ end
243
+ wait_for_service("org.apache.karaf.features.FeaturesService") do |fs|
244
+ require_relative "mocks/bundle_install_support"
245
+ fs.class.field_reader :installSupport
246
+ field = fs.class.java_class.get_declared_field("installSupport")
247
+ field.accessible = true
248
+ field.set(fs, Mocks::BundleInstallSupport.new(fs.installSupport, self))
249
+ end
250
+ wait_for_service("org.osgi.service.cm.ConfigurationAdmin") do |ca|
251
+ # register a listener, so that we can know if the Start Level Service is busted
252
+ bundle = org.osgi.framework.FrameworkUtil.get_bundle(ca.java_class)
253
+ listener = org.osgi.service.cm.ConfigurationListener.impl do |_method, event|
254
+ next unless event.type == org.osgi.service.cm.ConfigurationEvent::CM_UPDATED
255
+ next unless event.pid == "org.openhab.startlevel"
256
+
257
+ # have to wait for the StartLevelService itself to process this event
258
+ Thread.new do
259
+ sleep 1
260
+ reset_start_level_service
261
+ end
262
+ end
263
+ bundle.bundle_context.register_service(org.osgi.service.cm.ConfigurationListener.java_class,
264
+ listener,
265
+ nil)
266
+
267
+ cfg = ca.get_configuration("org.openhab.addons", nil)
268
+ props = cfg.properties || java.util.Hashtable.new
269
+ # remove all non-binding addons
270
+ props.remove("misc")
271
+ props.remove("package")
272
+ props.remove("persistence")
273
+ props.remove("transformation")
274
+ props.remove("ui")
275
+ props.remove("binding") unless include_bindings
276
+ cfg.update(props)
277
+
278
+ # configure persistence to use the mock service
279
+ cfg = ca.get_configuration("org.openhab.persistence", nil)
280
+ props = cfg.properties || java.util.Hashtable.new
281
+ props.put("default", "default")
282
+ cfg.update(props)
283
+
284
+ # configure ephemeris with basic values
285
+ cfg = ca.get_configuration("org.openhab.ephemeris", nil)
286
+ props = cfg.properties || java.util.Hashtable.new
287
+ props.put("country", "us")
288
+ props.put("dayset-weekend", %w[SATURDAY SUNDAY])
289
+ cfg.update(props)
290
+ end
291
+ wait_for_service("org.openhab.core.automation.RuleManager") do |re|
292
+ require_relative "mocks/synchronous_executor"
293
+ # overwrite thCallbacks to one that will spy to remove threading
294
+ field = re.class.java_class.declared_field :thCallbacks
295
+ field.accessible = true
296
+ field.set(re, Mocks::CallbacksMap.new)
297
+ re.class.field_accessor :executor
298
+ re.executor = Mocks::SynchronousExecutor.instance
299
+ end
300
+ wait_for_service("org.openhab.core.thing.internal.CommunicationManager") do |cm|
301
+ require_relative "mocks/safe_caller"
302
+ field = cm.class.java_class.declared_field :safeCaller
303
+ field.accessible = true
304
+ field.set(cm, Mocks::SafeCaller.instance)
305
+ end
306
+ end
307
+ end
308
+
309
+ # entire bundle trees that are allowed to be installed,
310
+ # but not started
311
+ BLOCKED_BUNDLE_TREES = %w[
312
+ org.apache.karaf.jaas
313
+ org.apache.sshd
314
+ org.eclipse.jetty
315
+ org.ops4j.pax.web
316
+ org.openhab.automation
317
+ org.openhab.binding
318
+ org.openhab.core.io
319
+ org.openhab.io
320
+ org.openhab.transform
321
+ ].freeze
322
+ private_constant :BLOCKED_BUNDLE_TREES
323
+
324
+ ALLOWED_BUNDLES = %w[
325
+ org.openhab.core.io.monitor
326
+ ].freeze
327
+ private_constant :ALLOWED_BUNDLES
328
+
329
+ BLOCKED_COMPONENTS = {
330
+ "org.openhab.core" => %w[
331
+ org.openhab.core.addon.AddonEventFactory
332
+ org.openhab.core.binding.i18n.BindingI18nLocalizationService
333
+ org.openhab.core.internal.auth.ManagedUserProvider
334
+ org.openhab.core.internal.auth.UserRegistryImpl
335
+ ].freeze,
336
+ "org.openhab.core.automation.module.script.rulesupport" => %w[
337
+ org.openhab.core.automation.module.script.rulesupport.internal.loader.DefaultScriptFileWatcher
338
+ ].freeze,
339
+ "org.openhab.core.config.core" => %w[
340
+ org.openhab.core.config.core.internal.i18n.I18nConfigOptionsProvider
341
+ org.openhab.core.config.core.status.ConfigStatusService
342
+ org.openhab.core.config.core.status.events.ConfigStatusEventFactory
343
+ ],
344
+ "org.openhab.core.model.script" => %w[
345
+ org.openhab.core.model.script.internal.RuleHumanLanguageInterpreter
346
+ org.openhab.core.model.script.internal.engine.action.VoiceActionService
347
+ org.openhab.core.model.script.jvmmodel.ScriptItemRefresher
348
+ ].freeze,
349
+ "org.openhab.core.thing" => %w[
350
+ org.openhab.core.thing.internal.console.FirmwareUpdateConsoleCommandExtension
351
+ ],
352
+ # the following bundles are blocked completely from starting
353
+ "org.apache.karaf.http.core" => nil,
354
+ "org.apache.karaf.features.command" => nil,
355
+ "org.apache.karaf.shell.commands" => nil,
356
+ "org.apache.karaf.shell.core" => nil,
357
+ "org.apache.karaf.shell.ssh" => nil,
358
+ "org.openhab.core.audio" => nil,
359
+ "org.openhab.core.automation.module.media" => nil,
360
+ "org.openhab.core.config.discovery" => nil,
361
+ "org.openhab.core.model.lsp" => nil,
362
+ "org.openhab.core.model.rule.runtime" => nil,
363
+ "org.openhab.core.model.rule" => nil,
364
+ "org.openhab.core.model.sitemap.runtime" => nil,
365
+ "org.openhab.core.voice" => nil
366
+ }.freeze
367
+ private_constant :BLOCKED_COMPONENTS
368
+
369
+ START_LEVEL_OVERRIDES = {}.freeze
370
+ private_constant :START_LEVEL_OVERRIDES
371
+
372
+ def set_up_bundle_listener
373
+ @thing_type_tracker = @config_description_tracker = nil
374
+ wait_for_service("org.openhab.core.thing.binding.ThingTypeProvider",
375
+ filter: "(openhab.scope=core.xml.thing)") do |ttp|
376
+ ttp.class.field_reader :thingTypeTracker
377
+ @thing_type_tracker = ttp.thingTypeTracker
378
+ @thing_type_tracker.class.field_reader :openState
379
+ begin
380
+ org.openhab.core.config.core.xml.osgi.XmlDocumentBundleTracker::OpenState.field_reader :OPENED
381
+ opened = org.openhab.core.config.core.xml.osgi.XmlDocumentBundleTracker::OpenState.OPENED
382
+ rescue NameError
383
+ # @deprecated OH3.4
384
+ org.openhab.core.config.xml.osgi.XmlDocumentBundleTracker::OpenState.field_reader :OPENED
385
+ opened = org.openhab.core.config.xml.osgi.XmlDocumentBundleTracker::OpenState.OPENED
386
+ end
387
+ sleep until @thing_type_tracker.openState == opened
388
+ @bundle_context.bundles.each do |bundle|
389
+ @thing_type_tracker.adding_bundle(bundle, nil)
390
+ end
391
+ end
392
+ wait_for_service("org.openhab.core.config.core.ConfigDescriptionProvider",
393
+ filter: "(openhab.scope=core.xml.config)") do |cdp|
394
+ cdp.class.field_reader :configDescriptionTracker
395
+ @config_description_tracker = cdp.configDescriptionTracker
396
+ @config_description_tracker.class.field_reader :openState
397
+ begin
398
+ org.openhab.core.config.core.xml.osgi.XmlDocumentBundleTracker::OpenState.field_reader :OPENED
399
+ opened = org.openhab.core.config.core.xml.osgi.XmlDocumentBundleTracker::OpenState.OPENED
400
+ rescue NameError
401
+ # @deprecated OH3.4
402
+ org.openhab.core.config.xml.osgi.XmlDocumentBundleTracker::OpenState.field_reader :OPENED
403
+ opened = org.openhab.core.config.xml.osgi.XmlDocumentBundleTracker::OpenState.OPENED
404
+ end
405
+ sleep until @config_description_tracker.openState == opened
406
+ @bundle_context.bundles.each do |bundle|
407
+ @config_description_tracker.adding_bundle(bundle, nil)
408
+ end
409
+ end
410
+ wait_for_service("org.osgi.service.component.runtime.ServiceComponentRuntime") { |scr| @scr = scr }
411
+ @bundle_context.add_bundle_listener do |event|
412
+ bundle = event.bundle
413
+ bundle_name = bundle.symbolic_name
414
+ sl = bundle.adapt(org.osgi.framework.startlevel.BundleStartLevel.java_class)
415
+ if (start_level = START_LEVEL_OVERRIDES[bundle_name])
416
+ sl.start_level = start_level
417
+ end
418
+ sl.start_level = @main.config.defaultStartLevel + 1 if blocked_bundle?(bundle)
419
+
420
+ if event.type == org.osgi.framework.BundleEvent::RESOLVED
421
+ @thing_type_tracker&.adding_bundle(event.bundle, nil)
422
+ @config_description_tracker&.adding_bundle(event.bundle, nil)
423
+ end
424
+ next unless event.type == org.osgi.framework.BundleEvent::STARTED
425
+
426
+ # just in case
427
+ raise "blocked bundle #{bundle.symbolic_name} started!" if blocked_bundle?(bundle)
428
+
429
+ add_class_loader(bundle)
430
+
431
+ # as soon as we _can_ do this, do it
432
+ link_osgi if bundle.get_resource("org/slf4j/LoggerFactory.class")
433
+
434
+ if @all_bundles_continue && all_bundles_started?
435
+ @all_bundles_continue.call
436
+ @all_bundles_continue = nil
437
+ end
438
+
439
+ if bundle_name == "org.openhab.core"
440
+ require_relative "mocks/synchronous_executor"
441
+
442
+ org.openhab.core.common.ThreadPoolManager.field_accessor :pools
443
+ org.openhab.core.common.ThreadPoolManager.pools = Mocks::SynchronousExecutorMap.instance
444
+ end
445
+ if bundle_name == "org.openhab.core.thing"
446
+ require_relative "mocks/bundle_resolver"
447
+ bundle.bundle_context.register_service(
448
+ org.openhab.core.util.BundleResolver.java_class,
449
+ Mocks::BundleResolver.instance,
450
+ java.util.Hashtable.new(org.osgi.framework.Constants::SERVICE_RANKING => 1.to_java(:int))
451
+ )
452
+
453
+ wait_for_service("org.openhab.core.thing.ThingManager") do |tm|
454
+ begin
455
+ # @deprecated OH3.4
456
+ tm.class.field_accessor :bundleResolver
457
+ tm.bundleResolver = Mocks::BundleResolver.instance
458
+ rescue NameError
459
+ # OH4
460
+ # I think the mock BundleResolver registration above is sufficient.
461
+ # It will be injected by OSGi and we don't need to override it again in OH4
462
+ end
463
+
464
+ require_relative "mocks/safe_caller"
465
+ field = tm.class.java_class.declared_field :safeCaller
466
+ field.accessible = true
467
+ field.set(tm, Mocks::SafeCaller.instance)
468
+
469
+ require_relative "mocks/thing_handler"
470
+ thf = Mocks::ThingHandlerFactory.instance
471
+ bundle = org.osgi.framework.FrameworkUtil.get_bundle(org.openhab.core.thing.Thing.java_class)
472
+ Mocks::BundleResolver.instance.register_class(thf.class, bundle)
473
+ bundle.bundle_context.register_service(org.openhab.core.thing.binding.ThingHandlerFactory.java_class, thf,
474
+ nil)
475
+ end
476
+ end
477
+ if bundle_name == "org.openhab.core.automation"
478
+ org.openhab.core.automation.internal.TriggerHandlerCallbackImpl.field_accessor :executor
479
+ end
480
+
481
+ next unless BLOCKED_COMPONENTS.key?(bundle_name)
482
+
483
+ components = BLOCKED_COMPONENTS[bundle_name]
484
+ dtos = if components.nil?
485
+ @scr.get_component_description_dt_os(bundle)
486
+ else
487
+ Array(components).map { |component| @scr.get_component_description_dto(bundle, component) }
488
+ end.compact
489
+ dtos.each do |dto|
490
+ @scr.disable_component(dto) if @scr.component_enabled?(dto)
491
+ end
492
+ rescue Exception => e
493
+ puts e.inspect
494
+ puts e.backtrace
495
+ end
496
+ @bundle_context.bundles.each do |bundle|
497
+ next unless bundle.symbolic_name.start_with?("org.openhab.core")
498
+
499
+ add_class_loader(bundle)
500
+ end
501
+ end
502
+
503
+ def set_up_service_listener
504
+ @awaiting_services = {}
505
+ @bundle_context.add_service_listener do |event|
506
+ next unless event.type == org.osgi.framework.ServiceEvent::REGISTERED
507
+
508
+ ref = event.service_reference
509
+ service = nil
510
+
511
+ ref.get_property(org.osgi.framework.Constants::OBJECTCLASS).each do |klass|
512
+ next unless @awaiting_services.key?(klass)
513
+
514
+ @awaiting_services[klass].each do |(block, filter)|
515
+ service ||= @bundle_context.get_service(ref)
516
+ next if filter && !filter.match(ref)
517
+
518
+ service ||= @bundle_context.get_service(ref)
519
+ break unless service
520
+
521
+ bundle = org.osgi.framework.FrameworkUtil.get_bundle(service.java_class)
522
+ add_class_loader(bundle) if bundle
523
+ block.call(service)
524
+ end
525
+ end
526
+ rescue Exception => e
527
+ puts e.inspect
528
+ puts e.backtrace
529
+ end
530
+ end
531
+
532
+ def add_class_loader(bundle)
533
+ return if @class_loaders.include?(bundle.symbolic_name)
534
+
535
+ @class_loaders << bundle.symbolic_name
536
+ ::JRuby.runtime.instance_config.add_loader(JRuby::OSGiBundleClassLoader.new(bundle))
537
+ end
538
+
539
+ def wait_for_service(service_name, filter: nil, &block)
540
+ if defined?(OSGi) &&
541
+ (services = OSGi.services(service_name, filter: filter))
542
+ services.each(&block)
543
+ end
544
+
545
+ waiters = @awaiting_services[service_name] ||= []
546
+ waiters << [block, filter && @bundle_context.create_filter(filter)]
547
+ end
548
+
549
+ def wait_for_start
550
+ wait do |continue|
551
+ @all_bundles_continue = continue
552
+ next continue.call if all_bundles_started?
553
+ end
554
+ end
555
+
556
+ def all_bundles_started?
557
+ has_core = false
558
+ result = @bundle_context.bundles.all? do |b|
559
+ has_core = true if b.symbolic_name == "org.openhab.core"
560
+ b.state == org.osgi.framework.Bundle::ACTIVE ||
561
+ blocked_bundle?(b)
562
+ end
563
+
564
+ result && has_core
565
+ end
566
+
567
+ def blocked_bundle?(bundle)
568
+ return false if ALLOWED_BUNDLES.include?(bundle.symbolic_name)
569
+
570
+ BLOCKED_COMPONENTS.fetch(bundle.symbolic_name, false).nil? ||
571
+ BLOCKED_BUNDLE_TREES.any? { |tree| bundle.symbolic_name.start_with?(tree) } ||
572
+ bundle.fragment?
573
+ end
574
+
575
+ def wait
576
+ mutex = Mutex.new
577
+ cond = ConditionVariable.new
578
+ skip_wait = false
579
+
580
+ continue = lambda do
581
+ # if continue was called synchronously, we can just return
582
+ next skip_wait = true if mutex.owned?
583
+
584
+ mutex.synchronize { cond.signal }
585
+ end
586
+ mutex.synchronize do
587
+ yield continue
588
+ cond.wait(mutex) unless skip_wait
589
+ end
590
+ end
591
+
592
+ def link_osgi
593
+ OSGi.instance_variable_set(:@bundle, @framework) if require "openhab/osgi"
594
+ end
595
+
596
+ # import global variables and constants that the DSL expects,
597
+ # since we're going to be running it in this same VM
598
+ def set_jruby_script_presets
599
+ wait_for_service("org.openhab.core.automation.module.script.internal.ScriptExtensionManager") do |sem|
600
+ # since we're not created by the ScriptEngineManager, this never gets set; manually set it
601
+ $se = $scriptExtension = ScriptExtensionManagerWrapper.new(sem)
602
+ end
603
+ end
604
+
605
+ # instance isn't part of the boot jars, but we need access to it
606
+ # before we boot karaf in order to create the clone, so we have to
607
+ # find it manually
608
+ def find_karaf_instance_jar
609
+ resolver = org.apache.karaf.main.util.SimpleMavenResolver.new([java.io.File.new("#{oh_runtime}/system")])
610
+ slf4j_version = find_maven_jar_version("org.ops4j.pax.logging", "pax-logging-api")
611
+ slf4j = resolver.resolve(java.net.URI.new("mvn:org.ops4j.pax.logging/pax-logging-api/#{slf4j_version}"))
612
+ karaf_version = find_jar_version("#{oh_runtime}/lib/boot", "org.apache.karaf.main")
613
+ karaf_instance = resolver.resolve(
614
+ java.net.URI.new(
615
+ "mvn:org.apache.karaf.instance/org.apache.karaf.instance.core/#{karaf_version}"
616
+ )
617
+ )
618
+ @karaf_instance_loader = java.net.URLClassLoader.new(
619
+ [slf4j.to_url, karaf_instance.to_url].to_java(java.net.URL), ::JRuby.runtime.jruby_class_loader
620
+ )
621
+ ::JRuby.runtime.instance_config.add_loader(@karaf_instance_loader)
622
+ end
623
+
624
+ def find_maven_jar_version(group, bundle)
625
+ Dir["#{oh_runtime}/system/#{group.tr(".", "/")}/#{bundle}/*"].map { |version| version.split("/").last }.max
626
+ end
627
+
628
+ def find_jar_version(path, bundle)
629
+ prefix = "#{path}/#{bundle}-"
630
+ Dir["#{prefix}*.jar"].map { |jar| jar.split("-", 2).last[0...-4] }.max
631
+ end
632
+
633
+ def load_boot_jars
634
+ (Dir["#{oh_runtime}/lib/boot/*.jar"] +
635
+ Dir["#{oh_runtime}/lib/endorsed/*.jar"] +
636
+ Dir["#{oh_runtime}/lib/jdk9plus/*.jar"]).each do |jar|
637
+ require jar
638
+ end
639
+ end
640
+
641
+ def set_env
642
+ ENV["DIRNAME"] = "#{oh_runtime}/bin"
643
+ ENV["KARAF_HOME"] = oh_runtime
644
+ if private_confdir
645
+ ENV["OPENHAB_CONF"] = "#{path}/conf"
646
+ FileUtils.mkdir_p([
647
+ "#{path}/conf/items",
648
+ "#{path}/conf/things",
649
+ "#{path}/conf/scripts",
650
+ "#{path}/conf/rules",
651
+ "#{path}/conf/persistence",
652
+ "#{path}/conf/sitemaps",
653
+ "#{path}/conf/transform"
654
+ ])
655
+ end
656
+ Shell.source_env_from("#{oh_runtime}/bin/setenv")
657
+ end
658
+
659
+ def set_java_properties
660
+ [ENV.fetch("JAVA_OPTS", nil), ENV.fetch("EXTRA_JAVA_OPTS", nil)].compact.each do |java_opts|
661
+ Shellwords.split(java_opts).each do |arg|
662
+ next unless arg.start_with?("-D")
663
+
664
+ k, v = arg[2..].split("=", 2)
665
+ java.lang.System.set_property(k, v)
666
+ end
667
+ end
668
+ end
669
+
670
+ # we can't set Java ENV directly, so we have to try and set some things
671
+ # as system properties
672
+ def set_java_properties_from_env
673
+ ENV.each do |(k, v)|
674
+ next unless k.match?(/^(?:KARAF|OPENHAB)_/)
675
+
676
+ prop = k.downcase.tr("_", ".")
677
+ next unless java.lang.System.get_property(prop).nil?
678
+
679
+ java.lang.System.set_property(prop, v)
680
+ end
681
+ end
682
+
683
+ def oh_home
684
+ @oh_home ||= ENV.fetch("OPENHAB_HOME", "/usr/share/openhab")
685
+ end
686
+
687
+ def oh_runtime
688
+ @oh_runtime ||= ENV.fetch("OPENHAB_RUNTIME", "#{oh_home}/runtime")
689
+ end
690
+
691
+ def oh_conf
692
+ @oh_conf ||= ENV.fetch("OPENHAB_CONF")
693
+ end
694
+
695
+ def oh_userdata
696
+ @oh_userdata ||= java.lang.System.get_property("openhab.userdata")
697
+ end
698
+
699
+ def felix_cm
700
+ @felix_cm ||= use_root_instance ? ENV.fetch("OPENHAB_USERDATA") : "#{path}/config"
701
+ end
702
+
703
+ def cleanup_instance
704
+ cleanup_clone
705
+ minimize_installed_features
706
+ filter_addons
707
+ end
708
+
709
+ def cleanup_clone
710
+ FileUtils.rm_rf(["#{oh_userdata}/cache",
711
+ "#{oh_userdata}/jsondb/backup",
712
+ "#{oh_userdata}/marketplace",
713
+ "#{oh_userdata}/logs/*",
714
+ "#{oh_userdata}/tmp/*",
715
+ "#{oh_userdata}/jsondb/org.openhab.marketplace.json",
716
+ "#{oh_userdata}/jsondb/org.openhab.jsonaddonservice.json",
717
+ "#{path}/config/org/apache/felix/fileinstall",
718
+ "#{felix_cm}/org/openhab/jsonaddonservice.config"])
719
+ FileUtils.rm_rf("#{oh_userdata}/jsondb") unless include_jsondb
720
+ end
721
+
722
+ def filter_addons
723
+ config_file = "#{path}/etc/org.apache.felix.fileinstall-deploy.cfg"
724
+ return unless File.exist?(config_file)
725
+
726
+ config = File.read(config_file)
727
+ new_config = config.sub(/^(felix\.fileinstall\.filter\s+=)[^\n]+$/, "\\1 .*/openhab-addons-[^/]+\\.kar")
728
+
729
+ return if config == new_config
730
+
731
+ File.write(config_file, new_config)
732
+ end
733
+
734
+ def prune_startlevels
735
+ config_file = java.lang.System.get_property("openhab.servicecfg")
736
+ return unless File.exist?(config_file)
737
+
738
+ startlevels = File.read(config_file)
739
+ startlevels.sub!(",rules:refresh,rules:dslprovider", "")
740
+
741
+ target_file = "#{oh_userdata}/services.cfg"
742
+ target_file_contents = File.read(target_file) if File.exist?(target_file)
743
+ File.write(target_file, startlevels) unless target_file_contents == startlevels
744
+ java.lang.System.set_property("openhab.servicecfg", target_file)
745
+ end
746
+
747
+ # workaround for https://github.com/openhab/openhab-core/pull/3092
748
+ def reset_start_level_service
749
+ sls = OSGi.service("org.openhab.core.service.StartLevelService")
750
+
751
+ unless sls
752
+ # try a different (hacky!) way to get it, since in openHAB 3.2.0 it's not exposed as a service
753
+ scr = OSGi.service("org.osgi.service.component.runtime.ServiceComponentRuntime")
754
+ scr.class.field_reader :componentRegistry
755
+ cr = scr.componentRegistry
756
+
757
+ oh_core_bundle = org.osgi.framework.FrameworkUtil.get_bundle(org.openhab.core.OpenHAB.java_class)
758
+ ch = cr.get_component_holder(oh_core_bundle, "org.openhab.core.service.StartLevelService")
759
+ sls = ch&.components&.first&.component_instance&.instance
760
+ end
761
+
762
+ # no SLS yet? then we couldn't have hit the bug
763
+ return unless sls
764
+
765
+ rs = OSGi.service("org.openhab.core.service.ReadyService")
766
+ sls.class.field_reader :trackers, :markers
767
+ rs.class.field_reader :trackers
768
+ return unless sls.markers.empty?
769
+ # SLS thinks it has trackers that RS doesn't?! Yeah, we hit the bug
770
+ return if (sls.trackers.values - rs.trackers.keys).empty?
771
+
772
+ ca = OSGi.service("org.osgi.service.cm.ConfigurationAdmin")
773
+ cfg = ca.get_configuration("org.openhab.startlevel", nil)
774
+ props = cfg.properties
775
+ config = props.keys.to_h { |k| [k, props.get(k)] }
776
+ m = sls.class.java_class.get_declared_method("modified", java.util.Map)
777
+ m.accessible = true
778
+ sls.trackers.clear
779
+ m.invoke(sls, config)
780
+ end
781
+
782
+ def minimize_installed_features
783
+ # cuts down openhab-runtime-base significantly, makes sure
784
+ # openhab-runtime-ui doesn't get installed (from profile.cfg),
785
+ # double-makes-sure no addons get installed, and marks several
786
+ # bundles to not actually start, even though they must still be
787
+ # installed to meet dependencies
788
+ version = find_maven_jar_version("org.openhab.core.bundles", "org.openhab.core")
789
+ File.write("#{oh_userdata}/etc/org.apache.karaf.features.xml", <<~XML)
790
+ <?xml version="1.0" encoding="UTF-8"?>
791
+ <featuresProcessing xmlns="http://karaf.apache.org/xmlns/features-processing/v1.0.0" xmlns:f="http://karaf.apache.org/xmlns/features/v1.6.0">
792
+ <!-- From openHAB 3.2.0 -->
793
+ <bundleReplacements>
794
+ <bundle originalUri="mvn:org.ops4j.pax.logging/pax-logging-api/[0,2.0.13)" replacement="mvn:org.ops4j.pax.logging/pax-logging-api/2.0.13" mode="maven" />
795
+ <bundle originalUri="mvn:org.ops4j.pax.logging/pax-logging-log4j2/[0,2.0.13)" replacement="mvn:org.ops4j.pax.logging/pax-logging-log4j2/2.0.13" mode="maven" />
796
+ <bundle originalUri="mvn:org.ops4j.pax.logging/pax-logging-logback/[0,2.0.13)" replacement="mvn:org.ops4j.pax.logging/pax-logging-logback/2.0.13" mode="maven" />
797
+ </bundleReplacements>
798
+
799
+ <blacklistedFeatures>
800
+ <feature>openhab-runtime-ui</feature>
801
+ <feature>openhab-core-ui*</feature>
802
+ <feature>openhab-misc-*</feature>
803
+ <feature>openhab-persistence-*</feature>
804
+ <feature>openhab-package-standard</feature>
805
+ <feature>openhab-ui-*</feature>
806
+ <feature>openhab-voice-*</feature>
807
+ </blacklistedFeatures>
808
+ <featureReplacements>
809
+ <replacement mode="replace">
810
+ <feature name="openhab-runtime-base" version="#{version.sub("-", ".")}">
811
+ <f:feature>openhab-core-base</f:feature>
812
+ <f:feature>openhab-core-automation-module-script</f:feature>
813
+ <f:feature>openhab-core-automation-module-script-rulesupport</f:feature>
814
+ <f:feature>openhab-core-automation-module-media</f:feature>
815
+ <f:feature>openhab-core-model-item</f:feature>
816
+ <f:feature>openhab-core-model-persistence</f:feature>
817
+ <f:feature>openhab-core-model-rule</f:feature>
818
+ <f:feature>openhab-core-model-script</f:feature>
819
+ <f:feature>openhab-core-model-sitemap</f:feature>
820
+ <f:feature>openhab-core-model-thing</f:feature>
821
+ <f:feature>openhab-core-storage-json</f:feature>
822
+ <f:feature>openhab-transport-http</f:feature>
823
+ <f:feature prerequisite="true">wrapper</f:feature>
824
+ <f:bundle>mvn:org.openhab.core.bundles/org.openhab.core.karaf/#{version}</f:bundle>
825
+ </feature>
826
+ </replacement>
827
+ </featureReplacements>
828
+ </featuresProcessing>
829
+ XML
830
+ end
831
+
832
+ def fix_rmi_registry_npe
833
+ full_path = File.join(oh_userdata, "etc/org.apache.karaf.management.cfg")
834
+ stat = File.stat(full_path)
835
+ return false unless stat.file? && stat.size.zero? # rubocop:disable Style/ZeroLengthPredicate
836
+
837
+ contents = <<~TEXT
838
+ # This file was autogenerated by openhab-scripting.
839
+ # Feel free to customize.
840
+ rmiRegistryPort = 1099
841
+ rmiServerPort = 44444
842
+ TEXT
843
+ begin
844
+ File.write(full_path, contents)
845
+ rescue Errno::EACCESS
846
+ abort "Unable to write to `#{full_path}`. Please use sudo and set it to:\n\n#{contents}"
847
+ end
848
+ end
849
+ end
850
+ end
851
+ end