I have in my RCP application a View that extends the CommonNavigator class. The projects in the workspace of my application should have different icons depending on their location on the disk: the projects that exist locally in the workspace should have a different icon from the projects that were imported.
I realised this by defining in the plugin.xml two project natures: MyProjectNature and MyProjectNatureImported with different icons and changing between the natures accordingly with the following method:
private void updateProjectNature(final IWorkspace lf_workspace)
{
String l_workspacePath = lf_workspace.getRoot().getLocation().toString();
IProject[] l_projectsInWorkspace = lf_workspace.getRoot().getProjects();
for (IProject l_project : l_projectsInWorkspace)
{
try
{
File l_projectFile = new File(l_workspacePath + l_project.getFullPath().toString());
final IProjectDescription l_projectDescription = l_project.getDescription();
final String[] l_currentNatures = l_projectDescription.getNatureIds();
final String[] l_newNatures = new String[l_currentNatures.length];
int l_index = 0;
if (l_projectFile.exists())
{
for (String l_nature : l_currentNatures)
{
if (l_nature.equals(MyProjectNatureImported.NATURE_ID))
{
l_newNatures[l_index] = MyProjectNature.NATURE_ID;
}
else
{
l_newNatures[l_index] = l_nature;
}
l_index++;
}
}
else
{
for (String l_nature : l_currentNatures)
{
if (l_nature.equals(MyProjectNature.NATURE_ID))
{
l_newNatures[l_index] = MyProjectNatureImported.NATURE_ID;
}
else
{
l_newNatures[l_index] = l_nature;
}
l_index++;
}
}
l_projectDescription.setNatureIds(l_newNatures);
l_project.setDescription(l_projectDescription, null);
}
catch (CoreException e)
{
LOGGER.warning("Error when setting the project nature of the project " + l_project.getName() + ": " + e.getMessage());
}
}
}
When I call this method from the ResourceChangeListener that I added to the workspace, I get an error for each project that it is locked and cannot be editted:
final IWorkspace lf_workspace = ResourcesPlugin.getWorkspace();
lf_workspace.addResourceChangeListener(new IResourceChangeListener()
{
#Override
public void resourceChanged(IResourceChangeEvent event)
{
updateProjectNature(lf_workspace);
}
});
But when I create a Job that runs each few seconds, then it works:
Job l_testJob = new Job("Update navigator")
{
#Override
protected IStatus run(IProgressMonitor monitor)
{
updateProjectNature(lf_workspace);
schedule(1000);
return Status.OK_STATUS;
}
#Override
public boolean shouldSchedule()
{
// Check if the job should be scheduled / executed or not
return !PlatformUI.getWorkbench().isClosing();
}
};
l_testJob.schedule(1000);
I would like to call the method only when changes are made to the workspace and not each second (to save the resources), but I don't understand why I get the error and cannot change the nature from the listener while from the job there is no problem.
Any ideas?
The workspace is locked while the resource change event is sent so that listeners can't make any more changes.
You should be able to submit a single Job from your listener with a scheduling rule to delay the job until the workspace is available. You should use a WorkspaceJob to make sure the update is atomic.
class UpdateNatureJob extends WorkspaceJob
{
UpdateNatureJob()
{
// Scheduling rule
setRule(ResourcesPlugin.getWorkspace().getRoot());
}
#Override
public IStatus runInWorkspace(final IProgressMonitor monitor)
{
... your nature update
return Status.OK_STATUS;
}
}
You are misusing project natures. While your actual problem can be worked around with an extra job, it is an indicator that project nature are likely the wrong means to achieve different images.
The designated way to emphasize those differences like local and imported projects are decorators. Decorators allow you to decorate or exchange image and text of an element (almost) irrespective where it is shown.
Related
There are a lot of questions and answer around concurrency, and mine could be similar to others, but for me it's not a duplicate as for some reason I must be missing something and hope to get some advice...
My question is more one where I need a second pair of eyes to point out what I'm doing incorrectly to enable my code to run in a background thread, but also updated the GUI, without freezing it.
Initially, a PDF file is uploaded to the application, using a task in a thread.
This works fine.
A progress bar is displayed, which animates without issue:
uploadFile()
public void uploadFile(File fileToProcess) {
fileBeingProcessed = fileToProcess;
Task<Parent> uploadingFileTask = new Task<Parent>() {
#Override
public Parent call() {
try {
progressBarStackPane.setVisible(true);
pdfPath = loadPDF(fileBeingProcessed.getAbsolutePath());
createPDFViewer();
openDocument();
} catch (IOException ex) {
java.util.logging.Logger.getLogger(MainSceneController.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
}
};
uploadingFileTask.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
#Override
public void handle(WorkerStateEvent event) {
fileHasBeenUploaded = true;
progressBarStackPane.setVisible(false);
uploadFilePane.setVisible(false);
tabPane.setVisible(true);
/* This is where I am getting issue, more so in createThumbnailPanels() */
setupThumbnailFlowPane();
createThumbnailPanels();
/****** ^^^^^^^^^^^^ ******/
}
});
uploadingFileTask.setOnFailed(evt -> {
uploadingFileTask.getException().printStackTrace(System.err);
System.err.println(Arrays.toString(uploadingFileTask.getException().getSuppressed()));
});
Thread uploadingFileThread = new Thread(uploadingFileTask);
uploadingFileThread.start();
}
Once the document has been uploaded, it is displayed in a tab which allows the user to view the document.
There is a secondary tab, which, after upload, is disabled, until the completion of another task called createThumbnailPanelsTask;
However, before this task is ran, the FlowPane for the Thumbnail Panels is created. This seems to work without issue, and doesn't appear to be the cause of the GUI hanging (this is clearly a loop in createThumbnailPanelsTask, but for clarity I will show setupThumbnailFlowPane()):
setupThumbnailFlowPane()
public void setupThumbnailFlowPane() {
stage = model.getStage();
root = model.getRoot();
secondaryTabScrollPane.setFitToWidth(true);
secondaryTabScrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
/**
This will be removed from here when refactored but for now it is here,
I don't think this is anything to do with my issue
**/
Set<Node> nodes = secondaryTabScrollPane.lookupAll(".scroll-bar");
for (final Node node : nodes) {
if (node instanceof ScrollBar) {
ScrollBar sb = (ScrollBar) node;
if (sb.getOrientation() == Orientation.VERTICAL) {
sb.setUnitIncrement(30.0);
}
if (sb.getOrientation() == Orientation.HORIZONTAL) {
sb.setVisible(false);
}
}
}
secondaryTab = new FlowPane();
secondaryTab.setId("secondaryTab");
secondaryTab.setBackground(new Background(new BackgroundFill(Color.LIGHTSLATEGRAY, new CornerRadii(0), new Insets(0))));
secondaryTab.prefWidthProperty().bind(stage.widthProperty());
secondaryTab.prefHeightProperty().bind(stage.heightProperty());
secondaryTab.setPrefWrapLength(stage.widthProperty().intValue() - 150);
secondaryTab.setHgap(5);
secondaryTab.setVgap(30);
secondaryTab.setBorder(new Border(new BorderStroke(Color.TRANSPARENT, BorderStrokeStyle.NONE, CornerRadii.EMPTY, new BorderWidths(8, 10, 20, 10))));
secondaryTab.setAlignment(Pos.CENTER);
}
Finally, createThumbnailPanels() is called, which is where I believe I am getting the problem.
What is suppose to happen is, after the document has uploaded, the upload file pane is hidden, revealing the Viewer Tab, and also the Secondary Tab.
The secondary tab is disabled at this point, and also has a loading image (a gif) on the left side of it.
The intended behaviour, is that the createThumbnailPanels() task will run in the background, and until it is complete, the tab will remain disabled, however, during this time, the gif image will be rotating, giving the impression there is some loading occurring.
Once the loading has completed, the gif is removed, and the tab is enabled, allowing the user to navigate to it, and see the generated thumbnail panels.
This all works, however, as mentioned, the task is hanging the GUI:
createThumbnailPanels()
public void createThumbnailPanels() {
Task<Void> createThumbnailPanelsTask = new Task<Void>() {
#Override
public Void call() {
if (model.getIcePdfDoc() != null) {
numberOfPagesInDocument = model.getIcePdfDoc().getNumberOfPages();
for (int thumbIndex = 0; thumbIndex < numberOfPagesInDocument; thumbIndex++) {
ThumbnailPanel tb = new ThumbnailPanel(thumbIndex, main, model);
Thumbnail tn = new Thumbnail(tb);
model.setThumbnailAt(tn, thumbIndex);
eventHandlers.setMouseEventsForThumbnails(tb);
/*
I have added this in as I am under the impression that a task runs in a background thread,
and then to update the GUI, I need to call this:
*/
Platform.runLater(() -> {
secondaryTab.getChildren().add(tb);
});
}
}
return null;
}
};
createThumbnailPanelsTask.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
#Override
public void handle(WorkerStateEvent event) {
/*
Further GUI modification run in setOnSucceeded so it runs on main GUI thread(?)
*/
secondaryTabScrollPane.setContent(secondaryTab);
secondaryTab.setDisable(false);
secondaryTab.setGraphic(null);
}
});
createThumbnailPanelsTask.setOnFailed(evt -> {
createThumbnailPanelsTask.getException().printStackTrace(System.err);
System.err.println(Arrays.toString(createThumbnailPanelsTask.getException().getSuppressed()));
});
Thread createThumbnailPanelsThread = new Thread(createThumbnailPanelsTask);
createThumbnailPanelsThread.start();
}
Everything, bar the GUI hanging while it creates the panels, works fine.
Once they've been created, the GUI can be controlled again, the loading gif has been removed, the tab is enabled and the user can navigate to it and view the panels.
Clearly, there is something I am missing about concurrency here.
As mentioned, I was under the impression that a Task runs in a background thread, so I'm a little confused by why it doesn't appear to be doing this. Again, clearly something I am missing.
I have read, and read, and read about concurrency, but just can't seem to work out where in my approach I have gone wrong. I am tempted to try using a Service, however, I feel that I am just over complicating things by considering that, and that there is clearly a simply way to do what I want to achieve.
Any help will be greatly appreciated... a push in the right direction, or some clarification on where I have gone wrong in my understanding.
Thanks in advance, no doubt it's something obvious that once sorted will help me avoid this issue in future!
UPDATED CODE
createThumbnailPanels()
public void createThumbnailPanels() {
Task<Void> createThumbnailPanelsTask = new Task<Void>() {
//TODO: Need to check that it's a PDF
#Override
public Void call() {
if (model.getIcePdfDoc() != null) {
numberOfPagesInDocument = model.getIcePdfDoc().getNumberOfPages();
for (int thumbIndex= 0; thumbIndex< numberOfPagesInDocument; thumbIndex++) {
ThumbnailPanel tb = new ThumbnailPanel(thumbIndex, main, model);
Thumbnail tn = new Thumbnail(tb);
eventHandlers.setMouseEventsForThumbnails(tb);
model.setThumbnailAt(tn, thumbIndex);
model.setThumbnailPanels(tb);
}
setThumbnailPanelsToScrollPane();
}
return null;
}
};
createThumbnailPanelsTask.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
#Override
public void handle(WorkerStateEvent event) {
// setThumbnailPanelsToScrollPane();
}
});
createThumbnailPanelsTask.setOnFailed(evt -> {
createThumbnailPanelsTask.getException().printStackTrace(System.err);
System.err.println(Arrays.toString(createThumbnailPanelsTask.getException().getSuppressed()));
});
Thread createThumbnailPanelsThread = new Thread(createThumbnailPanelsTask);
createThumbnailPanelsThread.start();
}
setThumbnailPanelsToScrollPane()
public void setThumbnailPanelsToScrollPane() {
Task<Void> setThumbnailPanelsToScrollPaneTask = new Task<Void>() {
//TODO: Need to check that it's a PDF
#Override
public Void call() {
Platform.runLater(() -> {
secondaryTab.getChildren().addAll(model.getThumbnailPanels());
secondaryTabScrollPane.setContent(main.informationExtractionPanel);
secondaryTab.setDisable(false);
secondaryTab.setGraphic(null);
});
return null;
}
};
setThumbnailPanelsToScrollPaneTask.setOnFailed(evt -> {
setThumbnailPanelsToScrollPaneTask.getException().printStackTrace(System.err);
System.err.println(Arrays.toString(setThumbnailPanelsToScrollPaneTask.getException().getSuppressed()));
});
Thread setThumbnailPanelsToScrollPaneThread = new Thread(setThumbnailPanelsToScrollPaneTask);
setThumbnailPanelsToScrollPaneThread.start();
}
FYI: If I call setThumbnailPanelsToScrollPane(); in the setOnSucceeded, it doesn't appear to work.
getChildren().add is running on the JavaFX GUI thread(thats what Platform.runLater does), but its only required to run it in a Platform.runLater if the parent that you add children to is connected to the root of the shown gui, that means you should be able to add children to a parent that is not connected to any root, and add the whole parent to the root at the end of the children addition process, if you're doing Platform.runLater in any asynchronous code it will run on the gui thread in your case it is in your asynchronous for loop adding ThumbnailPanels and if the number of them is large the gui will hang.
I'm using a recursive method which implements the use of the SwingWorker class to do a research in one folder and all its subfolders - in the local hard drive.
Basically works fine but I'm stuck when I want to stop the SwingWorker method: when the user change the 'source folder' (I'm using a JTree - JAVAFX - to show all the folders in the local hard drive), I want to stop the current 'SwingWorker research' in that folder and start a new one, with the newest 'source path' results choosed from the user.
All the results of the research are stored in a private ObservableList - and updated everytime in the done() method, just by filling one TableView - JavaFX: so, when the user change the 'source path' I have to clean the results of the previous research.
Start method:
private static ObservableList<msg> data = FXCollections.observableArrayList();
private static SwingWorker<Void, Void> worker;
private static String currentFolder;
#Override
public void start(Stage primaryStage) throws Exception {
// TODO Auto-generated method stub
stage = primaryStage;
primaryStage.setScene(new Scene(createContent()));
styleControls();
primaryStage.initStyle(StageStyle.UNDECORATED);
primaryStage.setMaximized(true);
primaryStage.setFullScreen(false);
primaryStage.show();
msgp = new MsgParser();
}
createContent() method- recursive function its called here:
public Parent createContent() {
tree.getSelectionModel().selectedItemProperty().addListener( new ChangeListener<Object>() {
#Override
public void changed(ObservableValue observable, Object oldValue,
Object newValue) {
TreeItem<File> selectedItem = (TreeItem<File>) newValue;
currentFolder = selectedItem.getValue().getAbsolutePath();
// I want to stop here the previous SwingWorker call : the tree
// ChangeListener event is called when the user change the
// source folder of the research, by selecting one TreeItem on it.
if(worker!= null)
worker.cancel(true);
//Here I clean previous results
data.clear();
TV.setItems(data);
//And I call again the method with the new source Folder
ListMail(new File(currentFolder));
}
});
}
ListMail() method: [recursive SwingWorker]
private void ListMail(File dir) {
worker = new SwingWorker<Void, Void>() {
#Override
protected Void doInBackground() throws Exception {
File[] directoryListing = dir.listFiles();
if (directoryListing != null) {
for (File child : directoryListing) {
if(!worker.isCancelled()) {
if(child != null){
if(!child.isDirectory()) {
if(child.getAbsolutePath().substring(child.getAbsolutePath().lastIndexOf('.')+1).equals("msg")) {
Message message = msgp.parseMsg(child.getPath());
String percorsoMail = child.getAbsolutePath().toUpperCase();
if(message != null) {
String fromEmail = message.getFromEmail();
String fromName = message.getFromName();
String subject = message.getSubject();
String received = message.getDate().toString();
String name;
if(fromEmail != null)
name = fromName + "(" + fromEmail + ")";
else name = fromName;
msg Message = new msg(name, subject, received);
if(!data.contains(Message))
data.add(Message);
//I use the Platform.runLater to
// take count of the number of results found
//It updates the GUI - works fine
Platform.runLater(new Runnable() {
#Override public void run() {
if(data != null && data.size() > 0)
setStatusLabel(data.size());
else
setStatusLabel(0);
}
});
}
}
} else {
/**
* Recursive call here : I do the research
* for the subfolders
*/
ListMail(child);
}
} else {
}
}
}
}
return null;
}
// Update GUI Here
protected void done() {
// I refresh here the TableView: works fine on-the-fly added results
TableView.setItems(data);
TableView.refresh();
}
};
//This doesn't do anything
if(!worker.isCancelled())
worker.execute();
}
Basically, the issue is that the SwingWorker thread never stop, I'm thinking because of the recursive calls which creates new pid process at every run or something ?
Also by using a dedicated external button, which I prefer to avoid, gives no results:
refreshBtn.setOnAction(e -> {
//Handle clicks on refreshBtn button
worker.cancel(true);
});
After I click on TreeItem to change source-folder, it just delete all the ObservableList elements created at that moment, but the previous research don't stop.
Everything works fine instead if I wait the research its finished - but this can works only when I'm in a deep-level folder, while I can't obviously wait when the research start with the "C:\" folder.
Ok so that's here how I managed this by using javafx.concurrent.
Just to point my experience with this, it seems using a recursive background Task for potentially long computations, such as scanning the Whole local drive like in my example, it's very memory consuming - also because I stored some results of this background computation in static local variables to access them faster: the result was a data-structure (ObservableList) with over 5000+ instances of a custom class to represent that specific data computed and then the OutOfMemoryError message or the background thread just going like in 'stand-by' without any advice after running for long time (waiting for garbage collection?).
Anyway here's the code that sum up how I solved: the threads are correctly closed. By the way, sometimes, there's a little 'GUI delay' due to cleaning the GUI on the isCancelled() method check: the GUI swing between clear/not clear, because in my opinion it keeps get filled by the results of the previous tasks in the recursion.
private static BackgroundTask backgroundTask;
private static Thread thread;
tree.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<Object>() {
#Override
public void changed(final ObservableValue observable, final Object oldValue, final Object newValue) {
//I close previous running background tasks if there's any
if (backgroundTask != null) {
while (backgroundTask.isRunning()) {
backgroundTask.cancel(true);
// reset GUI nodes here used to show results of the previous thread
}
}
backgroundTask = new BackGoundTask();
thread= new Thread(backgroundTask);
thread.setDaemon(true);
thread.start();
//This will be called only when latest recursion is finished, not at every run
backgroundTask.setOnSucceeded(e -> {});
}
});
BackgroundTask class:
public static class BackgroundTask extends Task<Object> {
// .. variables used by the task here
//constructor: initialize variables at every run of the Task
public BackgroundTask() {
}
#Override
protected Object call() throws Exception {
if (!isCancelled()) {
// ... Do all background work here
Platform.runLater(new Runnable() {
#Override
public void run() {
// GUI progress can goes here
}
});
//recursion here
if(something) {
//...
} else {
call();
}
} else {
//user want to cancel task: clean GUI nodes
}
return null;
}
}
I hope someone here can help. I am just trying to wrap my head around the Observer Design Pattern, Threading and how I can use both for a project I am doing.
I currently need to implement the both of them on a Media Player I am building using Java FX.
I need to use both of them to update my listView(Populated by a getNames function of files in my directory. I need any changes to my folder of songs to reflect straight away on the GUI.
Is it possible, to have a running thread constantly calling my getNames function(returns an items variable), and if there are any changes to the items variable then I can use the Observer pattern to notify my GUI class to update its list.
I know it's possible to have a thread constantly hitting the function, but I just need some advice on if its then possible to use the Observer pattern to notify on if the items have changed!
I have no code to show, as I am still trying to figure out how to implement this.
Any ideas?
Appreciate any advice at all! Thanks :)
UPDATE
After quite a long time, Got this working with threads and observer patterm. Didn't need WatchService. Used my thread to constantly call a check for change method, then if method returned through then Observer kicked in to update GUI.
Its possible to use this pattern , you need to run a thread to keep watch on folder for files update and to make this thread safe use eventQueue to run your thread
e.g.
java.awt.EventQueue.invokeLater or invokeAndWait
Once change is detected by the thread, then your observer pattern will update GUI
Hope this helps!!
The best approach to this (IMO) would be:
Consider this Oracle tutorial on the WatchService.
As you are using JavaFX, wrap "the basic steps required to implement a watch service" from that tutorial in a JavaFX Task.
Perhaps following the pattern from the Task javadoc "A Task Which Returns Partial Results" to feedback into your view any changes detected by the watch service.
As you note "unfortunately our lecturer won't let us use WatchService", then you can use a method like in the sample code below which is an active poll of the FileSystem. The use of the WatchService is definitely preferred as it can, internally within the JDK implementation, make use of OS provided file watch services. Those OS services can provide a notification of a file change event, so that the Java code does not need to actively poll the file system for changes. Nevertheless, the following inefficient job will likely suffice to do the job in this case...
What the code does is spawn a JavaFX task on a thread which polls the file system and modifies the observable list backing the ListView to match the files on the file system. The list modification is done within a Platform.runLater call to ensure that the modifications to the list backing the list view occur on the JavaFX application thread, so that the active scene graph is not modified off of the JavaFX application thread.
import javafx.application.*;
import javafx.collections.*;
import javafx.collections.transformation.SortedList;
import javafx.concurrent.Task;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.stage.Stage;
import java.io.File;
import java.nio.file.*;
import java.util.Arrays;
import java.util.Comparator;
public class FileWatcher extends Application {
private static final Path WATCH_DIR = Paths.get(System.getProperty("user.dir"));
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) {
ObservableList<File> songFileList = FXCollections.observableArrayList();
SortedList<File> sortedSongFileList = new SortedList<>(
songFileList,
Comparator.comparing(File::getName)
);
ListView<File> songListView = new ListView<>();
songListView.setItems(sortedSongFileList);
songListView.setCellFactory(param -> new ListCell<File>() {
#Override
protected void updateItem(File item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setText(null);
return;
}
setText(item.getName());
}
});
SongWatcher watcher = new SongWatcher(
WATCH_DIR, songFileList
);
Thread watcherThread = new Thread(watcher, "song-watcher");
watcherThread.setDaemon(true);
watcherThread.start();
Scene scene = new Scene(songListView);
stage.setScene(scene);
stage.show();
}
class SongWatcher extends Task<Void> {
private static final String SONG_EXTENSION = "mp3";
private static final long POLL_INTERVAL_MILLIS = 200;
private final Path directory;
private final ObservableList<File> songFiles;
SongWatcher(Path directory, ObservableList<File> songFiles) {
this.directory = directory;
this.songFiles = songFiles;
}
#Override
protected Void call() {
System.out.println("Started watching " + directory + " for song file changes.");
while (!isCancelled()) {
try {
Thread.sleep(POLL_INTERVAL_MILLIS);
} catch (InterruptedException e) {
if (isCancelled()) {
break;
}
Thread.currentThread().interrupt();
}
try {
if (!Files.isDirectory(directory)) {
throw new Exception("Watched directory " + directory + " is not a directory.");
}
File[] foundFiles =
directory
.toFile()
.listFiles(
(dir, name) -> name.endsWith(SONG_EXTENSION)
);
if (foundFiles == null) {
throw new Exception("Watched directory " + directory + " find files returned null (this is not expected).");
}
Platform.runLater(() -> {
// remove files from the song list which are no longer on the disk.
songFiles.removeIf(checkedFile ->
Arrays.binarySearch(foundFiles, checkedFile) < 0
);
// add any files which are on the disk which are not in the song list.
for (File file: foundFiles) {
if (!songFiles.contains(file)) {
songFiles.add(file);
}
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
return null;
}
#Override
protected void succeeded() {
System.out.println("Stopped watching " + directory + " for song file changes.");
}
#Override
protected void cancelled() {
System.out.println("Cancelled watching " + directory + " for song file changes.");
}
#Override
protected void failed() {
System.out.println("Failed watching " + directory + " for song file changes.");
if (getException() != null) {
getException().printStackTrace();
}
}
}
}
so, here is my today problem:
First of all, please note that I do NOT have the Matlab parallel toolbox available.
I am running java code witch interact with Matlab. Sometime Matlab directly call some java functions, sometimes it is the opposite. In this case, we use a notification system which comes from here:
http://undocumentedmatlab.com/blog/matlab-callbacks-for-java-events
We then address the notification in proper callbacks.
Here is a simple use case:
My user select a configuration file using the java interface, loaded into Matlab.
Using an interface listener, we notify Matlab that the configuration file has been selected, it then run a certain number of functions that will analyzes the file
Once the analysis is done, it is pushed into the java runtime, which will populate interface tables with the result. This step involve that matlab will call a java function.
Finally, java request the interface to be switched to an arbitrary decided tab.
This is the order of which things would happen in an ideal world, however, here is the code of the listener actionPerformed method:
#Override
public void actionPerformed(ActionEvent arg0) {
Model wModel = controller.getModel();
Window wWindow = controller.getWindow();
MatlabStructure wStructure = new MatlabStructure();
if(null != wModel) {
wModel.readMatlabData(wStructure);
wModel.notifyMatlab(wStructure, MatlabAction.UpdateCircuit);
}
if(null != wWindow) {
wWindow.getTabContainer().setSelectedComponent(wWindow.getInfosPannel());
}
}
What happen here, is that, when the notifyMatlab method is called, the code does not wait for it to be completed before it continues. So what happen is that the method complete and switch to an empty interface page (setSelectedComponent), and then the component is filled with values.
What I would like to, is for java to wait that my notifyMatlab returns a "I have completed !!" signal, and then pursue. Which involves asynchrounous code since Matlab will code java methods during its execution too ...
So far here is what I tried:
In the MatlabEventObject class, I added an isAcknowledge member, so now the class (which I originaly found in the above link), look like this (I removed all unchanged code from the original class):
public class MatlabEventObject extends java.util.EventObject {
private static final long serialVersionUID = 1L;
private boolean isAcknowledged = false;
public void onNotificationReceived() {
if (source instanceof MatlabEvent) {
System.out.println("Catched a MatlabEvent Pokemon !");
MatlabEvent wSource = (MatlabEvent) source;
wSource.onNotificationReceived();
}
}
public boolean isAcknowledged() {
return isAcknowledged;
}
public void acknowledge() {
isAcknowledged = true;
}
}
In the MatlabEvent class, I have added a future task which goal is to wait for acknowledgement, the methods now look like this:
public class MatlabEvent {
private Vector<IMatlabListener> data = new Vector<IMatlabListener>();
private Vector<MatlabEventObject> matlabEvents = new Vector<MatlabEventObject>();
public void notifyMatlab(final Object obj, final MatlabAction action) {
final Vector<IMatlabListener> dataCopy;
matlabEvents.clear();
synchronized (this) {
dataCopy = new Vector<IMatlabListener>(data);
}
for (int i = 0; i < dataCopy.size(); i++) {
matlabEvents.add(new MatlabEventObject(this, obj, action));
((IMatlabListener) dataCopy.elementAt(i)).testEvent(matlabEvents.get(i));
}
}
public void onNotificationReceived() {
ExecutorService service = Executors.newSingleThreadExecutor();
long timeout = 15;
System.out.println("Executing runnable.");
Runnable r = new Runnable() {
#Override
public void run() {
waitForAcknowledgement(matlabEvents);
}
};
try {
Future<?> task = service.submit(r);
task.get(timeout, TimeUnit.SECONDS);
System.out.println("Notification acknowledged.");
} catch (Exception e) {
e.printStackTrace();
}
}
private void waitForAcknowledgement(final Vector<MatlabEventObject> matlabEvents) {
boolean allEventsAcknowledged = false;
while(!allEventsAcknowledged) {
allEventsAcknowledged = true;
for(MatlabEventObject eventObject : matlabEvents) {
if(!eventObject.isAcknowledged()) {
allEventsAcknowledged = false;
}
break;
}
}
}
}
What happen is that I discover that Matlab actually WAIT for the java code to be completed. So my waitForAcknowledgement method always wait until it timeouts.
In addition, I must say that I have very little knowledge in parallel computing, but I think our java is single thread, so having java waiting for matlab code to complete while matlab is issuing calls to java functions may be an issue. But I can't be sure : ]
If you have any idea on how to solve this issue in a robust way, it will be much much appreciated.
today I changed my Eclipse IDE from 3.7 to 4.2 and my plugin-project has a new feature in the Statusbar of the UI called QuickAccess. But I dont need it, so how can I disable this feature, because the position of my button bar has changed...
For all who have the same problem, it seems that this new feature is hardcoded and can't be disabled :/ https://bugs.eclipse.org/bugs/show_bug.cgi?id=362420
Go to Help --> Install New Software
https://raw.github.com/atlanto/eclipse-4.x-filler/master/pdt_tools.eclipse-4.x-filler.update/
Install that Plugin and Restart the Eclipse. Quick Access automatically hide.
or else you have an option to hide Window --> Hide Quick Access.
Here's a post that shows a way to hide it with CSS. Verified with Eclipse 4.3
Lars Vogel just reported in his blog post "Porting Eclipse 3.x RCP application to Eclipse 4.4 – now without QuickAccess box":
Bug 411821 ([QuickAccess] Contribute SearchField through a fragment or other means)
is now solved.
Thanks to René Brandstetter:
If a RCP app doesn't provide the QuickAccess element in its model, than it will not be visible. So the default is no QuickAcces, easy enough? :)
See the commit 839ee2 for more details
Provide the "QuickAccess" via a e4 application model fragment inside of the "org.eclipse.ui.ide.application".
This removes the "QuickAccess" search field from every none "org.eclipse.ui.ide.application".
You could also hide it and make it work comparable to how it used to work in Eclipse3.7: when user presses ctrl+3 Quick Access functionality pops up (In Eclipse4.3 the ctrl+3 shortcut is still available).
Example of code you could add to your implementation of WorkbenchWindowAdvisor (for Eclipse4.3 rcp application)
private IHandlerActivation quickAccessHandlerActivation;
#Override
public void postWindowOpen() {
hideQuickAccess();
}
private void hideQuickAccess() {
IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
setQuickAccessVisible(window, false);
final IHandlerService service = (IHandlerService) window.getService(IHandlerService.class);
quickAccessHandlerActivation = service.activateHandler(QUICK_ACCESS_COMMAND_ID, new CustomQuickAccessHandler());
}
private void setQuickAccessVisible(IWorkbenchWindow window, boolean visible) {
if (window instanceof WorkbenchWindow) {
MTrimBar topTrim = ((WorkbenchWindow) window).getTopTrim();
for (MTrimElement element : topTrim.getChildren()) {
if (QUICK_ACCESS_ELEMENT_ID.equals(element.getElementId())) {
element.setVisible(visible);
if (visible) {
Composite control = (Composite) element.getWidget();
control.getChildren()[0].addFocusListener(new QuickAccessFocusListener());
}
break;
}
}
}
}
private class QuickAccessFocusListener implements FocusListener {
#Override
public void focusGained(FocusEvent e) {
//not interested
}
#Override
public void focusLost(FocusEvent e) {
((Control) e.widget).removeFocusListener(this);
hideQuickAccess();
}
}
private class CustomQuickAccessHandler extends AbstractHandler {
#Override
public Object execute(ExecutionEvent event) throws ExecutionException {
IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
final IHandlerService service = (IHandlerService) window.getService(IHandlerService.class);
setQuickAccessVisible(window, true);
if (quickAccessHandlerActivation != null) {
service.deactivateHandler(quickAccessHandlerActivation);
try {
return service.executeCommand(QUICK_ACCESS_COMMAND_ID, null);
} catch (NotDefinedException e) {
} catch (NotEnabledException e) {
} catch (NotHandledException e) {
}
}
return null;
}
}