diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/DocumentWindowController.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/DocumentWindowController.java index d0a50877b..a38fa0f1c 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/DocumentWindowController.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/DocumentWindowController.java @@ -1361,12 +1361,12 @@ public void onManageJarFxml(ActionEvent event) { libraryDialogController.openWindow(); } - public void onImportJarFxml(Window owner) { - libraryPanelController.performImportJarFxml(owner); + public boolean onImportJarFxml(Window owner) { + return libraryPanelController.performImportJarFxml(owner); } - public void onImportFromFolder(Window owner) { - libraryPanelController.performImportFromFolder(owner); + public boolean onImportFromFolder(Window owner) { + return libraryPanelController.performImportFromFolder(owner); } @FXML @@ -1411,6 +1411,15 @@ void onLibraryImportSelection(ActionEvent event) { } } + @FXML + void onLibraryRefresh(ActionEvent event) { + + UserLibrary userLibrary = SceneBuilderApp.getSingleton().getUserLibrary(); + + userLibrary.stopExplorer(); + userLibrary.startExplorer(); + } + @FXML void onLibraryRevealCustomFolder(ActionEvent event) { String userLibraryPath = ((UserLibrary) getEditorController().getLibrary()).getPath(); diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/SceneBuilderApp.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/SceneBuilderApp.java index e522c8276..962c318d0 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/SceneBuilderApp.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/SceneBuilderApp.java @@ -426,7 +426,7 @@ public void handleLaunch(List files) { userLibrary.explorationCountProperty().addListener((ChangeListener) (ov, t, t1) -> userLibraryExplorationCountDidChange()); - userLibrary.startWatching(); + userLibrary.startExplorer(); sendTrackingStartupInfo(); diff --git a/app/src/main/resources/com/oracle/javafx/scenebuilder/app/DocumentWindow.fxml b/app/src/main/resources/com/oracle/javafx/scenebuilder/app/DocumentWindow.fxml index 51e956850..4632f166e 100644 --- a/app/src/main/resources/com/oracle/javafx/scenebuilder/app/DocumentWindow.fxml +++ b/app/src/main/resources/com/oracle/javafx/scenebuilder/app/DocumentWindow.fxml @@ -74,6 +74,7 @@ + diff --git a/app/src/main/resources/com/oracle/javafx/scenebuilder/app/i18n/SceneBuilderApp.properties b/app/src/main/resources/com/oracle/javafx/scenebuilder/app/i18n/SceneBuilderApp.properties index 695a7ef20..597619ec3 100644 --- a/app/src/main/resources/com/oracle/javafx/scenebuilder/app/i18n/SceneBuilderApp.properties +++ b/app/src/main/resources/com/oracle/javafx/scenebuilder/app/i18n/SceneBuilderApp.properties @@ -266,6 +266,7 @@ library = Library library.exploring = Exploring Library.. library.panel.menu.manage.jar.fxml = JAR/FXML Manager library.panel.menu.import.selection = Import Selection +library.panel.menu.refresh = Refresh Library # Messages below are temporarily unused #library.panel.menu.category.view = View Library Category #library.panel.menu.category.create = Create Library Category diff --git a/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/library/LibraryPanelController.java b/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/library/LibraryPanelController.java index b28eeaa89..6cacaa0c5 100644 --- a/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/library/LibraryPanelController.java +++ b/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/library/LibraryPanelController.java @@ -168,21 +168,22 @@ public void setSearchPattern(String searchPattern) { /** * @param owner + * @return * @treatAsPrivate Perform the import jar action. */ - public void performImportJarFxml(Window owner) { + public boolean performImportJarFxml(Window owner) { // Open file chooser and get user selection final List importedFiles = performSelectJarOrFxmlFile(owner); - processImportJarFxml(importedFiles); + return processImportJarFxml(importedFiles); } /** * @param owner * @treatAsPrivate Perform the import jar action. */ - public void performImportFromFolder(Window owner) { + public boolean performImportFromFolder(Window owner) { File folder = performSelectFolder(owner); - processImportFolder(folder); + return processImportFolder(folder); } /** @@ -679,8 +680,6 @@ private void processInternalImport(List objects) { if (hasDependencies) { userLibraryUpdateRejected(); } else { - ((UserLibrary) getEditorController().getLibrary()).stopWatching(); - try { // The selection can be multiple, in which case each asset is // processed separately. @@ -705,8 +704,6 @@ private void processInternalImport(List objects) { if (currentDisplayMode.equals(DISPLAY_MODE.SECTIONS)) { sectionNameToKeepOpened = UserLibrary.TAG_USER_DEFINED; } - - ((UserLibrary) getEditorController().getLibrary()).startWatching(); } } } @@ -744,7 +741,7 @@ private File getUniqueFxmlFileName(String prefix, String libDir) { return file; } - private void processImportJarFxml(List importedFiles) { + private boolean processImportJarFxml(List importedFiles) { if (importedFiles != null && !importedFiles.isEmpty()) { sectionNameToKeepOpened = getExpandedSectionName(); Path libPath = Paths.get(((UserLibrary)getEditorController().getLibrary()).getPath()); @@ -782,12 +779,16 @@ private void processImportJarFxml(List importedFiles) { if (userChoice.equals(ButtonID.OK) && currentDisplayMode.equals(DISPLAY_MODE.SECTIONS)) { sectionNameToKeepOpened = UserLibrary.TAG_USER_DEFINED; } + + return true; } } } + + return false; } - private void processImportFolder(File folder) { + private boolean processImportFolder(File folder) { if (folder != null && folder.exists() && folder.isDirectory()) { Path libPath = Paths.get(((UserLibrary)getEditorController().getLibrary()).getPath()); if (createUserLibraryDir(libPath)) { @@ -809,8 +810,12 @@ private void processImportFolder(File folder) { if (userChoice.equals(ButtonID.OK) && currentDisplayMode.equals(DISPLAY_MODE.SECTIONS)) { sectionNameToKeepOpened = UserLibrary.TAG_USER_DEFINED; } + + return true; } } + + return false; } private List getSubsetOfFiles(String pattern, List files) { @@ -874,10 +879,6 @@ void copyFilesToUserLibraryDir(List files) { Path tempTargetPath = null; setUserLibraryPathString(); - // Here we deactivate the UserLib so that it unlocks the files contained - // in the lib dir in the file system meaning (especially on Windows). - ((UserLibrary) getEditorController().getLibrary()).stopWatching(); - try { for (File file : files) { savedFileName = file.getName(); @@ -901,8 +902,6 @@ void copyFilesToUserLibraryDir(List files) { } } - ((UserLibrary) getEditorController().getLibrary()).startWatching(); - if (errorCount > 0) { final ErrorDialog errorDialog = new ErrorDialog(null); errorDialog.setTitle(I18N.getString("error.copy.title")); diff --git a/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/library/manager/LibraryDialogController.java b/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/library/manager/LibraryDialogController.java index ef297ec48..4597900a7 100644 --- a/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/library/manager/LibraryDialogController.java +++ b/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/library/manager/LibraryDialogController.java @@ -61,9 +61,11 @@ import com.oracle.javafx.scenebuilder.kit.preferences.MavenPreferences; import com.oracle.javafx.scenebuilder.kit.preferences.PreferencesControllerBase; import com.oracle.javafx.scenebuilder.kit.preferences.PreferencesRecordArtifact; +import com.oracle.javafx.scenebuilder.kit.util.ReturningRunnable; import javafx.beans.InvalidationListener; import javafx.beans.Observable; +import javafx.beans.property.SimpleBooleanProperty; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.collections.transformation.SortedList; @@ -91,12 +93,13 @@ public class LibraryDialogController extends AbstractFxmlWindowController { private ObservableList listItems; - private Runnable onAddJar; - private Runnable onAddFolder; + private ReturningRunnable onAddJar; + private ReturningRunnable onAddFolder; private Consumer onEditFXML; private String userM2Repository; private String tempM2Repository; + private SimpleBooleanProperty changedProperty; private final PreferencesControllerBase preferencesControllerBase; @@ -109,6 +112,7 @@ public LibraryDialogController(EditorController editorController, String userM2R this.userM2Repository = userM2Repository; this.tempM2Repository = tempM2Repository; this.preferencesControllerBase = preferencesController; + this.changedProperty = new SimpleBooleanProperty(false); } @Override @@ -116,6 +120,8 @@ protected void controllerDidLoadFxml() { super.controllerDidLoadFxml(); this.classesLink.setTooltip(new Tooltip(I18N.getString("library.dialog.hyperlink.tooltip"))); + + userLibrary.stopExplorer(); // we stop an eventually running explorer before letting the user do something on this dialog. } @Override @@ -139,11 +145,10 @@ public void onCloseRequest(WindowEvent event) { public void openWindow() { super.openWindow(); super.getStage().setTitle(I18N.getString("library.dialog.title")); - loadLibraryList(); - + loadLibraryList(false); } - - void loadLibraryList() { + + void loadLibraryList(boolean changed) { if (listItems == null) { listItems = FXCollections.observableArrayList(); } @@ -181,10 +186,15 @@ void loadLibraryList() { libraryListView.getSelectionModel().selectFirst(); libraryListView.requestFocus(); + + changedProperty.set(changed); } @FXML private void close() { + if (changedProperty.get()) + userLibrary.startExplorer(); + listItems.clear(); closeWindow(); } @@ -200,17 +210,19 @@ private void manage() { private void addJar() { // documentWindowController.onImportJarFxml(getStage()); if (onAddJar != null) { - onAddJar.run(); + Boolean added = onAddJar.run(); + if (Boolean.TRUE.equals(added)) + loadLibraryList(true); } - loadLibraryList(); } @FXML private void addFolder() { if (onAddFolder != null) { - onAddFolder.run(); + Boolean added = onAddFolder.run(); + if (Boolean.TRUE.equals(added)) + loadLibraryList(true); } - loadLibraryList(); } @FXML @@ -222,7 +234,8 @@ private void addRelease() { @Override public void invalidated(Observable observable) { if (!mavenDialogController.getStage().isShowing()) { - loadLibraryList(); + if (mavenDialogController.isConfirmed()) + loadLibraryList(true); mavenDialogController.getStage().showingProperty().removeListener(this); } } @@ -238,7 +251,8 @@ private void addManually() { @Override public void invalidated(Observable observable) { if (!mavenDialogController.getStage().isShowing()) { - loadLibraryList(); + if (mavenDialogController.isConfirmed()) + loadLibraryList(true); mavenDialogController.getStage().showingProperty().removeListener(this); } } @@ -246,26 +260,10 @@ public void invalidated(Observable observable) { } /* - If the file is an fxml, we don't need to stop the library watcher. - Else we have to stop it first: - 1) We stop the library watcher, so that all related class loaders will be closed and the jar can be deleted. - 2) Then, if the file exists, the jar or fxml file will be deleted from the library. - 3) After the jar or fxml is removed, the library watcher is started again. + * We can simply delete the item since the library explorer is shut down when opening the dialog. */ public void processJarFXMLFolderDelete(DialogListItem dialogListItem) { - if (dialogListItem instanceof LibraryDialogListItem && - LibraryUtil.isFxmlPath(((LibraryDialogListItem) dialogListItem).getFilePath())) { - deleteFile(dialogListItem); - } else { - //1) - userLibrary.stopWatching(); - - //2) - deleteFile(dialogListItem); - - //3) - userLibrary.startWatching(); - } + deleteFile(dialogListItem); } private void deleteFile(DialogListItem dialogListItem) { @@ -305,7 +303,7 @@ private void deleteFile(DialogListItem dialogListItem) { } catch (IOException x) { Logger.getLogger(LibraryDialogController.class.getName()).log(Level.SEVERE, "Error while deleting the file.", x); } - loadLibraryList(); + loadLibraryList(true); } public void processJarFXMLFolderEdit(DialogListItem dialogListItem) { @@ -322,6 +320,7 @@ public void processJarFXMLFolderEdit(DialogListItem dialogListItem) { AbstractModalDialog.ButtonID userChoice = iwc.showAndWait(); if (userChoice == AbstractModalDialog.ButtonID.OK) { logInfoMessage("log.user.maven.updated", item); + changedProperty.set(true); } } else { // if (SceneBuilderApp.getSingleton().lookupUnusedDocumentWindowController() != null) { @@ -331,6 +330,7 @@ public void processJarFXMLFolderEdit(DialogListItem dialogListItem) { // item.getFilePath().toFile()); if (onEditFXML != null) { onEditFXML.accept(item.getFilePath()); + changedProperty.set(true); } } } @@ -352,6 +352,7 @@ public void processJarFXMLFolderEdit(DialogListItem dialogListItem) { mavenArtifact.setFilter(iwc.getNewExcludedItems()); updatePreferences(mavenArtifact); logInfoMessage("log.user.maven.updated", mavenArtifact.getCoordinates()); + changedProperty.set(true); } } } @@ -365,18 +366,13 @@ private void updatePreferences(MavenArtifact mavenArtifact) { return; } - userLibrary.stopWatching(); - // Update record artifact final PreferencesRecordArtifact recordArtifact = preferencesControllerBase. getRecordArtifact(mavenArtifact); recordArtifact.writeToJavaPreferences(); - - userLibrary.startWatching(); - } - public void setOnAddJar(Runnable onAddJar) { + public void setOnAddJar(ReturningRunnable onAddJar) { this.onAddJar = onAddJar; } @@ -384,7 +380,7 @@ public void setOnEditFXML(Consumer onEditFXML) { this.onEditFXML = onEditFXML; } - public void setOnAddFolder(Runnable onAddFolder) { + public void setOnAddFolder(ReturningRunnable onAddFolder) { this.onAddFolder = onAddFolder; } } \ No newline at end of file diff --git a/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/library/maven/MavenDialogController.java b/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/library/maven/MavenDialogController.java index e214a40f9..a40b44f53 100644 --- a/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/library/maven/MavenDialogController.java +++ b/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/library/maven/MavenDialogController.java @@ -109,6 +109,7 @@ public class MavenDialogController extends AbstractFxmlWindowController { }; private final PreferencesControllerBase preferencesControllerBase; + private boolean confirmed; public MavenDialogController(EditorController editorController, String userM2Repository, String tempM2Repository, PreferencesControllerBase preferencesControllerBase, Stage owner) { @@ -318,14 +319,16 @@ private void updatePreferences(MavenArtifact mavenArtifact) { return; } - userLibrary.stopWatching(); - // Update record artifact final PreferencesRecordArtifact recordArtifact = preferencesControllerBase. getRecordArtifact(mavenArtifact); recordArtifact.writeToJavaPreferences(); - - userLibrary.startWatching(); + + confirmed = true; + } + + public boolean isConfirmed() { + return confirmed; } } \ No newline at end of file diff --git a/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/library/maven/search/SearchMavenDialogController.java b/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/library/maven/search/SearchMavenDialogController.java index cc6962c3a..26931de9e 100644 --- a/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/library/maven/search/SearchMavenDialogController.java +++ b/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/library/maven/search/SearchMavenDialogController.java @@ -31,15 +31,6 @@ */ package com.oracle.javafx.scenebuilder.kit.editor.panel.library.maven.search; -import com.oracle.javafx.scenebuilder.kit.editor.EditorController; -import com.oracle.javafx.scenebuilder.kit.i18n.I18N; -import com.oracle.javafx.scenebuilder.kit.editor.panel.library.ImportWindowController; -import com.oracle.javafx.scenebuilder.kit.editor.panel.library.LibraryPanelController; -import com.oracle.javafx.scenebuilder.kit.editor.panel.library.maven.MavenArtifact; -import com.oracle.javafx.scenebuilder.kit.editor.panel.library.maven.MavenRepositorySystem; -import com.oracle.javafx.scenebuilder.kit.editor.panel.util.AbstractFxmlWindowController; -import com.oracle.javafx.scenebuilder.kit.editor.panel.util.dialog.AbstractModalDialog.ButtonID; -import com.oracle.javafx.scenebuilder.kit.library.user.UserLibrary; import java.io.File; import java.util.ArrayList; import java.util.HashMap; @@ -48,8 +39,23 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import org.eclipse.aether.artifact.Artifact; +import org.eclipse.aether.artifact.DefaultArtifact; +import org.eclipse.aether.repository.RemoteRepository; +import org.eclipse.aether.version.Version; + +import com.oracle.javafx.scenebuilder.kit.editor.EditorController; +import com.oracle.javafx.scenebuilder.kit.editor.panel.library.ImportWindowController; +import com.oracle.javafx.scenebuilder.kit.editor.panel.library.LibraryPanelController; +import com.oracle.javafx.scenebuilder.kit.editor.panel.library.maven.MavenArtifact; +import com.oracle.javafx.scenebuilder.kit.editor.panel.library.maven.MavenRepositorySystem; +import com.oracle.javafx.scenebuilder.kit.editor.panel.util.AbstractFxmlWindowController; +import com.oracle.javafx.scenebuilder.kit.editor.panel.util.dialog.AbstractModalDialog.ButtonID; +import com.oracle.javafx.scenebuilder.kit.i18n.I18N; +import com.oracle.javafx.scenebuilder.kit.library.user.UserLibrary; import com.oracle.javafx.scenebuilder.kit.preferences.PreferencesControllerBase; import com.oracle.javafx.scenebuilder.kit.preferences.PreferencesRecordArtifact; + import javafx.application.Platform; import javafx.beans.binding.Bindings; import javafx.collections.ListChangeListener; @@ -66,12 +72,7 @@ import javafx.scene.control.Tooltip; import javafx.stage.Modality; import javafx.stage.Stage; -import javafx.stage.Window; import javafx.stage.WindowEvent; -import org.eclipse.aether.artifact.Artifact; -import org.eclipse.aether.artifact.DefaultArtifact; -import org.eclipse.aether.repository.RemoteRepository; -import org.eclipse.aether.version.Version; /** @@ -108,6 +109,7 @@ public class SearchMavenDialogController extends AbstractFxmlWindowController { private DefaultArtifact artifact; private final Stage owner; private final PreferencesControllerBase preferencesControllerBase; + private boolean confirmed; public SearchMavenDialogController(EditorController editorController, String userM2Repository, String tempM2Repository, PreferencesControllerBase preferencesControllerBase, @@ -329,14 +331,12 @@ private void updatePreferences(MavenArtifact mavenArtifact) { return; } - userLibrary.stopWatching(); - // Update record artifact final PreferencesRecordArtifact recordArtifact = preferencesControllerBase. getRecordArtifact(mavenArtifact); recordArtifact.writeToJavaPreferences(); - - userLibrary.startWatching(); + + confirmed = true; } private void addVersion() { @@ -350,4 +350,8 @@ private void addVersion() { artifact = new DefaultArtifact(artifact.getGroupId()+ ":" + artifact.getArtifactId() + ":" +version.toString(), map); remoteRepository = maven.getRemoteRepository(version); } + + public boolean isConfirmed() { + return confirmed; + } } \ No newline at end of file diff --git a/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/library/user/LibraryFolderWatcher.java b/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/library/user/LibraryFolderWatcher.java index 13984a47a..4e1e6797b 100644 --- a/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/library/user/LibraryFolderWatcher.java +++ b/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/library/user/LibraryFolderWatcher.java @@ -43,10 +43,6 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.nio.file.StandardWatchEventKinds; -import java.nio.file.WatchEvent; -import java.nio.file.WatchKey; -import java.nio.file.WatchService; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -77,8 +73,6 @@ class LibraryFolderWatcher implements Runnable { private final UserLibrary library; - private enum FILE_TYPE {FXML, JAR, FOLDER_MARKER} - private static final List JAVAFX_MODULES = Arrays.asList( "javafx-base", "javafx-graphics", "javafx-controls", "javafx-fxml", "javafx-media", "javafx-web", "javafx-swing"); @@ -95,10 +89,7 @@ public LibraryFolderWatcher(UserLibrary library) { public void run() { try { - library.updateExplorationCount(0); - library.updateExplorationDate(new Date()); runDiscovery(); - runWatching(); } catch(InterruptedException x) { // Let's stop: Typically, when UserLibrary::stopWatching is invoked, an InterruptedException is triggered to stop this watch service } @@ -109,210 +100,60 @@ public void run() { * Private */ private void runDiscovery() throws InterruptedException { - // First put the builtin items in the library - library.setItems(BuiltinLibrary.getLibrary().getItems()); + library.setExploring(true); - // Attempts to add the maven jars, including dependencies - List additionalJars = library.getAdditionalJarPaths().get(); - - final Set currentJarsOrFolders = new HashSet<>(additionalJars); - final Set currentFxmls = new HashSet<>(); - - // Now attempts to discover the user library folder - final Path folder = Paths.get(library.getPath()); - if (folder != null && folder.toFile().exists()) { - boolean retry; - do { - try (DirectoryStream stream = Files.newDirectoryStream(folder)) { - for (Path entry: stream) { - if (LibraryUtil.isJarPath(entry)) { - currentJarsOrFolders.add(entry); - } else if (LibraryUtil.isFxmlPath(entry)) { - currentFxmls.add(entry); - } else if (LibraryUtil.isFolderMarkerPath(entry)) { - // open folders marker file: every line should be a single folder entry - // we scan the file and add the path to currentJarsOrFolders - List folderPaths = LibraryUtil.getFolderPaths(entry); - for (Path f : folderPaths) { - currentJarsOrFolders.add(f); + try { + // First put the builtin items in the library + library.setItems(BuiltinLibrary.getLibrary().getItems()); + + // Attempts to add the maven jars, including dependencies + List additionalJars = library.getAdditionalJarPaths().get(); + + final Set currentJarsOrFolders = new HashSet<>(additionalJars); + final Set currentFxmls = new HashSet<>(); + + // Now attempts to discover the user library folder + final Path folder = Paths.get(library.getPath()); + if (folder != null && folder.toFile().exists()) { + boolean retry; + do { + try (DirectoryStream stream = Files.newDirectoryStream(folder)) { + for (Path entry: stream) { + if (LibraryUtil.isJarPath(entry)) { + currentJarsOrFolders.add(entry); + } else if (LibraryUtil.isFxmlPath(entry)) { + currentFxmls.add(entry); + } else if (LibraryUtil.isFolderMarkerPath(entry)) { + // open folders marker file: every line should be a single folder entry + // we scan the file and add the path to currentJarsOrFolders + List folderPaths = LibraryUtil.getFolderPaths(entry); + for (Path f : folderPaths) { + currentJarsOrFolders.add(f); + } } } + retry = false; + } catch(IOException x) { + Thread.sleep(2000 /* ms */); + retry = true; } - retry = false; - } catch(IOException x) { - Thread.sleep(2000 /* ms */); - retry = true; - } finally { - library.updateExplorationCount(library.getExplorationCount()+1); } + while (retry && library.getExplorationCount() < 10); } - while (retry && library.getExplorationCount() < 10); - } - try { - library.setExploring(true); try { updateLibrary(currentFxmls); exploreAndUpdateLibrary(currentJarsOrFolders); } - finally { - library.setExploring(false); - } - } catch(IOException x) { } - } - - private void runWatching() throws InterruptedException { - WatchService watchService = null; - try { - while (true) { - final Path folder = Paths.get(library.getPath()); - - while (watchService == null) { - try { - watchService = folder.getFileSystem().newWatchService(); - } catch(IOException x) { - System.out.println("FileSystem.newWatchService() failed"); //NOI18N - System.out.println("Sleeping..."); //NOI18N - Thread.sleep(1000 /* ms */); - } - } - - WatchKey watchKey = null; - while ((watchKey == null) || (watchKey.isValid() == false)) { - try { - watchKey = folder.register(watchService, - StandardWatchEventKinds.ENTRY_CREATE, - StandardWatchEventKinds.ENTRY_DELETE, - StandardWatchEventKinds.ENTRY_MODIFY); - - WatchKey wk; - do { - wk = watchService.take(); - assert wk == watchKey; - - boolean isDirty = false; - for (WatchEvent e: wk.pollEvents()) { - final WatchEvent.Kind kind = e.kind(); - final Object context = e.context(); - - if (kind == StandardWatchEventKinds.ENTRY_CREATE - || kind == StandardWatchEventKinds.ENTRY_DELETE - || kind == StandardWatchEventKinds.ENTRY_MODIFY) { - assert context instanceof Path; - if (LibraryUtil.isJarPath((Path) context)) { - if (!hasJarBeenAdded((Path) context)) { - isDirty = true; - } - } else if (LibraryUtil.isFxmlPath((Path)context)){ - isDirty = true; - } else if (LibraryUtil.isFolderMarkerPath((Path)context)) { - isDirty = true; - } - } else { - assert kind == StandardWatchEventKinds.OVERFLOW; - } - } - - // We reconstruct a full set from scratch as soon as the - // dirty flag is set. - if (isDirty) { - // First put the builtin items in the library - library.setExploring(true); - try { - library.setItems(BuiltinLibrary.getLibrary().getItems()); - - // Now attempts to add the maven jars - List currentMavenJars = library.getAdditionalJarPaths().get(); - - final Set fxmls = new HashSet<>(); - fxmls.addAll(getAllFiles(FILE_TYPE.FXML)); - updateLibrary(fxmls); - - final Set jarsAndFolders = new HashSet<>(currentMavenJars); - jarsAndFolders.addAll(getAllFiles(FILE_TYPE.JAR)); - - Set foldersMarkers = getAllFiles(FILE_TYPE.FOLDER_MARKER); - for (Path path : foldersMarkers) { - // open folders marker file: every line should be a single folder entry - // we scan the file and add the path to currentJarsOrFolders - List folderPaths = LibraryUtil.getFolderPaths(path); - for (Path f : folderPaths) { - jarsAndFolders.add(f); - } - } - - exploreAndUpdateLibrary(jarsAndFolders); - - library.updateExplorationCount(library.getExplorationCount()+1); - } - finally { - library.setExploring(false); - } - } - } while (wk.reset()); - } catch(IOException x) { - Thread.sleep(1000 /* ms */); - } - } - + catch (IOException x) { + LOGGER.warning("Error updating library: " + x.getMessage()); } } finally { - // Typically, when UserLibrary::stopWatching is invoked, an InterruptedException is triggered to stop this watch service. - // The InterruptedException is handled outside; here we just make sure to close() the watcher service that polls the filesystem. - if (watchService != null) { - // we need to close the filesystem watcher here, otherwise it remains active and locking jars on library folder - try { - watchService.close(); - } catch (IOException e) { - LOGGER.severe("Error closing FileSystemWatchService: " + e.getMessage()); - } - } - } - } - - private Set getAllFiles(FILE_TYPE fileType) throws IOException { - Set res = new HashSet<>(); - final Path folder = Paths.get(library.getPath()); - - try (DirectoryStream ds = Files.newDirectoryStream(folder)) { - for (Path p : ds) { - switch (fileType) { - case FXML: - if (LibraryUtil.isFxmlPath(p)) { - res.add(p); - } - break; - case JAR: - if (LibraryUtil.isJarPath(p)) { - res.add(p); - } - break; - case FOLDER_MARKER: - if (LibraryUtil.isFolderMarkerPath(p)) { - res.add(p); - } - break; - default: - break; - } - } + library.setExploring(false); + library.updateExplorationCount(library.getExplorationCount()+1); } - - return res; } - - private boolean hasJarBeenAdded(Path context) { - boolean hasJarBeenAdded = false; - for (JarReport report : library.getJarReports()) { - if (report.getJar().getFileName().equals(context)) { - hasJarBeenAdded = true; - break; - } - } - return hasJarBeenAdded; - } - private void updateLibrary(Collection paths) throws IOException { final List newItems = new ArrayList<>(); diff --git a/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/library/user/UserLibrary.java b/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/library/user/UserLibrary.java index d3a17793e..95aa34c18 100644 --- a/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/library/user/UserLibrary.java +++ b/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/library/user/UserLibrary.java @@ -75,7 +75,7 @@ */ public class UserLibrary extends Library { - public enum State { READY, WATCHING } + public enum State { READY, EXPLORING } public static final String TAG_USER_DEFINED = "Custom"; //NOI18N @@ -94,8 +94,8 @@ public enum State { READY, WATCHING } private State state = State.READY; private Exception exception; - private LibraryFolderWatcher watcher; - private Thread watcherThread; + private LibraryFolderWatcher explorer; + private Thread explorerThread; // Where we store canonical class names of items we want to exclude from // the user defined one displayed in the Library panel. // As a consequence an empty file means we display all items. @@ -152,39 +152,39 @@ public synchronized State getState() { return state; } - public synchronized void startWatching() { + public synchronized void startExplorer() { + stopExplorer(); // automatically stops an eventually running explorer before spawning a new one + assert state == State.READY; if (state == State.READY) { - assert watcher == null; - assert watcherThread == null; - - watcher = new LibraryFolderWatcher(this); - watcherThread = new Thread(watcher); - watcherThread.setName(watcher.getClass().getSimpleName() + "(" + path + ")"); //NOI18N - watcherThread.setDaemon(true); - watcherThread.start(); - state = State.WATCHING; + assert explorer == null; + assert explorerThread == null; + + explorer = new LibraryFolderWatcher(this); + explorerThread = new Thread(explorer); + explorerThread.setName(explorer.getClass().getSimpleName() + "(" + path + ")"); //NOI18N + explorerThread.setDaemon(true); + explorerThread.start(); + state = State.EXPLORING; } } - public synchronized void stopWatching() { - assert state == State.WATCHING; - - if (state == State.WATCHING) { - assert watcher != null; - assert watcherThread != null; + public synchronized void stopExplorer() { + if (state == State.EXPLORING) { + assert explorer != null; + assert explorerThread != null; assert exception == null; - watcherThread.interrupt(); + explorerThread.interrupt(); try { - watcherThread.join(); + explorerThread.join(); } catch(InterruptedException x) { x.printStackTrace(); } finally { - watcher = null; - watcherThread = null; + explorer = null; + explorerThread = null; state = State.READY; // In READY state, we release the class loader. @@ -425,11 +425,11 @@ private void changeClassLoader(ClassLoader newClassLoader) { public static void main(String[] args) throws Exception { final String path = "/Users/elp/Desktop/MyLib"; //NOI18N final UserLibrary lib = new UserLibrary(path); - lib.startWatching(); + lib.startExplorer(); System.out.println("Starting to watch for 20 s"); //NOI18N Thread.sleep(20 * 1000); System.out.println("Stopping to watch for 20 s"); //NOI18N - lib.stopWatching(); + lib.stopExplorer(); Thread.sleep(20 * 1000); System.out.println("Exiting"); //NOI18N } diff --git a/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/util/ReturningRunnable.java b/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/util/ReturningRunnable.java new file mode 100644 index 000000000..16bc441bc --- /dev/null +++ b/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/util/ReturningRunnable.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2017, 2021, Gluon and/or its affiliates. + * All rights reserved. Use is subject to license terms. + * + * This file is available and licensed under the following license: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the distribution. + * - Neither the name of Oracle Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANYpackage com.oracle.javafx.scenebuilder.kit.util; + */ +package com.oracle.javafx.scenebuilder.kit.util; + +@FunctionalInterface +public interface ReturningRunnable { + + public T run(); +}