diff --git a/bundles/org.openhab.core.model.core/src/main/java/org/openhab/core/model/core/internal/ModelRepositoryImpl.java b/bundles/org.openhab.core.model.core/src/main/java/org/openhab/core/model/core/internal/ModelRepositoryImpl.java index 287e9adf09..40afefa0e9 100644 --- a/bundles/org.openhab.core.model.core/src/main/java/org/openhab/core/model/core/internal/ModelRepositoryImpl.java +++ b/bundles/org.openhab.core.model.core/src/main/java/org/openhab/core/model/core/internal/ModelRepositoryImpl.java @@ -98,20 +98,22 @@ public class ModelRepositoryImpl implements ModelRepository { @Override public boolean addOrRefreshModel(String name, final InputStream originalInputStream) { + logger.info("Loading model '{}'", name); Resource resource = null; - InputStream inputStream = null; - try { - if (originalInputStream != null) { - byte[] bytes = originalInputStream.readAllBytes(); - String validationResult = validateModel(name, new ByteArrayInputStream(bytes)); - if (validationResult != null) { - logger.warn("Configuration model '{}' has errors, therefore ignoring it: {}", name, - validationResult); - removeModel(name); - return false; - } - inputStream = new ByteArrayInputStream(bytes); + byte[] bytes = null; + try (InputStream inputStream = originalInputStream) { + bytes = inputStream.readAllBytes(); + String validationResult = validateModel(name, new ByteArrayInputStream(bytes)); + if (validationResult != null) { + logger.warn("Configuration model '{}' has errors, therefore ignoring it: {}", name, validationResult); + removeModel(name); + return false; } + } catch (IOException e) { + logger.warn("Configuration model '{}' cannot be parsed correctly!", name, e); + return false; + } + try (InputStream inputStream = new ByteArrayInputStream(bytes)) { resource = getResource(name); if (resource == null) { synchronized (resourceSet) { @@ -123,13 +125,6 @@ public class ModelRepositoryImpl implements ModelRepository { Resource.Factory.Registry.INSTANCE.getExtensionToFactoryMap().remove("*"); resource = resourceSet.createResource(URI.createURI(name)); if (resource != null) { - logger.info("Loading model '{}'", name); - if (inputStream == null) { - logger.warn( - "Resource '{}' not found. You have to pass an inputStream to create the resource.", - name); - return false; - } resource.load(inputStream, resourceOptions); notifyListeners(name, EventType.ADDED); return true; @@ -141,12 +136,7 @@ public class ModelRepositoryImpl implements ModelRepository { } else { synchronized (resourceSet) { resource.unload(); - logger.info("Refreshing model '{}'", name); - if (inputStream == null) { - resource.load(resourceOptions); - } else { - resource.load(inputStream, resourceOptions); - } + resource.load(inputStream, resourceOptions); notifyListeners(name, EventType.MODIFIED); return true; } @@ -156,13 +146,6 @@ public class ModelRepositoryImpl implements ModelRepository { if (resource != null) { resourceSet.getResources().remove(resource); } - } finally { - if (inputStream != null) { - try { - inputStream.close(); - } catch (IOException e) { - } - } } return false; } @@ -189,7 +172,7 @@ public class ModelRepositoryImpl implements ModelRepository { List resourceListCopy = new ArrayList<>(resourceSet.getResources()); return resourceListCopy.stream().filter(input -> { - return input != null && input.getURI().lastSegment().contains(".") && input.isLoaded() + return input.getURI().lastSegment().contains(".") && input.isLoaded() && modelType.equalsIgnoreCase(input.getURI().fileExtension()); }).map(from -> { return from.getURI().path(); @@ -203,16 +186,16 @@ public class ModelRepositoryImpl implements ModelRepository { // Make a copy to avoid ConcurrentModificationException List resourceListCopy = new ArrayList<>(resourceSet.getResources()); for (Resource resource : resourceListCopy) { - if (resource != null && resource.getURI().lastSegment().contains(".") && resource.isLoaded()) { - if (modelType.equalsIgnoreCase(resource.getURI().fileExtension())) { - XtextResource xtextResource = (XtextResource) resource; - // It's not sufficient to discard the derived state. - // The quick & dirts solution is to reparse the whole resource. - // We trigger this by dummy updating the resource. - logger.debug("Refreshing resource '{}'", resource.getURI().lastSegment()); - xtextResource.update(1, 0, ""); - notifyListeners(resource.getURI().lastSegment(), EventType.MODIFIED); - } + if (resource.getURI().lastSegment().contains(".") && resource.isLoaded() + && modelType.equalsIgnoreCase(resource.getURI().fileExtension()) + && !resource.getURI().lastSegment().startsWith("tmp_")) { + XtextResource xtextResource = (XtextResource) resource; + // It's not sufficient to discard the derived state. + // The quick & dirts solution is to reparse the whole resource. + // We trigger this by dummy updating the resource. + logger.debug("Refreshing resource '{}'", resource.getURI().lastSegment()); + xtextResource.update(1, 0, ""); + notifyListeners(resource.getURI().lastSegment(), EventType.MODIFIED); } } } @@ -225,13 +208,13 @@ public class ModelRepositoryImpl implements ModelRepository { // Make a copy to avoid ConcurrentModificationException List resourceListCopy = new ArrayList<>(resourceSet.getResources()); for (Resource resource : resourceListCopy) { - if (resource != null && resource.getURI().lastSegment().contains(".") && resource.isLoaded()) { - if (modelType.equalsIgnoreCase(resource.getURI().fileExtension())) { - logger.debug("Removing resource '{}'", resource.getURI().lastSegment()); - ret.add(resource.getURI().lastSegment()); - resourceSet.getResources().remove(resource); - notifyListeners(resource.getURI().lastSegment(), EventType.REMOVED); - } + if (resource.getURI().lastSegment().contains(".") && resource.isLoaded() + && modelType.equalsIgnoreCase(resource.getURI().fileExtension()) + && !resource.getURI().lastSegment().startsWith("tmp_")) { + logger.debug("Removing resource '{}'", resource.getURI().lastSegment()); + ret.add(resource.getURI().lastSegment()); + resourceSet.getResources().remove(resource); + notifyListeners(resource.getURI().lastSegment(), EventType.REMOVED); } } } diff --git a/bundles/org.openhab.core.model.core/src/main/java/org/openhab/core/model/core/internal/folder/FolderObserver.java b/bundles/org.openhab.core.model.core/src/main/java/org/openhab/core/model/core/internal/folder/FolderObserver.java index a30e83d6cb..98d7a61b79 100644 --- a/bundles/org.openhab.core.model.core/src/main/java/org/openhab/core/model/core/internal/folder/FolderObserver.java +++ b/bundles/org.openhab.core.model.core/src/main/java/org/openhab/core/model/core/internal/folder/FolderObserver.java @@ -38,6 +38,8 @@ import org.openhab.core.config.core.ConfigConstants; import org.openhab.core.model.core.ModelParser; import org.openhab.core.model.core.ModelRepository; import org.openhab.core.service.AbstractWatchService; +import org.openhab.core.service.ReadyMarker; +import org.openhab.core.service.ReadyService; import org.osgi.service.component.ComponentContext; import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; @@ -60,6 +62,11 @@ public class FolderObserver extends AbstractWatchService { /* the model repository is provided as a service */ private final ModelRepository modelRepository; + private static final String READYMARKER_TYPE = "dsl"; + + private final ReadyService readyService; + + private boolean activated; /* map that stores a list of valid file extensions for each folder */ private final Map folderFileExtMap = new ConcurrentHashMap<>(); @@ -72,18 +79,21 @@ public class FolderObserver extends AbstractWatchService { private final Map nameFileMap = new HashMap<>(); @Activate - public FolderObserver(final @Reference ModelRepository modelRepo) { + public FolderObserver(final @Reference ModelRepository modelRepo, final @Reference ReadyService readyService) { super(ConfigConstants.getConfigFolder()); this.modelRepository = modelRepo; + this.readyService = readyService; } @Reference(cardinality = ReferenceCardinality.AT_LEAST_ONE, policy = ReferencePolicy.DYNAMIC) protected void addModelParser(ModelParser modelParser) { parsers.add(modelParser.getExtension()); - // if the component isn't activated yet, ignoredFiles will be empty and thus this method does nothing - processIgnoredFiles(modelParser.getExtension()); + if (activated) { + processIgnoredFiles(modelParser.getExtension()); + readyService.markReady(new ReadyMarker(READYMARKER_TYPE, modelParser.getExtension())); + } } protected void removeModelParser(ModelParser modelParser) { @@ -118,13 +128,14 @@ public class FolderObserver extends AbstractWatchService { } addModelsToRepo(); - super.activate(); + this.activated = true; } @Override @Deactivate public void deactivate() { + this.activated = false; super.deactivate(); deleteModelsFromRepo(); this.ignoredFiles.clear(); @@ -186,6 +197,9 @@ public class FolderObserver extends AbstractWatchService { } } } + for (String ext : validExtension) { + readyService.markReady(new ReadyMarker(READYMARKER_TYPE, ext)); + } } } } diff --git a/bundles/org.openhab.core.model.item/src/org/openhab/core/model/item/internal/GenericItemProvider.java b/bundles/org.openhab.core.model.item/src/org/openhab/core/model/item/internal/GenericItemProvider.java index e0d50db8ca..5d3880d469 100644 --- a/bundles/org.openhab.core.model.item/src/org/openhab/core/model/item/internal/GenericItemProvider.java +++ b/bundles/org.openhab.core.model.item/src/org/openhab/core/model/item/internal/GenericItemProvider.java @@ -127,7 +127,7 @@ public class GenericItemProvider extends AbstractProvider * * @param factory The {@link ItemFactory} to add. */ - @Reference(cardinality = ReferenceCardinality.MULTIPLE, policy = ReferencePolicy.DYNAMIC) + @Reference(cardinality = ReferenceCardinality.AT_LEAST_ONE, policy = ReferencePolicy.DYNAMIC) public void addItemFactory(ItemFactory factory) { itemFactorys.add(factory); dispatchBindingsPerItemType(null, factory.getSupportedItemTypes()); diff --git a/itests/org.openhab.core.model.core.tests/src/main/java/org/openhab/core/model/core/internal/folder/FolderObserverTest.java b/itests/org.openhab.core.model.core.tests/src/main/java/org/openhab/core/model/core/internal/folder/FolderObserverTest.java index aa517ab5bc..22d2ae341d 100644 --- a/itests/org.openhab.core.model.core.tests/src/main/java/org/openhab/core/model/core/internal/folder/FolderObserverTest.java +++ b/itests/org.openhab.core.model.core.tests/src/main/java/org/openhab/core/model/core/internal/folder/FolderObserverTest.java @@ -44,6 +44,7 @@ import org.openhab.core.model.core.ModelParser; import org.openhab.core.model.core.ModelRepository; import org.openhab.core.model.core.ModelRepositoryChangeListener; import org.openhab.core.service.AbstractWatchService; +import org.openhab.core.service.ReadyService; import org.openhab.core.test.java.JavaOSGiTest; import org.osgi.service.component.ComponentContext; @@ -89,6 +90,9 @@ public class FolderObserverTest extends JavaOSGiTest { @Mock private ModelParser modelParser; + @Mock + private ReadyService readyService; + @Mock private ComponentContext context; private Dictionary configProps; @@ -124,7 +128,7 @@ public class FolderObserverTest extends JavaOSGiTest { modelRepo = new ModelRepoDummy(); - folderObserver = new FolderObserver(modelRepo); + folderObserver = new FolderObserver(modelRepo, readyService); folderObserver.addModelParser(modelParser); } @@ -348,7 +352,7 @@ public class FolderObserverTest extends JavaOSGiTest { throw new RuntimeException("intentional failure."); } }; - FolderObserver localFolderObserver = new FolderObserver(modelRepo); + FolderObserver localFolderObserver = new FolderObserver(modelRepo, readyService); localFolderObserver.addModelParser(modelParser); String validExtension = "java";