diff --git a/owlplug-client/pom.xml b/owlplug-client/pom.xml
index e7a07aec..994fe2c7 100644
--- a/owlplug-client/pom.xml
+++ b/owlplug-client/pom.xml
@@ -11,7 +11,7 @@
com.owlplug
owlplug
- 1.33.0
+ 1.33.1
owlplug-client
@@ -35,17 +35,17 @@
com.owlplug
owlplug-host
- 1.33.0
+ 1.33.1
com.owlplug
owlplug-controls
- 1.33.0
+ 1.33.1
com.owlplug
owlplug-parsers
- 1.33.0
+ 1.33.1
org.springframework.boot
diff --git a/owlplug-client/src/main/java/com/owlplug/core/components/ApplicationDefaults.java b/owlplug-client/src/main/java/com/owlplug/core/components/ApplicationDefaults.java
index e32bcdf5..57855792 100644
--- a/owlplug-client/src/main/java/com/owlplug/core/components/ApplicationDefaults.java
+++ b/owlplug-client/src/main/java/com/owlplug/core/components/ApplicationDefaults.java
@@ -108,6 +108,7 @@ public class ApplicationDefaults {
public static final String LV2_EXTRA_DIRECTORY_KEY = "LV2_EXTRA_DIRECTORY_KEY";
public static final String NATIVE_HOST_ENABLED_KEY = "NATIVE_HOST_ENABLED_KEY";
public static final String PREFERRED_NATIVE_LOADER = "PREFERRED_NATIVE_LOADER";
+ public static final String NATIVE_LOADER_TIMEOUT_KEY = "NATIVE_LOADER_TIMEOUT_KEY";
public static final String SELECTED_ACCOUNT_KEY = "SELECTED_ACCOUNT_KEY";
public static final String SYNC_PLUGINS_STARTUP_KEY = "SYNC_PLUGINS_STARTUP_KEY";
public static final String STORE_DIRECTORY_ENABLED_KEY = "STORE_DIRECTORY_ENABLED_KEY";
diff --git a/owlplug-client/src/main/java/com/owlplug/core/controllers/OptionsController.java b/owlplug-client/src/main/java/com/owlplug/core/controllers/OptionsController.java
index 9ec93569..f25a963b 100644
--- a/owlplug-client/src/main/java/com/owlplug/core/controllers/OptionsController.java
+++ b/owlplug-client/src/main/java/com/owlplug/core/controllers/OptionsController.java
@@ -63,6 +63,8 @@ public class OptionsController extends BaseController {
@FXML
private ComboBox pluginNativeComboBox;
@FXML
+ private TextField loaderTimeoutTextField;
+ @FXML
private CheckBox syncPluginsCheckBox;
@FXML
private CheckBox syncFileStatCheckbox;
@@ -155,6 +157,7 @@ public void initialize() {
pluginNativeCheckbox.selectedProperty().addListener((observable, oldValue, newValue) -> {
this.getPreferences().putBoolean(ApplicationDefaults.NATIVE_HOST_ENABLED_KEY, newValue);
this.pluginNativeComboBox.setDisable(!newValue);
+ updateScannerTimeoutFieldState();
});
ObservableList pluginLoaders = FXCollections.observableArrayList(
@@ -163,8 +166,27 @@ public void initialize() {
pluginNativeComboBox.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
if (newValue != null) {
- this.getPreferences().put(ApplicationDefaults.PREFERRED_NATIVE_LOADER,newValue.getId());
+ this.getPreferences().put(ApplicationDefaults.PREFERRED_NATIVE_LOADER, newValue.getId());
nativeHostService.setCurrentPluginLoader(newValue);
+ updateScannerTimeoutFieldState();
+ }
+ });
+
+ loaderTimeoutTextField.textProperty().addListener((observable, oldValue, newValue) -> {
+ if (!newValue.matches("\\d*")) {
+ loaderTimeoutTextField.setText(newValue.replaceAll("[^\\d]", ""));
+ return;
+ }
+ try {
+ long timeout = Long.parseLong(newValue);
+ if (timeout >= 0 && timeout <= 3600) {
+ this.getPreferences().putLong(ApplicationDefaults.NATIVE_LOADER_TIMEOUT_KEY, timeout);
+ nativeHostService.setScannerTimeout(timeout);
+ } else {
+ loaderTimeoutTextField.setText(oldValue);
+ }
+ } catch (NumberFormatException ignored) {
+ // Ignore in case of invalid values (empty string)
}
});
@@ -265,6 +287,13 @@ public void initialize() {
refreshView();
}
+ private void updateScannerTimeoutFieldState() {
+ NativePluginLoader selected = pluginNativeComboBox.getSelectionModel().getSelectedItem();
+ boolean isOwlPlugScanner = selected != null && "owlplug-scanner".equals(selected.getId());
+ boolean nativeEnabled = pluginNativeCheckbox.isSelected() && !pluginNativeCheckbox.isDisable();
+ loaderTimeoutTextField.setDisable(!nativeEnabled || !isOwlPlugScanner);
+ }
+
public void refreshView() {
vst2PluginPathFragment.refresh();
@@ -287,6 +316,10 @@ public void refreshView() {
NativePluginLoader pluginLoader = nativeHostService.getCurrentPluginLoader();
pluginNativeComboBox.getSelectionModel().select(pluginLoader);
+ long timeout = this.getPreferences().getLong(ApplicationDefaults.NATIVE_LOADER_TIMEOUT_KEY, 10L);
+ loaderTimeoutTextField.setText(String.valueOf(timeout));
+ updateScannerTimeoutFieldState();
+
if (!storeDirectoryCheckBox.isSelected()) {
storeDirectoryTextField.setVisible(false);
}
diff --git a/owlplug-client/src/main/java/com/owlplug/core/services/OptionsService.java b/owlplug-client/src/main/java/com/owlplug/core/services/OptionsService.java
index 0f28b0cf..494989a4 100644
--- a/owlplug-client/src/main/java/com/owlplug/core/services/OptionsService.java
+++ b/owlplug-client/src/main/java/com/owlplug/core/services/OptionsService.java
@@ -89,6 +89,9 @@ private void initialize() {
if (prefs.get(ApplicationDefaults.STORE_SUBDIRECTORY_ENABLED, null) == null) {
prefs.putBoolean(ApplicationDefaults.STORE_SUBDIRECTORY_ENABLED, Boolean.TRUE);
}
+ if (prefs.get(ApplicationDefaults.NATIVE_LOADER_TIMEOUT_KEY, null) == null) {
+ prefs.putLong(ApplicationDefaults.NATIVE_LOADER_TIMEOUT_KEY, 10L);
+ }
}
/**
diff --git a/owlplug-client/src/main/java/com/owlplug/plugin/controllers/PluginInfoController.java b/owlplug-client/src/main/java/com/owlplug/plugin/controllers/PluginInfoController.java
index 72a2d7f5..b6590b73 100644
--- a/owlplug-client/src/main/java/com/owlplug/plugin/controllers/PluginInfoController.java
+++ b/owlplug-client/src/main/java/com/owlplug/plugin/controllers/PluginInfoController.java
@@ -43,7 +43,6 @@
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;
-import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
@@ -111,6 +110,8 @@ public class PluginInfoController extends BaseController {
private ListView pluginComponentListView;
@FXML
private ToggleSwitch nativeDiscoveryToggleButton;
+ @FXML
+ private Label lastScanErrorLabel;
private final ObjectProperty pluginProperty = new SimpleObjectProperty();
private final ArrayList knownPluginImages = new ArrayList<>();
@@ -157,6 +158,8 @@ public void initialize() {
});
pluginComponentListView.setCellFactory(new PluginComponentCellFactory(this.getApplicationDefaults()));
+ lastScanErrorLabel.managedProperty().bind(lastScanErrorLabel.visibleProperty());
+ lastScanErrorLabel.setVisible(false);
nativeDiscoveryToggleButton.selectedProperty().addListener((observable, oldValue, newValue) -> {
Plugin plugin = pluginProperty.get();
@@ -175,8 +178,6 @@ public void refresh() {
return;
}
- // All reads below come from already-loaded in-memory fields — safe to run
- // on the FX thread without risk of blocking.
pluginFormatIcon.setImage(this.getApplicationDefaults().getPluginFormatIcon(plugin.getFormat()));
pluginFormatLabel.setText(plugin.getFormat().getText() + " Plugin");
pluginTitleLabel.setText(plugin.getName());
@@ -202,6 +203,11 @@ public void refresh() {
if (plugin.getFootprint() != null) {
nativeDiscoveryToggleButton.setSelected(plugin.getFootprint().isNativeDiscoveryEnabled());
+ String scanError = plugin.getFootprint().getLastScanStatus();
+ lastScanErrorLabel.setText(scanError != null ? scanError : "");
+ lastScanErrorLabel.setVisible(scanError != null);
+ } else {
+ lastScanErrorLabel.setVisible(false);
}
setPluginImage();
diff --git a/owlplug-client/src/main/java/com/owlplug/plugin/controllers/PluginTableController.java b/owlplug-client/src/main/java/com/owlplug/plugin/controllers/PluginTableController.java
index 16172886..86a71eae 100644
--- a/owlplug-client/src/main/java/com/owlplug/plugin/controllers/PluginTableController.java
+++ b/owlplug-client/src/main/java/com/owlplug/plugin/controllers/PluginTableController.java
@@ -23,6 +23,7 @@
import com.owlplug.core.utils.FileUtils;
import com.owlplug.core.utils.PlatformUtils;
import com.owlplug.plugin.controllers.dialogs.DisablePluginDialogController;
+import com.owlplug.plugin.model.IPlugin;
import com.owlplug.plugin.model.Plugin;
import com.owlplug.plugin.model.PluginFormat;
import com.owlplug.plugin.model.PluginState;
@@ -63,9 +64,9 @@ public class PluginTableController extends BaseController {
private PluginService pluginService;
private final SimpleStringProperty search = new SimpleStringProperty();
- private final TableView tableView;
+ private final TableView tableView;
- private final ObservableList pluginList;
+ private final ObservableList pluginList;
public PluginTableController() {
@@ -75,7 +76,7 @@ public PluginTableController() {
createColumns();
tableView.setRowFactory(tv -> {
- TableRow row = new TableRow<>();
+ TableRow row = new TableRow<>();
row.itemProperty().addListener((obs, oldItem, newItem) -> {
if (newItem != null) {
row.setContextMenu(createPluginContextMenu(newItem));
@@ -88,7 +89,7 @@ public PluginTableController() {
pluginList = FXCollections.observableArrayList();
// Wraps an ObservableList and filters its content using the provided Predicate.
// All changes in the ObservableList are propagated immediately to the FilteredList.
- FilteredList filteredPluginList = new FilteredList<>(pluginList);
+ FilteredList filteredPluginList = new FilteredList<>(pluginList);
filteredPluginList.predicateProperty().bind(Bindings.createObjectBinding(() -> {
if (search.getValue() == null || search.getValue().isEmpty()) {
@@ -99,17 +100,17 @@ public PluginTableController() {
search.getValue().toLowerCase()));
}, search));
- SortedList sortedPluginList = new SortedList<>(filteredPluginList);
+ SortedList sortedPluginList = new SortedList<>(filteredPluginList);
tableView.setItems(sortedPluginList);
sortedPluginList.comparatorProperty().bind(tableView.comparatorProperty());
}
private void createColumns() {
- TableColumn nameColumn = new TableColumn<>("Name");
+ TableColumn nameColumn = new TableColumn<>("Name");
nameColumn.setCellValueFactory(cellData -> new SimpleStringProperty(cellData.getValue().getName()));
- TableColumn formatColumn = new TableColumn<>("Format");
- formatColumn.setCellValueFactory(cellData -> new SimpleObjectProperty<>(cellData.getValue().getFormat()));
+ TableColumn formatColumn = new TableColumn<>("Format");
+ formatColumn.setCellValueFactory(cellData -> new SimpleObjectProperty<>(cellData.getValue().asPlugin().getFormat()));
formatColumn.setCellFactory(e -> new TableCell<>() {
@Override
public void updateItem(PluginFormat item, boolean empty) {
@@ -123,17 +124,17 @@ public void updateItem(PluginFormat item, boolean empty) {
}
}
});
- TableColumn manufacturerColumn = new TableColumn<>("Manufacturer");
+ TableColumn manufacturerColumn = new TableColumn<>("Manufacturer");
manufacturerColumn.setCellValueFactory(cellData ->
new SimpleStringProperty(cellData.getValue().getManufacturerName()));
- TableColumn versionColumn = new TableColumn<>("Version");
+ TableColumn versionColumn = new TableColumn<>("Version");
versionColumn.setCellValueFactory(cellData -> new SimpleStringProperty(cellData.getValue().getVersion()));
- TableColumn categoryColumn = new TableColumn<>("Category");
+ TableColumn categoryColumn = new TableColumn<>("Category");
categoryColumn.setCellValueFactory(cellData -> new SimpleStringProperty(cellData.getValue().getCategory()));
// Directory Column
- TableColumn directoryColumn = new TableColumn<>("Directory");
+ TableColumn directoryColumn = new TableColumn<>("Directory");
directoryColumn.setCellValueFactory(cellData -> new SimpleStringProperty(
- FileUtils.getParentDirectoryName(cellData.getValue().getPath())));
+ FileUtils.getParentDirectoryName(cellData.getValue().asPlugin().getPath())));
directoryColumn.setCellFactory(e -> new TableCell<>() {
@Override
public void updateItem(String item, boolean empty) {
@@ -148,9 +149,9 @@ public void updateItem(String item, boolean empty) {
}
});
// Scan Directory Column
- TableColumn scanDirectoryColumn = new TableColumn<>("Scan Dir.");
+ TableColumn scanDirectoryColumn = new TableColumn<>("Scan Dir.");
scanDirectoryColumn.setCellValueFactory(cellData -> new SimpleStringProperty(
- FileUtils.getFilename(cellData.getValue().getScanDirectoryPath())));
+ FileUtils.getFilename(cellData.getValue().asPlugin().getScanDirectoryPath())));
scanDirectoryColumn.setCellFactory(e -> new TableCell<>() {
@Override
public void updateItem(String item, boolean empty) {
@@ -165,9 +166,9 @@ public void updateItem(String item, boolean empty) {
}
});
// Plugin State Column
- TableColumn stateColumn = new TableColumn<>("State");
+ TableColumn stateColumn = new TableColumn<>("State");
stateColumn.setCellValueFactory(cellData -> new SimpleObjectProperty<>(
- pluginService.getPluginState(cellData.getValue())));
+ pluginService.getPluginState(cellData.getValue().asPlugin())));
stateColumn.setCellFactory(e -> new TableCell<>() {
@Override
public void updateItem(PluginState item, boolean empty) {
@@ -188,10 +189,16 @@ public void updateItem(PluginState item, boolean empty) {
public void setPlugins(Iterable plugins) {
pluginList.clear();
- plugins.forEach(pluginList::add);
+ plugins.forEach(p -> {
+ pluginList.add(p);
+ if (p.getComponents().size() > 1) {
+ pluginList.addAll(p.getComponents());
+ }
+ });
+
}
- public TableView getTableView() {
+ public TableView getTableView() {
return tableView;
}
@@ -201,7 +208,8 @@ public void setNodeManaged(boolean isManaged) {
}
public void selectPluginById(long id) {
- for (Plugin plugin : pluginList) {
+ for (IPlugin p : pluginList) {
+ Plugin plugin = p.asPlugin(); // Get plugin or component parent
if (plugin.getId().equals(id)) {
tableView.getSelectionModel().select(plugin);
break;
@@ -217,37 +225,39 @@ public void refresh() {
tableView.refresh();
}
- private ContextMenu createPluginContextMenu(Plugin plugin) {
+ private ContextMenu createPluginContextMenu(IPlugin plugin) {
ContextMenu menu = new ContextMenu();
MenuItem openDirItem = new MenuItem("Reveal in File Explorer");
openDirItem.setOnAction(e -> {
- File pluginFile = new File(plugin.getPath());
+ File pluginFile = new File(plugin.asPlugin().getPath());
PlatformUtils.openFromDesktop(pluginFile.getParentFile());
});
menu.getItems().addAll(openDirItem, new SeparatorMenuItem());
- if (plugin.isDisabled()) {
- MenuItem enableItem = new MenuItem("Enable plugin");
- enableItem.setOnAction(e -> {
- Async.run(() -> pluginService.enablePlugin(plugin));
- });
- menu.getItems().add(enableItem);
- } else {
- MenuItem disableItem = new MenuItem("Disable plugin");
- disableItem.setOnAction(e -> {
- if (this.getPreferences().getBoolean(ApplicationDefaults.SHOW_DIALOG_DISABLE_PLUGIN_KEY, true)) {
- this.disableController.setPlugin(plugin);
- this.disableController.show();
- } else {
- this.disableController.disablePluginWithoutPrompt(plugin);
- }
- });
- menu.getItems().add(disableItem);
- }
+ if (plugin instanceof Plugin p) {
+ if (p.isDisabled()) {
+ MenuItem enableItem = new MenuItem("Enable plugin");
+ enableItem.setOnAction(e -> {
+ Async.run(() -> pluginService.enablePlugin(p));
+ });
+ menu.getItems().add(enableItem);
+ } else {
+ MenuItem disableItem = new MenuItem("Disable plugin");
+ disableItem.setOnAction(e -> {
+ if (this.getPreferences().getBoolean(ApplicationDefaults.SHOW_DIALOG_DISABLE_PLUGIN_KEY, true)) {
+ this.disableController.setPlugin(p);
+ this.disableController.show();
+ } else {
+ this.disableController.disablePluginWithoutPrompt(p);
+ }
+ });
+ menu.getItems().add(disableItem);
+ }
- menu.getItems().add(new SeparatorMenuItem());
+ menu.getItems().add(new SeparatorMenuItem());
+ }
MenuItem infoDisplayItem = new MenuItem("Toggle info display");
menu.getItems().add(infoDisplayItem);
diff --git a/owlplug-client/src/main/java/com/owlplug/plugin/controllers/PluginTreeViewController.java b/owlplug-client/src/main/java/com/owlplug/plugin/controllers/PluginTreeViewController.java
index 4d091732..9890f4a1 100644
--- a/owlplug-client/src/main/java/com/owlplug/plugin/controllers/PluginTreeViewController.java
+++ b/owlplug-client/src/main/java/com/owlplug/plugin/controllers/PluginTreeViewController.java
@@ -21,6 +21,7 @@
import com.owlplug.core.controllers.BaseController;
import com.owlplug.core.ui.FilterableTreeItem;
import com.owlplug.plugin.model.IDirectory;
+import com.owlplug.plugin.model.IPlugin;
import com.owlplug.plugin.model.Plugin;
import com.owlplug.plugin.model.PluginComponent;
import com.owlplug.plugin.model.PluginDirectory;
@@ -73,7 +74,7 @@ public PluginTreeViewController() {
return null;
}
return (item) -> {
- if (item instanceof Plugin plugin) {
+ if (item instanceof IPlugin plugin) {
return plugin.getName().toLowerCase().contains(search.getValue().toLowerCase())
|| (plugin.getCategory() != null && plugin.getCategory().toLowerCase().contains(
search.getValue().toLowerCase()));
@@ -89,7 +90,7 @@ public PluginTreeViewController() {
return null;
}
return (item) -> {
- if (item instanceof Plugin plugin) {
+ if (item instanceof IPlugin plugin) {
return plugin.getName().toLowerCase().contains(search.getValue().toLowerCase())
|| (plugin.getCategory() != null && plugin.getCategory().toLowerCase().contains(search.getValue().toLowerCase()));
} else {
diff --git a/owlplug-client/src/main/java/com/owlplug/plugin/model/IPlugin.java b/owlplug-client/src/main/java/com/owlplug/plugin/model/IPlugin.java
new file mode 100644
index 00000000..22a58ff1
--- /dev/null
+++ b/owlplug-client/src/main/java/com/owlplug/plugin/model/IPlugin.java
@@ -0,0 +1,57 @@
+/* OwlPlug
+ * Copyright (C) 2021 Arthur
+ *
+ * This file is part of OwlPlug.
+ *
+ * OwlPlug is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3
+ * as published by the Free Software Foundation.
+ *
+ * OwlPlug is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with OwlPlug. If not, see .
+ */
+
+
+package com.owlplug.plugin.model;
+
+public interface IPlugin {
+
+ /**
+ * Retrieve the associated Plugin object from this Plugin object.
+ * In case of Component, it returns the parent plugin.
+ * In case of Plugin, it should return itself.
+ * @return plugin
+ */
+ Plugin asPlugin();
+
+ /**
+ * Get object identifier.
+ * The identifier is not guaranteed to be unique across Plugin and Component.
+ * @return id
+ */
+ Long getId();
+
+ String getName();
+
+ String getDescriptiveName();
+
+ String getVersion();
+
+ String getUid();
+
+ String getCategory();
+
+ String getManufacturerName();
+
+ String getIdentifier();
+
+ String getBundleId();
+
+ PluginType getType();
+
+}
diff --git a/owlplug-client/src/main/java/com/owlplug/plugin/model/Plugin.java b/owlplug-client/src/main/java/com/owlplug/plugin/model/Plugin.java
index f3bba4db..c320dd02 100644
--- a/owlplug-client/src/main/java/com/owlplug/plugin/model/Plugin.java
+++ b/owlplug-client/src/main/java/com/owlplug/plugin/model/Plugin.java
@@ -42,7 +42,7 @@
@Inheritance
@Table(indexes = { @Index(name = "IDX_PLUGIN_ID", columnList = "id"),
@Index(name = "IDX_PLUGIN_NAME", columnList = "name") })
-public class Plugin {
+public class Plugin implements IPlugin {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@@ -238,4 +238,9 @@ public Set getComponents() {
public void setComponents(Set components) {
this.components = components;
}
+
+ @Override
+ public Plugin asPlugin() {
+ return this;
+ }
}
diff --git a/owlplug-client/src/main/java/com/owlplug/plugin/model/PluginComponent.java b/owlplug-client/src/main/java/com/owlplug/plugin/model/PluginComponent.java
index dfdaa2cb..d41ada15 100644
--- a/owlplug-client/src/main/java/com/owlplug/plugin/model/PluginComponent.java
+++ b/owlplug-client/src/main/java/com/owlplug/plugin/model/PluginComponent.java
@@ -19,6 +19,7 @@
package com.owlplug.plugin.model;
import com.fasterxml.jackson.annotation.JsonIgnore;
+import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
@@ -31,7 +32,7 @@
@Entity
@Table(indexes = { @Index(name = "IDX_PLUGIN_COMPONENT_ID", columnList = "id") })
-public class PluginComponent {
+public class PluginComponent implements IPlugin {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@@ -48,6 +49,7 @@ public class PluginComponent {
@Enumerated(EnumType.STRING)
protected PluginType type;
+
@ManyToOne
@JsonIgnore
private Plugin plugin;
@@ -140,4 +142,8 @@ public void setPlugin(Plugin plugin) {
this.plugin = plugin;
}
+ @Override
+ public Plugin asPlugin() {
+ return this.plugin;
+ }
}
diff --git a/owlplug-client/src/main/java/com/owlplug/plugin/model/PluginFootprint.java b/owlplug-client/src/main/java/com/owlplug/plugin/model/PluginFootprint.java
index e33124b7..bf116a09 100644
--- a/owlplug-client/src/main/java/com/owlplug/plugin/model/PluginFootprint.java
+++ b/owlplug-client/src/main/java/com/owlplug/plugin/model/PluginFootprint.java
@@ -39,6 +39,8 @@ public class PluginFootprint {
protected boolean nativeDiscoveryEnabled = true;
protected String screenshotUrl;
+
+ private String lastScanStatus;
public PluginFootprint() {
}
@@ -75,4 +77,12 @@ public Long getId() {
return id;
}
+ public String getLastScanStatus() {
+ return lastScanStatus;
+ }
+
+ public void setLastScanStatus(String lastScanStatus) {
+ this.lastScanStatus = lastScanStatus;
+ }
+
}
diff --git a/owlplug-client/src/main/java/com/owlplug/plugin/services/NativeHostService.java b/owlplug-client/src/main/java/com/owlplug/plugin/services/NativeHostService.java
index e746e5f3..ebd41fb0 100644
--- a/owlplug-client/src/main/java/com/owlplug/plugin/services/NativeHostService.java
+++ b/owlplug-client/src/main/java/com/owlplug/plugin/services/NativeHostService.java
@@ -23,6 +23,7 @@
import com.owlplug.host.NativePlugin;
import com.owlplug.host.loaders.DummyPluginLoader;
import com.owlplug.host.loaders.EmbeddedScannerPluginLoader;
+import com.owlplug.host.loaders.NativeLoaderException;
import com.owlplug.host.loaders.NativePluginLoader;
import com.owlplug.host.loaders.jni.JNINativePluginLoader;
import jakarta.annotation.PostConstruct;
@@ -52,6 +53,8 @@ private void init() {
loader.init();
}
configureCurrentPluginLoader();
+ long timeoutSeconds = this.getPreferences().getLong(ApplicationDefaults.NATIVE_LOADER_TIMEOUT_KEY, 30L);
+ EmbeddedScannerPluginLoader.getInstance().setTimeout(timeoutSeconds * 1000);
}
private void configureCurrentPluginLoader() {
@@ -110,7 +113,11 @@ public void setCurrentPluginLoader(NativePluginLoader pluginLoader) {
this.currentPluginLoader = pluginLoader;
}
- public List loadPlugin(String path) {
+ public void setScannerTimeout(long timeoutSeconds) {
+ EmbeddedScannerPluginLoader.getInstance().setTimeout(timeoutSeconds * 1000);
+ }
+
+ public List loadPlugin(String path) throws NativeLoaderException {
if (currentPluginLoader != null) {
return currentPluginLoader.loadPlugin(path);
} else {
diff --git a/owlplug-client/src/main/java/com/owlplug/plugin/tasks/PluginScanTask.java b/owlplug-client/src/main/java/com/owlplug/plugin/tasks/PluginScanTask.java
index 1a5f96b4..d665515a 100644
--- a/owlplug-client/src/main/java/com/owlplug/plugin/tasks/PluginScanTask.java
+++ b/owlplug-client/src/main/java/com/owlplug/plugin/tasks/PluginScanTask.java
@@ -21,7 +21,9 @@
import com.owlplug.core.tasks.AbstractTask;
import com.owlplug.core.tasks.TaskException;
import com.owlplug.core.tasks.TaskResult;
+import com.owlplug.core.utils.StringUtils;
import com.owlplug.host.NativePlugin;
+import com.owlplug.host.loaders.NativeLoaderException;
import com.owlplug.plugin.model.Plugin;
import com.owlplug.plugin.model.PluginComponent;
import com.owlplug.plugin.model.PluginFootprint;
@@ -172,27 +174,35 @@ protected void collect() throws Exception {
log.debug("Load plugin using native discovery: " + plugin.getPath());
this.updateMessage("Exploring plugin " + plugin.getName());
- List nativePlugins = nativeHostService.loadPlugin(plugin.getPath());
+ try {
+ List nativePlugins = nativeHostService.loadPlugin(plugin.getPath());
+ pluginFootprint.setLastScanStatus(null);
- if (nativePlugins != null && !nativePlugins.isEmpty()) {
- log.debug("Found {} components (nativePlugin) for plugin {}", nativePlugins.size(), plugin.getName());
+ if (!nativePlugins.isEmpty()) {
+ log.debug("Found {} components (nativePlugin) for plugin {}", nativePlugins.size(), plugin.getName());
- plugin.setNativeCompatible(true);
+ plugin.setNativeCompatible(true);
- for (NativePlugin nativePlugin : nativePlugins) {
- PluginComponent component = createComponentFromNative(nativePlugin);
- component.setPlugin(plugin);
- plugin.getComponents().add(component);
- log.debug("Created component {} for plugin {}", component.getName(), plugin.getName());
- }
-
- // Hardcode plugin properties from the first component (nativePlugin) retrieved.
- mapPluginPropertiesFromNative(plugin, nativePlugins.get(0));
+ for (NativePlugin nativePlugin : nativePlugins) {
+ PluginComponent component = createComponentFromNative(nativePlugin);
+ component.setPlugin(plugin);
+ plugin.getComponents().add(component);
+ log.debug("Created component {} for plugin {}", component.getName(), plugin.getName());
+ }
+ // Hardcode plugin properties from the first component (nativePlugin) retrieved.
+ mapPluginPropertiesFromNative(plugin, nativePlugins.get(0));
+ }
+ } catch (NativeLoaderException e) {
+ log.error("Native scan failed for plugin {}: {}", plugin.getName(), e.getMessage());
+ pluginFootprint.setLastScanStatus(StringUtils.truncate(
+ e.getMessage(), 240, "..."
+ ));
}
}
plugin.setScanComplete(true);
+ pluginFootprintRepository.save(pluginFootprint);
pluginRepository.save(plugin);
this.commitProgress(80.0 / pluginFiles.size());
diff --git a/owlplug-client/src/main/resources/fxml/OptionsView.fxml b/owlplug-client/src/main/resources/fxml/OptionsView.fxml
index ec61670c..38ebbd37 100644
--- a/owlplug-client/src/main/resources/fxml/OptionsView.fxml
+++ b/owlplug-client/src/main/resources/fxml/OptionsView.fxml
@@ -29,13 +29,14 @@
-
-
-
+
+
+
+
diff --git a/owlplug-client/src/main/resources/fxml/plugins/PluginInfoView.fxml b/owlplug-client/src/main/resources/fxml/plugins/PluginInfoView.fxml
index 174fe092..d4eb5c28 100644
--- a/owlplug-client/src/main/resources/fxml/plugins/PluginInfoView.fxml
+++ b/owlplug-client/src/main/resources/fxml/plugins/PluginInfoView.fxml
@@ -137,5 +137,12 @@
+
+
+
diff --git a/owlplug-controls/pom.xml b/owlplug-controls/pom.xml
index 88454622..88387828 100644
--- a/owlplug-controls/pom.xml
+++ b/owlplug-controls/pom.xml
@@ -5,7 +5,7 @@
owlplug
com.owlplug
- 1.33.0
+ 1.33.1
4.0.0
diff --git a/owlplug-host/pom.xml b/owlplug-host/pom.xml
index f3448be4..45394ed2 100644
--- a/owlplug-host/pom.xml
+++ b/owlplug-host/pom.xml
@@ -7,7 +7,7 @@
com.owlplug
owlplug
- 1.33.0
+ 1.33.1
owlplug-host
owlplug-host
diff --git a/owlplug-host/src/main/java/com/owlplug/host/io/CommandRunner.java b/owlplug-host/src/main/java/com/owlplug/host/io/CommandRunner.java
index 573f6bbd..59b621f0 100644
--- a/owlplug-host/src/main/java/com/owlplug/host/io/CommandRunner.java
+++ b/owlplug-host/src/main/java/com/owlplug/host/io/CommandRunner.java
@@ -57,12 +57,10 @@ public CommandResult run(String... command) throws IOException {
Process process = pb.start();
ExecutorService executor = Executors.newFixedThreadPool(1);
+ Future stdoutFuture = executor.submit(new StreamReader(process.getInputStream()));
- Callable stdoutReader = new StreamReader(process.getInputStream());
- Future stdoutFuture = executor.submit(stdoutReader);
-
- boolean finished;
try {
+ boolean finished;
if (timeoutActivated) {
finished = process.waitFor(timeout, TimeUnit.MILLISECONDS);
} else {
@@ -70,27 +68,21 @@ public CommandResult run(String... command) throws IOException {
finished = true;
}
- } catch (InterruptedException e) {
- log.error("Interrupted while waiting for process");
- process.destroy();
- throw new IOException("Interrupted while waiting for process", e);
- }
-
- if (!finished) {
- log.error("Forcibly destroying process after timeout {}ms exceeded.", timeout);
- process.destroyForcibly();
- throw new IOException("Process timeout exceeded: " + timeout + "ms");
- }
+ if (!finished) {
+ log.error("Forcibly destroying process after timeout {}ms exceeded.", timeout);
+ throw new IOException("Process timeout exceeded: " + timeout + "ms");
+ }
- try {
- // Let 1 seconds for gracefully read and complete process
String stdout = stdoutFuture.get(1, TimeUnit.SECONDS);
return new CommandResult(process.exitValue(), stdout);
} catch (InterruptedException e) {
- throw new IOException("Interrupted while reading process output", e);
+ throw new IOException("Process execution interrupted", e);
} catch (ExecutionException | TimeoutException e) {
throw new IOException("Failed to read process output", e);
} finally {
+ // destroyForcibly closes the process streams, which unblocks any readLine()
+ // call in the StreamReader thread — safe to call even if the process already exited
+ process.destroyForcibly();
executor.shutdownNow();
}
}
diff --git a/owlplug-host/src/main/java/com/owlplug/host/loaders/DummyPluginLoader.java b/owlplug-host/src/main/java/com/owlplug/host/loaders/DummyPluginLoader.java
index e75bf0b2..e17fc23b 100644
--- a/owlplug-host/src/main/java/com/owlplug/host/loaders/DummyPluginLoader.java
+++ b/owlplug-host/src/main/java/com/owlplug/host/loaders/DummyPluginLoader.java
@@ -47,8 +47,8 @@ public void open() {
}
@Override
- public List loadPlugin(String path) {
- return null;
+ public List loadPlugin(String path) throws NativeLoaderException {
+ return List.of();
}
@Override
diff --git a/owlplug-host/src/main/java/com/owlplug/host/loaders/EmbeddedScannerPluginLoader.java b/owlplug-host/src/main/java/com/owlplug/host/loaders/EmbeddedScannerPluginLoader.java
index 2d3fb555..bf31eec8 100644
--- a/owlplug-host/src/main/java/com/owlplug/host/loaders/EmbeddedScannerPluginLoader.java
+++ b/owlplug-host/src/main/java/com/owlplug/host/loaders/EmbeddedScannerPluginLoader.java
@@ -58,9 +58,11 @@ public class EmbeddedScannerPluginLoader implements NativePluginLoader {
private static final String DEFAULT_SCANNER_ID =
DEFAULT_SCANNER_NAME + "-" + DEFAULT_SCANNER_VERSION + "-" + DEFAULT_SCANNER_PLATFORM_TAG + DEFAULT_SCANNER_EXT;
+ private final String scannerDirectory;
+ private final String scannerId;
private boolean available = false;
- private String scannerDirectory;
- private String scannerId;
+ // 10s default timeout for scanner execution
+ private long timeoutMillis = 10000;
public static EmbeddedScannerPluginLoader getInstance() {
if (INSTANCE == null) {
@@ -112,7 +114,7 @@ public void open() {
}
@Override
- public List loadPlugin(String path) {
+ public List loadPlugin(String path) throws NativeLoaderException {
log.debug("Load plugin {}", path);
@@ -123,27 +125,22 @@ public List loadPlugin(String path) {
try {
CommandRunner commandRunner = new CommandRunner();
commandRunner.setTimeoutActivated(true);
- commandRunner.setTimeout(10000); // 10 seconds timeout
- CommandResult result = commandRunner.run(scannerDirectory + SEPARATOR + scannerId, path);
+ commandRunner.setTimeout(timeoutMillis);
+ CommandResult result = commandRunner.run(scannerDirectory + SEPARATOR + scannerId, path);
log.debug("Response received from scanner");
log.debug(result.getOutput());
- if (result.getExitValue() >= 0) {
-
+ if (result.getExitValue() == 0) {
log.debug("Extracting XML from content received by the scanner");
- String output = result.getOutput();
-
- return createPluginsFromCommandOutput(output);
-
+ return createPluginsFromCommandOutput(result.getOutput());
} else {
- log.debug("Invalid return code {} received from plugin scanner", result.getExitValue());
+ throw new NativeLoaderException("Scanner exited with code " + result.getExitValue());
}
} catch (IOException e) {
log.error("Error executing plugin scanner {}", path, e);
+ throw new NativeLoaderException("Scanner execution failed: " + e.getMessage(), e);
}
-
- return null;
}
private List createPluginsFromCommandOutput(String output) {
@@ -223,6 +220,10 @@ public String getId() {
return "owlplug-scanner";
}
+ public void setTimeout(long timeoutMillis) {
+ this.timeoutMillis = timeoutMillis;
+ }
+
@Override
public String toString() {
return this.getName();
diff --git a/owlplug-host/src/main/java/com/owlplug/host/loaders/NativeLoaderException.java b/owlplug-host/src/main/java/com/owlplug/host/loaders/NativeLoaderException.java
new file mode 100644
index 00000000..a3e96636
--- /dev/null
+++ b/owlplug-host/src/main/java/com/owlplug/host/loaders/NativeLoaderException.java
@@ -0,0 +1,31 @@
+/* OwlPlug
+ * Copyright (C) 2021 Arthur
+ *
+ * This file is part of OwlPlug.
+ *
+ * OwlPlug is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3
+ * as published by the Free Software Foundation.
+ *
+ * OwlPlug is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with OwlPlug. If not, see .
+ */
+
+package com.owlplug.host.loaders;
+
+public class NativeLoaderException extends Exception {
+
+ public NativeLoaderException(String message) {
+ super(message);
+ }
+
+ public NativeLoaderException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+}
\ No newline at end of file
diff --git a/owlplug-host/src/main/java/com/owlplug/host/loaders/NativePluginLoader.java b/owlplug-host/src/main/java/com/owlplug/host/loaders/NativePluginLoader.java
index 3b37ff73..54bcaa0e 100644
--- a/owlplug-host/src/main/java/com/owlplug/host/loaders/NativePluginLoader.java
+++ b/owlplug-host/src/main/java/com/owlplug/host/loaders/NativePluginLoader.java
@@ -27,7 +27,7 @@ public interface NativePluginLoader {
public void open();
- public List loadPlugin(String path);
+ public List loadPlugin(String path) throws NativeLoaderException;
public void close();
diff --git a/owlplug-host/src/main/java/com/owlplug/host/loaders/jni/JNINativePluginLoader.java b/owlplug-host/src/main/java/com/owlplug/host/loaders/jni/JNINativePluginLoader.java
index 6ab320a8..fe3934ed 100644
--- a/owlplug-host/src/main/java/com/owlplug/host/loaders/jni/JNINativePluginLoader.java
+++ b/owlplug-host/src/main/java/com/owlplug/host/loaders/jni/JNINativePluginLoader.java
@@ -19,6 +19,7 @@
package com.owlplug.host.loaders.jni;
import com.owlplug.host.NativePlugin;
+import com.owlplug.host.loaders.NativeLoaderException;
import com.owlplug.host.loaders.NativePluginLoader;
import java.util.List;
@@ -50,8 +51,12 @@ public void open() {
}
@Override
- public List loadPlugin(String path) {
- return nativePluginMapper.mapPlugin(path);
+ public List loadPlugin(String path) throws NativeLoaderException {
+ List plugins = nativePluginMapper.mapPlugin(path);
+ if (plugins == null) {
+ throw new NativeLoaderException("Scan rejected this plugin, probably incompatible with current platform.");
+ }
+ return plugins;
}
@Override
diff --git a/owlplug-parsers/pom.xml b/owlplug-parsers/pom.xml
index 4cb0cd39..552605a8 100644
--- a/owlplug-parsers/pom.xml
+++ b/owlplug-parsers/pom.xml
@@ -5,7 +5,7 @@
owlplug
com.owlplug
- 1.33.0
+ 1.33.1
4.0.0
diff --git a/pom.xml b/pom.xml
index be54529e..6cfb1a93 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
com.owlplug
owlplug
- 1.33.0
+ 1.33.1
pom