mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-06 03:50:02 +02:00
Refactored FileSystemListenerList to use more modern concurrent
mechanisms
This commit is contained in:
parent
888f2635b4
commit
f1fd921db7
7 changed files with 231 additions and 331 deletions
|
@ -19,8 +19,8 @@ import static org.junit.Assert.*;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.HashMap;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
|
@ -29,13 +29,13 @@ import org.junit.*;
|
||||||
import generic.test.TestUtils;
|
import generic.test.TestUtils;
|
||||||
import ghidra.framework.model.*;
|
import ghidra.framework.model.*;
|
||||||
import ghidra.framework.store.FileSystem;
|
import ghidra.framework.store.FileSystem;
|
||||||
import ghidra.framework.store.FileSystemListenerList;
|
import ghidra.framework.store.FileSystemEventManager;
|
||||||
import ghidra.framework.store.local.LocalFileSystem;
|
import ghidra.framework.store.local.LocalFileSystem;
|
||||||
import ghidra.program.database.ProgramDB;
|
import ghidra.program.database.ProgramDB;
|
||||||
import ghidra.program.model.lang.Language;
|
import ghidra.program.model.lang.Language;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
|
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
|
||||||
import ghidra.util.task.TaskMonitorAdapter;
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
public class ProjectFileManagerTest extends AbstractGhidraHeadedIntegrationTest {
|
public class ProjectFileManagerTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
|
|
||||||
|
@ -45,11 +45,7 @@ public class ProjectFileManagerTest extends AbstractGhidraHeadedIntegrationTest
|
||||||
private LocalFileSystem privateFS;
|
private LocalFileSystem privateFS;
|
||||||
private ProjectFileManager fileMgr;
|
private ProjectFileManager fileMgr;
|
||||||
private DomainFolder root;
|
private DomainFolder root;
|
||||||
private ArrayList<MyEvent> events = new ArrayList<>();
|
private List<MyEvent> events = new ArrayList<>();
|
||||||
|
|
||||||
public ProjectFileManagerTest() {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
|
@ -107,16 +103,16 @@ public class ProjectFileManagerTest extends AbstractGhidraHeadedIntegrationTest
|
||||||
}
|
}
|
||||||
|
|
||||||
private void flushFileSystemEvents() {
|
private void flushFileSystemEvents() {
|
||||||
FileSystemListenerList privateListenerList =
|
FileSystemEventManager privateEventManager =
|
||||||
(FileSystemListenerList) TestUtils.getInstanceField("listeners", privateFS);
|
(FileSystemEventManager) TestUtils.getInstanceField("eventManager", privateFS);
|
||||||
FileSystemListenerList sharedListenerList =
|
FileSystemEventManager sharedEventManager =
|
||||||
(FileSystemListenerList) TestUtils.getInstanceField("listeners", sharedFS);
|
(FileSystemEventManager) TestUtils.getInstanceField("eventManager", sharedFS);
|
||||||
|
|
||||||
flushTheseEvents(privateListenerList);
|
flushTheseEvents(privateEventManager);
|
||||||
flushTheseEvents(sharedListenerList);
|
flushTheseEvents(sharedEventManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void flushTheseEvents(FileSystemListenerList listenerList) {
|
private void flushTheseEvents(FileSystemEventManager eventManager) {
|
||||||
// Events get added synchronously, but processed asynchronously, so we can check to see
|
// Events get added synchronously, but processed asynchronously, so we can check to see
|
||||||
// if any have been added by an action we triggered without waiting. Also, we know that
|
// if any have been added by an action we triggered without waiting. Also, we know that
|
||||||
// no more events will get added, since we are the thread (the main thread) doing the
|
// no more events will get added, since we are the thread (the main thread) doing the
|
||||||
|
@ -124,14 +120,12 @@ public class ProjectFileManagerTest extends AbstractGhidraHeadedIntegrationTest
|
||||||
//
|
//
|
||||||
// If there are queued actions, then we have to kick the handling thread and
|
// If there are queued actions, then we have to kick the handling thread and
|
||||||
// let it finish running.
|
// let it finish running.
|
||||||
while (listenerList.isProcessingEvents()) {
|
|
||||||
// give the event tread some time to send events
|
|
||||||
try {
|
try {
|
||||||
Thread.sleep(100);
|
assertTrue(eventManager.flushEvents(DEFAULT_WAIT_TIMEOUT, TimeUnit.MILLISECONDS));
|
||||||
}
|
}
|
||||||
catch (InterruptedException e) {
|
catch (InterruptedException e) {
|
||||||
// don't care, we will try again
|
failWithException("Interrupted waiting for filesystem events", e);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -180,7 +174,7 @@ public class ProjectFileManagerTest extends AbstractGhidraHeadedIntegrationTest
|
||||||
Language language = getSLEIGH_X86_LANGUAGE();
|
Language language = getSLEIGH_X86_LANGUAGE();
|
||||||
Program p = new ProgramDB(name, language, language.getDefaultCompilerSpec(), this);
|
Program p = new ProgramDB(name, language, language.getDefaultCompilerSpec(), this);
|
||||||
try {
|
try {
|
||||||
return folder.createFile(name, p, TaskMonitorAdapter.DUMMY_MONITOR);
|
return folder.createFile(name, p, TaskMonitor.DUMMY);
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
p.release(this);
|
p.release(this);
|
||||||
|
@ -215,7 +209,7 @@ public class ProjectFileManagerTest extends AbstractGhidraHeadedIntegrationTest
|
||||||
assertNotNull(df2);
|
assertNotNull(df2);
|
||||||
assertEquals("file2", df.getName());
|
assertEquals("file2", df.getName());
|
||||||
|
|
||||||
df1.addToVersionControl("", false, TaskMonitorAdapter.DUMMY_MONITOR);
|
df1.addToVersionControl("", false, TaskMonitor.DUMMY);
|
||||||
|
|
||||||
df = fileMgr.getFileByID(fileID1);
|
df = fileMgr.getFileByID(fileID1);
|
||||||
assertNotNull(df1);
|
assertNotNull(df1);
|
||||||
|
@ -262,7 +256,7 @@ public class ProjectFileManagerTest extends AbstractGhidraHeadedIntegrationTest
|
||||||
DomainFile df1 = createFile(folder, "file1");
|
DomainFile df1 = createFile(folder, "file1");
|
||||||
String fileID = df1.getFileID();
|
String fileID = df1.getFileID();
|
||||||
|
|
||||||
df1.addToVersionControl("", true, TaskMonitorAdapter.DUMMY_MONITOR);
|
df1.addToVersionControl("", true, TaskMonitor.DUMMY);
|
||||||
assertEquals(fileID, df1.getFileID());
|
assertEquals(fileID, df1.getFileID());
|
||||||
|
|
||||||
df1.undoCheckout(true);
|
df1.undoCheckout(true);
|
||||||
|
@ -282,7 +276,7 @@ public class ProjectFileManagerTest extends AbstractGhidraHeadedIntegrationTest
|
||||||
// create shared file /a/file1 and keep checked-out
|
// create shared file /a/file1 and keep checked-out
|
||||||
DomainFile df1 = createFile(folder, "file1");
|
DomainFile df1 = createFile(folder, "file1");
|
||||||
String fileID = df1.getFileID();
|
String fileID = df1.getFileID();
|
||||||
df1.addToVersionControl("", true, TaskMonitorAdapter.DUMMY_MONITOR);
|
df1.addToVersionControl("", true, TaskMonitor.DUMMY);
|
||||||
assertEquals(fileID, df1.getFileID());
|
assertEquals(fileID, df1.getFileID());
|
||||||
|
|
||||||
// Force Hijack - terminate checkout at versioned file-system
|
// Force Hijack - terminate checkout at versioned file-system
|
||||||
|
|
|
@ -15,8 +15,8 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.framework.store;
|
package ghidra.framework.store;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.List;
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <code>FileSystemListenerList</code> maintains a list of FileSystemListener's.
|
* <code>FileSystemListenerList</code> maintains a list of FileSystemListener's.
|
||||||
|
@ -24,32 +24,31 @@ import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
* all FileSystemListener's within its list. Employs either a synchronous
|
* all FileSystemListener's within its list. Employs either a synchronous
|
||||||
* and asynchronous notification mechanism.
|
* and asynchronous notification mechanism.
|
||||||
*/
|
*/
|
||||||
public class FileSystemListenerList implements FileSystemListener {
|
public class FileSystemEventManager implements FileSystemListener {
|
||||||
|
|
||||||
private List<FileSystemListener> listenerList = new CopyOnWriteArrayList<>();
|
private List<FileSystemListener> listeners = new CopyOnWriteArrayList<>();
|
||||||
|
private BlockingQueue<FileSystemEvent> eventQueue = new LinkedBlockingQueue<>();
|
||||||
|
|
||||||
private List<FileSystemEvent> events =
|
private volatile boolean disposed = false;
|
||||||
Collections.synchronizedList(new LinkedList<FileSystemEvent>());
|
|
||||||
|
|
||||||
private boolean enableAsynchronousDispatching;
|
|
||||||
private boolean isEventProcessingThreadWaiting;
|
|
||||||
private boolean alive = true;
|
|
||||||
private Object lock = new Object();
|
|
||||||
private Thread thread;
|
private Thread thread;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct FileSystemListenerList
|
* Constructor
|
||||||
* @param enableAsynchronousDispatching if true a separate dispatch thread will be used
|
* @param enableAsynchronousDispatching if true a separate dispatch thread will be used
|
||||||
* to notify listeners. If false, blocking notification will be performed.
|
* to notify listeners. If false, blocking notification will be performed.
|
||||||
*/
|
*/
|
||||||
public FileSystemListenerList(boolean enableAsynchronousDispatching) {
|
public FileSystemEventManager(boolean enableAsynchronousDispatching) {
|
||||||
this.enableAsynchronousDispatching = enableAsynchronousDispatching;
|
|
||||||
|
if (enableAsynchronousDispatching) {
|
||||||
|
thread = new FileSystemEventProcessingThread();
|
||||||
|
thread.start();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void dispose() {
|
public void dispose() {
|
||||||
alive = false;
|
disposed = true;
|
||||||
synchronized (lock) {
|
if (thread != null) {
|
||||||
lock.notify();
|
thread.interrupt();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,13 +56,8 @@ public class FileSystemListenerList implements FileSystemListener {
|
||||||
* Add a listener to this list.
|
* Add a listener to this list.
|
||||||
* @param listener the listener
|
* @param listener the listener
|
||||||
*/
|
*/
|
||||||
public synchronized void add(FileSystemListener listener) {
|
public void add(FileSystemListener listener) {
|
||||||
listenerList.add(listener);
|
listeners.add(listener);
|
||||||
if (thread == null && enableAsynchronousDispatching) {
|
|
||||||
thread = new FileSystemEventProcessingThread();
|
|
||||||
thread.setName("File System Listener");
|
|
||||||
thread.start();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -71,157 +65,106 @@ public class FileSystemListenerList implements FileSystemListener {
|
||||||
* @param listener the listener
|
* @param listener the listener
|
||||||
*/
|
*/
|
||||||
public void remove(FileSystemListener listener) {
|
public void remove(FileSystemListener listener) {
|
||||||
listenerList.remove(listener);
|
listeners.remove(listener);
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove all listeners from this list.
|
|
||||||
*/
|
|
||||||
public void clear() {
|
|
||||||
listenerList.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void itemMoved(String parentPath, String name, String newParentPath, String newName) {
|
public void itemMoved(String parentPath, String name, String newParentPath, String newName) {
|
||||||
if (enableAsynchronousDispatching) {
|
handleEvent(new ItemMovedEvent(parentPath, name, newParentPath, newName));
|
||||||
add(new ItemMovedEvent(parentPath, name, newParentPath, newName));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
for (FileSystemListener l : listenerList) {
|
|
||||||
l.itemMoved(parentPath, name, newParentPath, newName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void itemRenamed(String parentPath, String itemName, String newName) {
|
public void itemRenamed(String parentPath, String itemName, String newName) {
|
||||||
if (enableAsynchronousDispatching) {
|
handleEvent(new ItemRenamedEvent(parentPath, itemName, newName));
|
||||||
add(new ItemRenamedEvent(parentPath, itemName, newName));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
for (FileSystemListener l : listenerList) {
|
|
||||||
l.itemRenamed(parentPath, itemName, newName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void itemDeleted(String parentPath, String itemName) {
|
public void itemDeleted(String parentPath, String itemName) {
|
||||||
if (enableAsynchronousDispatching) {
|
handleEvent(new ItemDeletedEvent(parentPath, itemName));
|
||||||
add(new ItemDeletedEvent(parentPath, itemName));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
for (FileSystemListener l : listenerList) {
|
|
||||||
l.itemDeleted(parentPath, itemName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void folderRenamed(String parentPath, String folderName, String newFolderName) {
|
public void folderRenamed(String parentPath, String folderName, String newFolderName) {
|
||||||
if (enableAsynchronousDispatching) {
|
handleEvent(new FolderRenamedEvent(parentPath, folderName, newFolderName));
|
||||||
add(new FolderRenamedEvent(parentPath, folderName, newFolderName));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
for (FileSystemListener l : listenerList) {
|
|
||||||
l.folderRenamed(parentPath, folderName, newFolderName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void folderMoved(String parentPath, String folderName, String newParentPath) {
|
public void folderMoved(String parentPath, String folderName, String newParentPath) {
|
||||||
if (enableAsynchronousDispatching) {
|
handleEvent(new FolderMovedEvent(parentPath, folderName, newParentPath));
|
||||||
add(new FolderMovedEvent(parentPath, folderName, newParentPath));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
for (FileSystemListener l : listenerList) {
|
|
||||||
l.folderMoved(parentPath, folderName, newParentPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void folderDeleted(String parentPath, String folderName) {
|
public void folderDeleted(String parentPath, String folderName) {
|
||||||
if (enableAsynchronousDispatching) {
|
handleEvent(new FolderDeletedEvent(parentPath, folderName));
|
||||||
add(new FolderDeletedEvent(parentPath, folderName));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
for (FileSystemListener l : listenerList) {
|
|
||||||
l.folderDeleted(parentPath, folderName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void itemCreated(String parentPath, String itemName) {
|
public void itemCreated(String parentPath, String itemName) {
|
||||||
if (enableAsynchronousDispatching) {
|
handleEvent(new ItemCreatedEvent(parentPath, itemName));
|
||||||
add(new ItemCreatedEvent(parentPath, itemName));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
for (FileSystemListener l : listenerList) {
|
|
||||||
l.itemCreated(parentPath, itemName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void folderCreated(String parentPath, String folderName) {
|
public void folderCreated(String parentPath, String folderName) {
|
||||||
if (enableAsynchronousDispatching) {
|
handleEvent(new FolderCreatedEvent(parentPath, folderName));
|
||||||
add(new FolderCreatedEvent(parentPath, folderName));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
for (FileSystemListener l : listenerList) {
|
|
||||||
l.folderCreated(parentPath, folderName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void itemChanged(String parentPath, String itemName) {
|
public void itemChanged(String parentPath, String itemName) {
|
||||||
if (enableAsynchronousDispatching) {
|
handleEvent(new ItemChangedEvent(parentPath, itemName));
|
||||||
add(new ItemChangedEvent(parentPath, itemName));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
for (FileSystemListener l : listenerList) {
|
|
||||||
l.itemChanged(parentPath, itemName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void syncronize() {
|
public void syncronize() {
|
||||||
if (enableAsynchronousDispatching) {
|
// Note: synchronize calls will only work when using a threaded event queue
|
||||||
|
if (isAsynchronous()) {
|
||||||
add(new SynchronizeEvent());
|
add(new SynchronizeEvent());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void add(FileSystemEvent ev) {
|
private boolean isAsynchronous() {
|
||||||
if (!listenerList.isEmpty()) {
|
return thread != null;
|
||||||
events.add(ev);
|
|
||||||
synchronized (lock) {
|
|
||||||
lock.notify();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void add(FileSystemEvent ev) {
|
||||||
|
if (!listeners.isEmpty()) {
|
||||||
|
eventQueue.add(ev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleEvent(FileSystemEvent e) {
|
||||||
|
if (disposed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isAsynchronous()) {
|
||||||
|
add(e);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
e.process(listeners);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if this class is processing events <b>or</b> needs to process events that are
|
* Blocks until all current events have been processed.
|
||||||
* in its event queue.
|
* <p>
|
||||||
|
* Note: clients should only use this method when {@link #isAsynchronous()} returns true, since
|
||||||
|
* this class cannot track when non-threaded events have finished broadcasting to listeners.
|
||||||
|
* In a synchronous use case, any test that needs to know when client events have been processed
|
||||||
|
* must use some other mechanism to know when event processing is finished.
|
||||||
*
|
*
|
||||||
* @return true if this class is processing events <b>or</b> needs to process events that are
|
* @param timeout the maximum time to wait
|
||||||
* in its event queue.
|
* @param unit the time unit of the {@code time} argument
|
||||||
|
* @return true if the events were processed in the given timeout
|
||||||
|
* @throws InterruptedException if this waiting thread is interrupted
|
||||||
*/
|
*/
|
||||||
public boolean isProcessingEvents() {
|
public boolean flushEvents(long timeout, TimeUnit unit) throws InterruptedException {
|
||||||
synchronized (this) {
|
if (!isAsynchronous()) {
|
||||||
if (thread == null) {
|
return true; // each thread processes its own event
|
||||||
return false; // non-threaded; does not 'process' events, done synchronously
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized (lock) { // lock so nobody adds new events
|
MarkerEvent event = new MarkerEvent();
|
||||||
return !isEventProcessingThreadWaiting || (events.size() > 0);
|
eventQueue.add(event);
|
||||||
}
|
return event.waitForEvent(timeout, unit);
|
||||||
}
|
}
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
@ -231,42 +174,25 @@ public class FileSystemListenerList implements FileSystemListener {
|
||||||
private class FileSystemEventProcessingThread extends Thread {
|
private class FileSystemEventProcessingThread extends Thread {
|
||||||
|
|
||||||
FileSystemEventProcessingThread() {
|
FileSystemEventProcessingThread() {
|
||||||
super("File System Event Processor");
|
super("File System Listener");
|
||||||
setDaemon(true);
|
setDaemon(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
while (alive) {
|
while (!disposed) {
|
||||||
while (!events.isEmpty()) {
|
|
||||||
FileSystemEvent event;
|
|
||||||
synchronized (lock) {
|
|
||||||
event = events.remove(0);
|
|
||||||
}
|
|
||||||
synchronized (FileSystemListenerList.this) {
|
|
||||||
for (FileSystemListener l : listenerList) {
|
|
||||||
event.dispatch(l);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
doWait();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void doWait() {
|
FileSystemEvent event;
|
||||||
try {
|
try {
|
||||||
synchronized (lock) {
|
event = eventQueue.take();
|
||||||
if (alive && events.isEmpty()) {
|
event.process(listeners);
|
||||||
isEventProcessingThreadWaiting = true;
|
|
||||||
lock.wait();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (InterruptedException e) {
|
catch (InterruptedException e) {
|
||||||
// not sure why we are ignoring this
|
// interrupt has been cleared; if other threads rely on this interrupted state,
|
||||||
|
// then mark the thread as interrupted again by calling:
|
||||||
|
// Thread.currentThread().interrupt();
|
||||||
|
// For now, this code relies on the 'alive' flag to know when to terminate
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
isEventProcessingThreadWaiting = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -284,7 +210,18 @@ public class FileSystemListenerList implements FileSystemListener {
|
||||||
this.newName = newName;
|
this.newName = newName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void process(List<FileSystemListener> listeners) {
|
||||||
|
for (FileSystemListener l : listeners) {
|
||||||
|
dispatch(l);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
abstract void dispatch(FileSystemListener listener);
|
abstract void dispatch(FileSystemListener listener);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getClass().getSimpleName();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class ItemMovedEvent extends FileSystemEvent {
|
private static class ItemMovedEvent extends FileSystemEvent {
|
||||||
|
@ -396,4 +333,29 @@ public class FileSystemListenerList implements FileSystemListener {
|
||||||
listener.syncronize();
|
listener.syncronize();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// an event used by the flush method to mark when current events have been processed
|
||||||
|
private static class MarkerEvent extends FileSystemEvent {
|
||||||
|
|
||||||
|
private CountDownLatch latch = new CountDownLatch(1);
|
||||||
|
|
||||||
|
MarkerEvent() {
|
||||||
|
super(null, null, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void dispatch(FileSystemListener listener) {
|
||||||
|
// we don't actually process the event
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void process(List<FileSystemListener> listeners) {
|
||||||
|
latch.countDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean waitForEvent(long timeout, TimeUnit unit) throws InterruptedException {
|
||||||
|
return latch.await(timeout, unit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -81,7 +81,7 @@ public class IndexedLocalFileSystem extends LocalFileSystem {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
* @param file path path for root directory.
|
* @param rootPath path path for root directory.
|
||||||
* @param isVersioned if true item versioning will be enabled.
|
* @param isVersioned if true item versioning will be enabled.
|
||||||
* @param readOnly if true modifications within this file-system will not be allowed
|
* @param readOnly if true modifications within this file-system will not be allowed
|
||||||
* and result in an ReadOnlyException
|
* and result in an ReadOnlyException
|
||||||
|
@ -740,7 +740,7 @@ public class IndexedLocalFileSystem extends LocalFileSystem {
|
||||||
subfolder.name = name;
|
subfolder.name = name;
|
||||||
folder.folders.put(name, subfolder);
|
folder.folders.put(name, subfolder);
|
||||||
if (option == GetFolderOption.CREATE_ALL_NOTIFY) {
|
if (option == GetFolderOption.CREATE_ALL_NOTIFY) {
|
||||||
listeners.folderCreated(folder.getPathname(), name);
|
eventManager.folderCreated(folder.getPathname(), name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -943,7 +943,7 @@ public class IndexedLocalFileSystem extends LocalFileSystem {
|
||||||
indexJournal.close();
|
indexJournal.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
listeners.folderCreated(parentPath, getName(path));
|
eventManager.folderCreated(parentPath, getName(path));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -978,7 +978,7 @@ public class IndexedLocalFileSystem extends LocalFileSystem {
|
||||||
indexJournal.close();
|
indexJournal.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
listeners.folderDeleted(getParentPath(folderPath), getName(folderPath));
|
eventManager.folderDeleted(getParentPath(folderPath), getName(folderPath));
|
||||||
}
|
}
|
||||||
|
|
||||||
void migrateItem(LocalFolderItem item) throws IOException {
|
void migrateItem(LocalFolderItem item) throws IOException {
|
||||||
|
@ -1085,10 +1085,10 @@ public class IndexedLocalFileSystem extends LocalFileSystem {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (folderPath.equals(newFolderPath)) {
|
if (folderPath.equals(newFolderPath)) {
|
||||||
listeners.itemRenamed(folderPath, name, newName);
|
eventManager.itemRenamed(folderPath, name, newName);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
listeners.itemMoved(folderPath, name, newFolderPath, newName);
|
eventManager.itemMoved(folderPath, name, newFolderPath, newName);
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteEmptyVersionedFolders(folderPath);
|
deleteEmptyVersionedFolders(folderPath);
|
||||||
|
@ -1142,7 +1142,7 @@ public class IndexedLocalFileSystem extends LocalFileSystem {
|
||||||
|
|
||||||
updateAffectedItemPaths(folder);
|
updateAffectedItemPaths(folder);
|
||||||
|
|
||||||
listeners.folderMoved(parentPath, folderName, newParentPath);
|
eventManager.folderMoved(parentPath, folderName, newParentPath);
|
||||||
deleteEmptyVersionedFolders(parentPath);
|
deleteEmptyVersionedFolders(parentPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1198,7 +1198,7 @@ public class IndexedLocalFileSystem extends LocalFileSystem {
|
||||||
|
|
||||||
updateAffectedItemPaths(folder);
|
updateAffectedItemPaths(folder);
|
||||||
|
|
||||||
listeners.folderRenamed(parentPath, folderName, newFolderName);
|
eventManager.folderRenamed(parentPath, folderName, newFolderName);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -74,7 +74,7 @@ public abstract class LocalFileSystem implements FileSystem {
|
||||||
protected final File root;
|
protected final File root;
|
||||||
protected final boolean isVersioned;
|
protected final boolean isVersioned;
|
||||||
protected final boolean readOnly;
|
protected final boolean readOnly;
|
||||||
protected final FileSystemListenerList listeners;
|
protected final FileSystemEventManager eventManager;
|
||||||
|
|
||||||
private RepositoryLogger repositoryLogger;
|
private RepositoryLogger repositoryLogger;
|
||||||
|
|
||||||
|
@ -187,7 +187,7 @@ public abstract class LocalFileSystem implements FileSystem {
|
||||||
|
|
||||||
this.isVersioned = isVersioned;
|
this.isVersioned = isVersioned;
|
||||||
this.readOnly = readOnly;
|
this.readOnly = readOnly;
|
||||||
listeners = new FileSystemListenerList(enableAsyncronousDispatching);
|
eventManager = new FileSystemEventManager(enableAsyncronousDispatching);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -205,7 +205,7 @@ public abstract class LocalFileSystem implements FileSystem {
|
||||||
this.root = null;
|
this.root = null;
|
||||||
this.isVersioned = false;
|
this.isVersioned = false;
|
||||||
this.readOnly = true;
|
this.readOnly = true;
|
||||||
listeners = null;
|
eventManager = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void cleanupTemporaryFiles(String folderPath) {
|
private void cleanupTemporaryFiles(String folderPath) {
|
||||||
|
@ -542,7 +542,7 @@ public abstract class LocalFileSystem implements FileSystem {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
listeners.itemCreated(parentPath, name);
|
eventManager.itemCreated(parentPath, name);
|
||||||
|
|
||||||
return dataFile;
|
return dataFile;
|
||||||
}
|
}
|
||||||
|
@ -632,10 +632,10 @@ public abstract class LocalFileSystem implements FileSystem {
|
||||||
success = true;
|
success = true;
|
||||||
|
|
||||||
if (folderPath.equals(newFolderPath)) {
|
if (folderPath.equals(newFolderPath)) {
|
||||||
listeners.itemRenamed(folderPath, name, newName);
|
eventManager.itemRenamed(folderPath, name, newName);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
listeners.itemMoved(folderPath, name, newFolderPath, newName);
|
eventManager.itemMoved(folderPath, name, newFolderPath, newName);
|
||||||
}
|
}
|
||||||
deleteEmptyVersionedFolders(folderPath);
|
deleteEmptyVersionedFolders(folderPath);
|
||||||
deallocateItemStorage(folderPath, name);
|
deallocateItemStorage(folderPath, name);
|
||||||
|
@ -675,8 +675,8 @@ public abstract class LocalFileSystem implements FileSystem {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void addFileSystemListener(FileSystemListener listener) {
|
public void addFileSystemListener(FileSystemListener listener) {
|
||||||
if (listeners != null) {
|
if (eventManager != null) {
|
||||||
listeners.add(listener);
|
eventManager.add(listener);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -685,8 +685,8 @@ public abstract class LocalFileSystem implements FileSystem {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void removeFileSystemListener(FileSystemListener listener) {
|
public void removeFileSystemListener(FileSystemListener listener) {
|
||||||
if (listeners != null) {
|
if (eventManager != null) {
|
||||||
listeners.remove(listener);
|
eventManager.remove(listener);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -694,7 +694,7 @@ public abstract class LocalFileSystem implements FileSystem {
|
||||||
* Returns file system listener.
|
* Returns file system listener.
|
||||||
*/
|
*/
|
||||||
FileSystemListener getListener() {
|
FileSystemListener getListener() {
|
||||||
return listeners;
|
return eventManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -844,8 +844,8 @@ public abstract class LocalFileSystem implements FileSystem {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void dispose() {
|
public void dispose() {
|
||||||
if (listeners != null) {
|
if (eventManager != null) {
|
||||||
listeners.dispose();
|
eventManager.dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
/* ###
|
/* ###
|
||||||
* IP: GHIDRA
|
* IP: GHIDRA
|
||||||
* REVIEWED: YES
|
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -16,14 +15,13 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.framework.store.local;
|
package ghidra.framework.store.local;
|
||||||
|
|
||||||
import ghidra.framework.store.FolderNotEmptyException;
|
|
||||||
import ghidra.util.*;
|
|
||||||
import ghidra.util.exception.DuplicateFileException;
|
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
|
||||||
|
import ghidra.framework.store.FolderNotEmptyException;
|
||||||
|
import ghidra.util.*;
|
||||||
|
import ghidra.util.exception.DuplicateFileException;
|
||||||
import utilities.util.FileUtilities;
|
import utilities.util.FileUtilities;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -150,12 +148,12 @@ public class MangledLocalFileSystem extends LocalFileSystem {
|
||||||
if (dirList == null) {
|
if (dirList == null) {
|
||||||
throw new FileNotFoundException("Folder " + folderPath + " not found");
|
throw new FileNotFoundException("Folder " + folderPath + " not found");
|
||||||
}
|
}
|
||||||
ArrayList<String> fileList = new ArrayList<String>(dirList.length);
|
ArrayList<String> fileList = new ArrayList<>(dirList.length);
|
||||||
for (int i = 0; i < dirList.length; i++) {
|
for (File element : dirList) {
|
||||||
String name = dirList[i].getName();
|
String name = element.getName();
|
||||||
if (name.endsWith(PROPERTY_EXT) && dirList[i].isFile()) {
|
if (name.endsWith(PROPERTY_EXT) && element.isFile()) {
|
||||||
if (!NamingUtilities.isValidMangledName(dirList[i].getName())) {
|
if (!NamingUtilities.isValidMangledName(element.getName())) {
|
||||||
log.warn("Ignoring property file with bad name: " + dirList[i]);
|
log.warn("Ignoring property file with bad name: " + element);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
int index = name.lastIndexOf(PROPERTY_EXT);
|
int index = name.lastIndexOf(PROPERTY_EXT);
|
||||||
|
@ -172,6 +170,7 @@ public class MangledLocalFileSystem extends LocalFileSystem {
|
||||||
/*
|
/*
|
||||||
* @see ghidra.framework.store.FileSystem#getFolders(java.lang.String)
|
* @see ghidra.framework.store.FileSystem#getFolders(java.lang.String)
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public synchronized String[] getFolderNames(String folderPath) throws IOException {
|
public synchronized String[] getFolderNames(String folderPath) throws IOException {
|
||||||
File dir = getFile(folderPath);
|
File dir = getFile(folderPath);
|
||||||
|
|
||||||
|
@ -179,12 +178,12 @@ public class MangledLocalFileSystem extends LocalFileSystem {
|
||||||
if (dirList == null) {
|
if (dirList == null) {
|
||||||
throw new FileNotFoundException("Folder " + folderPath + " not found");
|
throw new FileNotFoundException("Folder " + folderPath + " not found");
|
||||||
}
|
}
|
||||||
ArrayList<String> folderList = new ArrayList<String>(dirList.length);
|
ArrayList<String> folderList = new ArrayList<>(dirList.length);
|
||||||
for (int i = 0; i < dirList.length; i++) {
|
for (File element : dirList) {
|
||||||
if (!dirList[i].isDirectory()) {
|
if (!element.isDirectory()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
String name = demangleName(dirList[i].getName());
|
String name = demangleName(element.getName());
|
||||||
if (name != null) {
|
if (name != null) {
|
||||||
folderList.add(name);
|
folderList.add(name);
|
||||||
}
|
}
|
||||||
|
@ -196,6 +195,7 @@ public class MangledLocalFileSystem extends LocalFileSystem {
|
||||||
/*
|
/*
|
||||||
* @see ghidra.framework.store.FileSystem#createFolder(java.lang.String, java.lang.String)
|
* @see ghidra.framework.store.FileSystem#createFolder(java.lang.String, java.lang.String)
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public synchronized void createFolder(String parentPath, String folderName)
|
public synchronized void createFolder(String parentPath, String folderName)
|
||||||
throws InvalidNameException, IOException {
|
throws InvalidNameException, IOException {
|
||||||
|
|
||||||
|
@ -218,6 +218,7 @@ public class MangledLocalFileSystem extends LocalFileSystem {
|
||||||
/*
|
/*
|
||||||
* @see ghidra.framework.store.FileSystem#deleteFolder(java.lang.String)
|
* @see ghidra.framework.store.FileSystem#deleteFolder(java.lang.String)
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public synchronized void deleteFolder(String folderPath) throws IOException {
|
public synchronized void deleteFolder(String folderPath) throws IOException {
|
||||||
|
|
||||||
if (readOnly) {
|
if (readOnly) {
|
||||||
|
@ -243,12 +244,13 @@ public class MangledLocalFileSystem extends LocalFileSystem {
|
||||||
}
|
}
|
||||||
FileUtilities.deleteDir(file);
|
FileUtilities.deleteDir(file);
|
||||||
|
|
||||||
listeners.folderDeleted(getParentPath(folderPath), getName(folderPath));
|
eventManager.folderDeleted(getParentPath(folderPath), getName(folderPath));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @see ghidra.framework.store.FileSystem#moveFolder(java.lang.String, java.lang.String, java.lang.String)
|
* @see ghidra.framework.store.FileSystem#moveFolder(java.lang.String, java.lang.String, java.lang.String)
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public synchronized void moveFolder(String parentPath, String folderName, String newParentPath)
|
public synchronized void moveFolder(String parentPath, String folderName, String newParentPath)
|
||||||
throws InvalidNameException, IOException {
|
throws InvalidNameException, IOException {
|
||||||
|
|
||||||
|
@ -277,7 +279,7 @@ public class MangledLocalFileSystem extends LocalFileSystem {
|
||||||
throw new IOException("move failed for unknown reason");
|
throw new IOException("move failed for unknown reason");
|
||||||
}
|
}
|
||||||
|
|
||||||
listeners.folderMoved(parentPath, folderName, newParentPath);
|
eventManager.folderMoved(parentPath, folderName, newParentPath);
|
||||||
deleteEmptyVersionedFolders(parentPath);
|
deleteEmptyVersionedFolders(parentPath);
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
|
@ -290,7 +292,9 @@ public class MangledLocalFileSystem extends LocalFileSystem {
|
||||||
/*
|
/*
|
||||||
* @see ghidra.framework.store.FileSystem#renameFolder(java.lang.String, java.lang.String, java.lang.String)
|
* @see ghidra.framework.store.FileSystem#renameFolder(java.lang.String, java.lang.String, java.lang.String)
|
||||||
*/
|
*/
|
||||||
public synchronized void renameFolder(String parentPath, String folderName, String newFolderName)
|
@Override
|
||||||
|
public synchronized void renameFolder(String parentPath, String folderName,
|
||||||
|
String newFolderName)
|
||||||
throws InvalidNameException, IOException {
|
throws InvalidNameException, IOException {
|
||||||
|
|
||||||
if (readOnly) {
|
if (readOnly) {
|
||||||
|
@ -315,7 +319,7 @@ public class MangledLocalFileSystem extends LocalFileSystem {
|
||||||
throw new IOException("Folder may contain files that are in use");
|
throw new IOException("Folder may contain files that are in use");
|
||||||
}
|
}
|
||||||
|
|
||||||
listeners.folderRenamed(parentPath, folderName, newFolderName);
|
eventManager.folderRenamed(parentPath, folderName, newFolderName);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -329,7 +333,8 @@ public class MangledLocalFileSystem extends LocalFileSystem {
|
||||||
throw new FileNotFoundException("Empty read-only file system");
|
throw new FileNotFoundException("Empty read-only file system");
|
||||||
}
|
}
|
||||||
if (path.charAt(0) != SEPARATOR_CHAR) {
|
if (path.charAt(0) != SEPARATOR_CHAR) {
|
||||||
throw new FileNotFoundException("Path names must begin with \'" + SEPARATOR_CHAR + "\'");
|
throw new FileNotFoundException(
|
||||||
|
"Path names must begin with \'" + SEPARATOR_CHAR + "\'");
|
||||||
}
|
}
|
||||||
if (path.length() == 1) {
|
if (path.length() == 1) {
|
||||||
return root;
|
return root;
|
||||||
|
@ -344,9 +349,9 @@ public class MangledLocalFileSystem extends LocalFileSystem {
|
||||||
}
|
}
|
||||||
StringBuilder buf = new StringBuilder();
|
StringBuilder buf = new StringBuilder();
|
||||||
String[] split = path.split(SEPARATOR);
|
String[] split = path.split(SEPARATOR);
|
||||||
for (int i = 0; i < split.length; i++) {
|
for (String element : split) {
|
||||||
buf.append(SEPARATOR_CHAR);
|
buf.append(SEPARATOR_CHAR);
|
||||||
buf.append(escapeHiddenDirPrefixChars(split[i]));
|
buf.append(escapeHiddenDirPrefixChars(element));
|
||||||
}
|
}
|
||||||
return NamingUtilities.mangle(buf.toString());
|
return NamingUtilities.mangle(buf.toString());
|
||||||
}
|
}
|
||||||
|
@ -399,7 +404,7 @@ public class MangledLocalFileSystem extends LocalFileSystem {
|
||||||
String parentPath = getParentPath(folderPath);
|
String parentPath = getParentPath(folderPath);
|
||||||
createFolders(parentDir, parentPath);
|
createFolders(parentDir, parentPath);
|
||||||
folderDir.mkdir();
|
folderDir.mkdir();
|
||||||
listeners.folderCreated(parentPath, getName(folderPath));
|
eventManager.folderCreated(parentPath, getName(folderPath));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -443,7 +448,8 @@ public class MangledLocalFileSystem extends LocalFileSystem {
|
||||||
}
|
}
|
||||||
|
|
||||||
IndexedV1LocalFileSystem indexedFs =
|
IndexedV1LocalFileSystem indexedFs =
|
||||||
new IndexedV1LocalFileSystem(tmpRoot.getAbsolutePath(), isVersioned, false, false, true);
|
new IndexedV1LocalFileSystem(tmpRoot.getAbsolutePath(), isVersioned, false, false,
|
||||||
|
true);
|
||||||
|
|
||||||
migrationInProgress = true;
|
migrationInProgress = true;
|
||||||
migrateFolder(SEPARATOR, indexedFs);
|
migrateFolder(SEPARATOR, indexedFs);
|
||||||
|
|
|
@ -41,7 +41,7 @@ import ghidra.util.task.TaskMonitor;
|
||||||
public class RemoteFileSystem implements FileSystem, RemoteAdapterListener {
|
public class RemoteFileSystem implements FileSystem, RemoteAdapterListener {
|
||||||
|
|
||||||
private RepositoryAdapter repository;
|
private RepositoryAdapter repository;
|
||||||
private FileSystemListenerList listeners = new FileSystemListenerList(true);
|
private FileSystemEventManager eventManager = new FileSystemEventManager(true);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a new remote file system which corresponds to a remote repository.
|
* Construct a new remote file system which corresponds to a remote repository.
|
||||||
|
@ -49,7 +49,7 @@ public class RemoteFileSystem implements FileSystem, RemoteAdapterListener {
|
||||||
*/
|
*/
|
||||||
public RemoteFileSystem(RepositoryAdapter repository) {
|
public RemoteFileSystem(RepositoryAdapter repository) {
|
||||||
this.repository = repository;
|
this.repository = repository;
|
||||||
repository.setFileSystemListener(listeners);
|
repository.setFileSystemListener(eventManager);
|
||||||
repository.addListener(this);
|
repository.addListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,12 +65,12 @@ public class RemoteFileSystem implements FileSystem, RemoteAdapterListener {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addFileSystemListener(FileSystemListener listener) {
|
public void addFileSystemListener(FileSystemListener listener) {
|
||||||
listeners.add(listener);
|
eventManager.add(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void removeFileSystemListener(FileSystemListener listener) {
|
public void removeFileSystemListener(FileSystemListener listener) {
|
||||||
listeners.remove(listener);
|
eventManager.remove(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -230,13 +230,13 @@ public class RemoteFileSystem implements FileSystem, RemoteAdapterListener {
|
||||||
@Override
|
@Override
|
||||||
public void connectionStateChanged(Object adapter) {
|
public void connectionStateChanged(Object adapter) {
|
||||||
if (adapter == repository) {
|
if (adapter == repository) {
|
||||||
listeners.syncronize();
|
eventManager.syncronize();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void dispose() {
|
public void dispose() {
|
||||||
listeners.dispose();
|
eventManager.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,15 +18,14 @@ package ghidra.framework.store.local;
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.List;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import org.junit.*;
|
import org.junit.*;
|
||||||
|
|
||||||
import db.*;
|
import db.*;
|
||||||
import db.buffers.BufferFile;
|
import db.buffers.BufferFile;
|
||||||
import generic.test.AbstractGenericTest;
|
import generic.test.*;
|
||||||
import generic.test.TestUtils;
|
|
||||||
import ghidra.framework.store.*;
|
import ghidra.framework.store.*;
|
||||||
import ghidra.util.InvalidNameException;
|
import ghidra.util.InvalidNameException;
|
||||||
import ghidra.util.PropertyFile;
|
import ghidra.util.PropertyFile;
|
||||||
|
@ -41,7 +40,7 @@ public abstract class AbstractLocalFileSystemTest extends AbstractGenericTest {
|
||||||
LocalFileSystem fs;
|
LocalFileSystem fs;
|
||||||
File projectDir;
|
File projectDir;
|
||||||
|
|
||||||
ArrayList<MyEvent> events = new ArrayList<>();
|
List<MyEvent> events = new ArrayList<>();
|
||||||
|
|
||||||
public AbstractLocalFileSystemTest(boolean useIndexedFileSystem) {
|
public AbstractLocalFileSystemTest(boolean useIndexedFileSystem) {
|
||||||
super();
|
super();
|
||||||
|
@ -51,7 +50,7 @@ public abstract class AbstractLocalFileSystemTest extends AbstractGenericTest {
|
||||||
@Before
|
@Before
|
||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
|
|
||||||
File tempDir = new File(AbstractGenericTest.getTestDirectoryPath());
|
File tempDir = new File(AbstractGTest.getTestDirectoryPath());
|
||||||
projectDir = new File(tempDir, "testproject");
|
projectDir = new File(tempDir, "testproject");
|
||||||
FileUtilities.deleteDir(projectDir);
|
FileUtilities.deleteDir(projectDir);
|
||||||
projectDir.mkdir();
|
projectDir.mkdir();
|
||||||
|
@ -114,6 +113,7 @@ public abstract class AbstractLocalFileSystemTest extends AbstractGenericTest {
|
||||||
Assert.fail();
|
Assert.fail();
|
||||||
}
|
}
|
||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
|
// expected
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -121,6 +121,7 @@ public abstract class AbstractLocalFileSystemTest extends AbstractGenericTest {
|
||||||
Assert.fail();
|
Assert.fail();
|
||||||
}
|
}
|
||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
|
// expected
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -161,78 +162,6 @@ public abstract class AbstractLocalFileSystemTest extends AbstractGenericTest {
|
||||||
checkEvent("Folder Created", "/a1/a2/a3", "a4", null, null, events.get(3));
|
checkEvent("Folder Created", "/a1/a2/a3", "a4", null, null, events.get(3));
|
||||||
}
|
}
|
||||||
|
|
||||||
// @Test
|
|
||||||
// public void testFolderPathTooLong() throws Exception {
|
|
||||||
//
|
|
||||||
// StringBuffer sb = new StringBuffer();
|
|
||||||
// int projectDirLength = projectDir.getAbsolutePath().length();
|
|
||||||
// int i = 0;
|
|
||||||
// while (sb.length() + projectDirLength < 248) {
|
|
||||||
// sb.append("/a" + i);
|
|
||||||
// ++i;
|
|
||||||
// }
|
|
||||||
// try {
|
|
||||||
// fs.createFolder(sb.toString(), "aaaa");
|
|
||||||
// Assert.fail("Should have gotten IO exception on too long of a filename!");
|
|
||||||
// }
|
|
||||||
// catch (IOException e) {
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// @Test
|
|
||||||
// public void testDataFilePathTooLong() throws Exception {
|
|
||||||
// fs.createFolder("/", "abc");
|
|
||||||
// String data = "This is a test";
|
|
||||||
// byte[] dataBytes = data.getBytes();
|
|
||||||
//
|
|
||||||
// StringBuffer sb = new StringBuffer();
|
|
||||||
// int projectDirLength = projectDir.getAbsolutePath().length();
|
|
||||||
// int i = 0;
|
|
||||||
// while (sb.length() + projectDirLength < 248) {
|
|
||||||
// sb.append("/a" + i);
|
|
||||||
// ++i;
|
|
||||||
// }
|
|
||||||
// try {
|
|
||||||
// fs.createDataFile(sb.toString(), "freddxxxx", new ByteArrayInputStream(dataBytes), null,
|
|
||||||
// "Data", null);
|
|
||||||
// Assert.fail("Should have gotten IO Exception!");
|
|
||||||
// }
|
|
||||||
// catch (IOException e) {
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// public void testDataBasePathTooLong() throws Exception {
|
|
||||||
// fs.createFolder("/", "abc");
|
|
||||||
// DBHandle dbh = new DBHandle();
|
|
||||||
// long id = dbh.startTransaction();
|
|
||||||
// dbh.createTable("test", new Schema(0, "key", new Class[] { IntField.class },
|
|
||||||
// new String[] { "dummy" }));
|
|
||||||
// dbh.endTransaction(id, true);
|
|
||||||
// int projectDirLength = projectDir.getAbsolutePath().length();
|
|
||||||
// StringBuffer sb = new StringBuffer();
|
|
||||||
// int i = 0;
|
|
||||||
// while (sb.length() + projectDirLength < 248) {
|
|
||||||
// sb.append("/a" + i);
|
|
||||||
// ++i;
|
|
||||||
// }
|
|
||||||
// try {
|
|
||||||
// fs.createDatabase(sb.toString(), "freddxxxx", null, "Database", dbh.getBufferSize(),
|
|
||||||
// "bob", null);
|
|
||||||
// Assert.fail("Should have gotten IO Exception!");
|
|
||||||
// }
|
|
||||||
// catch (IOException e) {
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string
|
|
||||||
* @param string2
|
|
||||||
* @param string3
|
|
||||||
* @param object
|
|
||||||
* @param object2
|
|
||||||
* @param object3
|
|
||||||
*/
|
|
||||||
private void checkEvent(String op, String path, String name, String newPath, String newName,
|
private void checkEvent(String op, String path, String name, String newPath, String newName,
|
||||||
Object evObj) {
|
Object evObj) {
|
||||||
MyEvent event = (MyEvent) evObj;
|
MyEvent event = (MyEvent) evObj;
|
||||||
|
@ -262,6 +191,7 @@ public abstract class AbstractLocalFileSystemTest extends AbstractGenericTest {
|
||||||
Assert.fail();
|
Assert.fail();
|
||||||
}
|
}
|
||||||
catch (FolderNotEmptyException e) {
|
catch (FolderNotEmptyException e) {
|
||||||
|
// expected
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -337,12 +267,14 @@ public abstract class AbstractLocalFileSystemTest extends AbstractGenericTest {
|
||||||
Assert.fail();
|
Assert.fail();
|
||||||
}
|
}
|
||||||
catch (FileNotFoundException e) {
|
catch (FileNotFoundException e) {
|
||||||
|
// expected
|
||||||
}
|
}
|
||||||
fs.createFolder("/b", "def");
|
fs.createFolder("/b", "def");
|
||||||
try {
|
try {
|
||||||
fs.moveFolder("/mno/abc", "def", "/b");
|
fs.moveFolder("/mno/abc", "def", "/b");
|
||||||
}
|
}
|
||||||
catch (DuplicateFileException e) {
|
catch (DuplicateFileException e) {
|
||||||
|
// expected
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -675,8 +607,6 @@ public abstract class AbstractLocalFileSystemTest extends AbstractGenericTest {
|
||||||
|
|
||||||
DataFileItem df = fs.createDataFile("/abc", "fred", new ByteArrayInputStream(dataBytes),
|
DataFileItem df = fs.createDataFile("/abc", "fred", new ByteArrayInputStream(dataBytes),
|
||||||
null, "Data", null);
|
null, "Data", null);
|
||||||
DataFileItem df2 = fs.createDataFile("/abc", "bob", new ByteArrayInputStream(dataBytes),
|
|
||||||
null, "Data", null);
|
|
||||||
createDatabase("/abc", "greg", "123");
|
createDatabase("/abc", "greg", "123");
|
||||||
|
|
||||||
String[] items = fs.getItemNames("/abc");
|
String[] items = fs.getItemNames("/abc");
|
||||||
|
@ -991,25 +921,43 @@ public abstract class AbstractLocalFileSystemTest extends AbstractGenericTest {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void syncronize() {
|
public void syncronize() {
|
||||||
|
// not tracked
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void flushFileSystemEvents() {
|
private void flushFileSystemEvents() {
|
||||||
FileSystemListenerList listenerList =
|
FileSystemEventManager eventManager =
|
||||||
(FileSystemListenerList) TestUtils.getInstanceField("listeners", fs);
|
(FileSystemEventManager) TestUtils.getInstanceField("eventManager", fs);
|
||||||
while (listenerList.isProcessingEvents()) {
|
|
||||||
// give the event tread some time to send events
|
|
||||||
try {
|
try {
|
||||||
Thread.sleep(100);
|
eventManager.flushEvents(DEFAULT_WAIT_TIMEOUT, TimeUnit.MILLISECONDS);
|
||||||
}
|
}
|
||||||
catch (InterruptedException e) {
|
catch (InterruptedException e) {
|
||||||
// don't care, we will try again
|
failWithException("Interrupted waiting for filesystem events", e);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class MyEvent {
|
class MyEvent {
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
final int prime = 31;
|
||||||
|
int result = 1;
|
||||||
|
result = prime * result + ((name == null) ? 0 : name.hashCode());
|
||||||
|
result = prime * result + ((newName == null) ? 0 : newName.hashCode());
|
||||||
|
result = prime * result + ((newParentPath == null) ? 0 : newParentPath.hashCode());
|
||||||
|
result = prime * result + ((op == null) ? 0 : op.hashCode());
|
||||||
|
result = prime * result + ((parentPath == null) ? 0 : parentPath.hashCode());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
MyEvent other = (MyEvent) obj;
|
||||||
|
return eq(op, other.op) && eq(parentPath, other.parentPath) && eq(name, other.name) &&
|
||||||
|
eq(newParentPath, other.newParentPath) && eq(newName, other.newName);
|
||||||
|
}
|
||||||
|
|
||||||
String op;
|
String op;
|
||||||
String parentPath;
|
String parentPath;
|
||||||
String name;
|
String name;
|
||||||
|
@ -1024,18 +972,8 @@ class MyEvent {
|
||||||
this.newName = newName;
|
this.newName = newName;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object obj) {
|
|
||||||
MyEvent other = (MyEvent) obj;
|
|
||||||
return eq(op, other.op) && eq(parentPath, other.parentPath) && eq(name, other.name) &&
|
|
||||||
eq(newParentPath, other.newParentPath) && eq(newName, other.newName);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean eq(String s1, String s2) {
|
private boolean eq(String s1, String s2) {
|
||||||
if (s1 == null) {
|
return Objects.equals(s1, s2);
|
||||||
return s2 == null;
|
|
||||||
}
|
|
||||||
return s1.equals(s2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue