Skip to content

Commit cec746d

Browse files
committed
YAML things: Fix way to request models refresh to avoid a circular ref
Fix the circular reference. YamlModelRepositoryImpl was referencing YamlThingProvider through YamlModelListener and YamlThingProvider was referencing YamlModelRepository. So circular reference. The reference between YamlThingProvider and YamlModelRepository is now optional. The refresh now only update things attached to a new loaded binding. Signed-off-by: Laurent Garnier <[email protected]>
1 parent 26167c9 commit cec746d

File tree

4 files changed

+77
-18
lines changed

4 files changed

+77
-18
lines changed

bundles/org.openhab.core.model.yaml/src/main/java/org/openhab/core/model/yaml/YamlModelListener.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
*
2626
* @author Laurent Garnier - Initial contribution
2727
* @author Laurent Garnier - Added basic version management
28+
* @author Laurent Garnier - Added method refreshedModel
2829
*/
2930
@NonNullByDefault
3031
public interface YamlModelListener<T extends YamlElement> {
@@ -56,6 +57,15 @@ public interface YamlModelListener<T extends YamlElement> {
5657
*/
5758
void removedModel(String modelName, Collection<T> elements);
5859

60+
/**
61+
* Method called by the model repository when elements from a model are refreshed. All elements are contained
62+
* in the collection.
63+
*
64+
* @param modelName the name of the model
65+
* @param elements the collection of refreshed elements
66+
*/
67+
void refreshedModel(String modelName, Collection<T> elements);
68+
5969
/**
6070
* Get the DTO class to be used for each object of this model type. The DTO class MUST implement {@link YamlElement}
6171
* and fulfill all requirements defined for the interface.

bundles/org.openhab.core.model.yaml/src/main/java/org/openhab/core/model/yaml/internal/YamlModelRepositoryImpl.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -568,7 +568,6 @@ public void updateElementInModel(String modelName, YamlElement element) {
568568
@Override
569569
@SuppressWarnings({ "rawtypes", "unchecked" })
570570
public void refreshModelElements(String modelName, String elementName) {
571-
logger.info("Refreshing {} from YAML model {}", elementName, modelName);
572571
YamlModelWrapper model = modelCache.get(modelName);
573572
if (model == null) {
574573
logger.warn("Failed to refresh model {} because it is not known.", modelName);
@@ -589,7 +588,7 @@ public void refreshModelElements(String modelName, String elementName) {
589588
List elements = parseJsonNodes(modelNodes != null ? modelNodes : List.of(), modelMapNode, elementClass,
590589
null, null);
591590
if (!elements.isEmpty()) {
592-
listener.updatedModel(modelName, elements);
591+
listener.refreshedModel(modelName, elements);
593592
}
594593
});
595594
}

bundles/org.openhab.core.model.yaml/src/main/java/org/openhab/core/model/yaml/internal/semantics/YamlSemanticTagProvider.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,10 @@ public void removedModel(String modelName, Collection<YamlSemanticTagDTO> elemen
105105
});
106106
}
107107

108+
@Override
109+
public void refreshedModel(String modelName, Collection<YamlSemanticTagDTO> elements) {
110+
}
111+
108112
private SemanticTag mapSemanticTag(YamlSemanticTagDTO tagDTO) {
109113
return new SemanticTagImpl(tagDTO.uid, tagDTO.label, tagDTO.description, tagDTO.synonyms);
110114
}

bundles/org.openhab.core.model.yaml/src/main/java/org/openhab/core/model/yaml/internal/things/YamlThingProvider.java

Lines changed: 62 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,6 @@ public class YamlThingProvider extends AbstractProvider<Thing>
8484

8585
private final Logger logger = LoggerFactory.getLogger(YamlThingProvider.class);
8686

87-
private final YamlModelRepository modelRepository;
8887
private final BundleResolver bundleResolver;
8988
private final ThingTypeRegistry thingTypeRegistry;
9089
private final ChannelTypeRegistry channelTypeRegistry;
@@ -93,6 +92,7 @@ public class YamlThingProvider extends AbstractProvider<Thing>
9392

9493
private final List<ThingHandlerFactory> thingHandlerFactories = new CopyOnWriteArrayList<>();
9594
private final Set<String> loadedXmlThingTypes = new CopyOnWriteArraySet<>();
95+
private final List<ThingHandlerFactory> thingHandlerFactoriesToProcess = new CopyOnWriteArrayList<>();
9696

9797
private final Map<String, Collection<Thing>> thingsMap = new ConcurrentHashMap<>();
9898

@@ -148,17 +148,18 @@ public void run() {
148148

149149
private @Nullable Thread lazyRetryThread;
150150

151+
private @Nullable YamlModelRepository modelRepository;
152+
151153
private record QueueContent(ThingHandlerFactory thingHandlerFactory, ThingTypeUID thingTypeUID,
152154
Configuration configuration, ThingUID thingUID, @Nullable ThingUID bridgeUID) {
153155
}
154156

155157
@Activate
156-
public YamlThingProvider(final @Reference YamlModelRepository modelRepository,
157-
final @Reference BundleResolver bundleResolver, final @Reference ThingTypeRegistry thingTypeRegistry,
158+
public YamlThingProvider(final @Reference BundleResolver bundleResolver,
159+
final @Reference ThingTypeRegistry thingTypeRegistry,
158160
final @Reference ChannelTypeRegistry channelTypeRegistry,
159161
final @Reference ConfigDescriptionRegistry configDescriptionRegistry,
160162
final @Reference LocaleProvider localeProvider) {
161-
this.modelRepository = modelRepository;
162163
this.bundleResolver = bundleResolver;
163164
this.thingTypeRegistry = thingTypeRegistry;
164165
this.channelTypeRegistry = channelTypeRegistry;
@@ -240,14 +241,29 @@ public void removedModel(String modelName, Collection<YamlThingDTO> elements) {
240241
}
241242
}
242243

244+
@Override
245+
public void refreshedModel(String modelName, Collection<YamlThingDTO> elements) {
246+
List<YamlThingDTO> elementsNeedingRefresh = elements.stream().filter(this::refreshNeeded).toList();
247+
if (!elementsNeedingRefresh.isEmpty()) {
248+
logger.info("Refreshing {} {} from YAML model {}", elementsNeedingRefresh.size(),
249+
getElementClass().getAnnotation(YamlElementName.class).value(), modelName);
250+
updatedModel(modelName, elementsNeedingRefresh);
251+
} else {
252+
logger.debug("No refresh needed from YAML model {}", modelName);
253+
}
254+
}
255+
243256
@Reference(cardinality = ReferenceCardinality.MULTIPLE, policy = ReferencePolicy.DYNAMIC)
244257
protected void addThingHandlerFactory(final ThingHandlerFactory thingHandlerFactory) {
258+
logger.debug("addThingHandlerFactory {}", thingHandlerFactory.getClass().getSimpleName());
245259
thingHandlerFactories.add(thingHandlerFactory);
246-
thingHandlerFactoryAdded(thingHandlerFactory);
260+
thingHandlerFactoriesToProcess.add(thingHandlerFactory);
261+
processAddedthingHandlerFactory(thingHandlerFactory);
247262
}
248263

249264
protected void removeThingHandlerFactory(final ThingHandlerFactory thingHandlerFactory) {
250265
thingHandlerFactories.remove(thingHandlerFactory);
266+
thingHandlerFactoriesToProcess.remove(thingHandlerFactory);
251267
}
252268

253269
@Reference
@@ -267,26 +283,56 @@ public void onReadyMarkerAdded(ReadyMarker readyMarker) {
267283
} else if (XML_THING_TYPE.equals(type)) {
268284
String bsn = readyMarker.getIdentifier();
269285
loadedXmlThingTypes.add(bsn);
270-
thingHandlerFactories.stream().filter(factory -> bsn.equals(getBundleName(factory))).forEach(factory -> {
271-
thingHandlerFactoryAdded(factory);
272-
});
286+
thingHandlerFactoriesToProcess.stream().filter(factory -> bsn.equals(getBundleName(factory)))
287+
.forEach(factory -> {
288+
processAddedthingHandlerFactory(factory);
289+
});
273290
}
274291
}
275292

293+
@Reference(cardinality = ReferenceCardinality.OPTIONAL, policy = ReferencePolicy.DYNAMIC)
294+
public void setModelRepository(final YamlModelRepository modelRepository) {
295+
logger.debug("setModelRepository");
296+
this.modelRepository = modelRepository;
297+
thingHandlerFactoriesToProcess.forEach(factory -> {
298+
processAddedthingHandlerFactory(factory);
299+
});
300+
}
301+
302+
public void unsetModelRepository(final YamlModelRepository modelRepository) {
303+
this.modelRepository = null;
304+
}
305+
276306
@Override
277307
public void onReadyMarkerRemoved(ReadyMarker readyMarker) {
278308
loadedXmlThingTypes.remove(readyMarker.getIdentifier());
279309
}
280310

281-
private void thingHandlerFactoryAdded(ThingHandlerFactory thingHandlerFactory) {
311+
private boolean refreshNeeded(YamlThingDTO thingDto) {
312+
ThingUID thingUID = new ThingUID(thingDto.uid);
313+
String[] segments = thingUID.getAsString().split(AbstractUID.SEPARATOR);
314+
ThingTypeUID thingTypeUID = new ThingTypeUID(thingUID.getBindingId(), segments[1]);
315+
ThingHandlerFactory handlerFactory = thingHandlerFactoriesToProcess.stream()
316+
.filter(thf -> thf.supportsThingType(thingTypeUID)).findFirst().orElse(null);
317+
return handlerFactory != null;
318+
}
319+
320+
private void processAddedthingHandlerFactory(ThingHandlerFactory thingHandlerFactory) {
282321
String bundleName = getBundleName(thingHandlerFactory);
283-
if (bundleName != null && loadedXmlThingTypes.contains(bundleName)) {
284-
logger.debug("Refreshing models due to new thing handler factory {}",
285-
thingHandlerFactory.getClass().getSimpleName());
286-
thingsMap.keySet().forEach(modelName -> {
287-
modelRepository.refreshModelElements(modelName,
288-
getElementClass().getAnnotation(YamlElementName.class).value());
289-
});
322+
YamlModelRepository repository = modelRepository;
323+
if (repository != null && bundleName != null && loadedXmlThingTypes.contains(bundleName)) {
324+
if (!thingsMap.isEmpty()) {
325+
logger.debug("Refreshing models due to new thing handler factory {}",
326+
thingHandlerFactory.getClass().getSimpleName());
327+
thingsMap.keySet().forEach(modelName -> {
328+
repository.refreshModelElements(modelName,
329+
getElementClass().getAnnotation(YamlElementName.class).value());
330+
});
331+
} else {
332+
logger.debug("No things yet loaded; no need to trigger a refresh due to new thing handler factory {}",
333+
thingHandlerFactory.getClass().getSimpleName());
334+
}
335+
thingHandlerFactoriesToProcess.remove(thingHandlerFactory);
290336
}
291337
}
292338

0 commit comments

Comments
 (0)