Revision: 3982
Author: [email protected]
Date: Tue Nov 9 13:05:03 2010
Log: Adding the ability to use an executor for pushing events to the
foreground. This is mainly used for testing but could prove useful if we
ever allow architect to be used in a headless mode.
http://code.google.com/p/power-architect/source/detail?r=3982
Modified:
/trunk/src/main/java/ca/sqlpower/architect/enterprise/ArchitectClientSideSession.java
=======================================
---
/trunk/src/main/java/ca/sqlpower/architect/enterprise/ArchitectClientSideSession.java
Wed Nov 3 12:00:08 2010
+++
/trunk/src/main/java/ca/sqlpower/architect/enterprise/ArchitectClientSideSession.java
Tue Nov 9 13:05:03 2010
@@ -14,6 +14,10 @@
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
import java.util.prefs.Preferences;
import javax.annotation.Nonnull;
@@ -80,9 +84,9 @@
import ca.sqlpower.util.DefaultUserPrompterFactory;
import ca.sqlpower.util.SQLPowerUtils;
import ca.sqlpower.util.TransactionEvent;
+import ca.sqlpower.util.UserPrompterFactory;
import ca.sqlpower.util.UserPrompter.UserPromptOptions;
import ca.sqlpower.util.UserPrompter.UserPromptResponse;
-import ca.sqlpower.util.UserPrompterFactory;
public class ArchitectClientSideSession extends ArchitectSessionImpl
implements RevisionController {
@@ -134,6 +138,25 @@
public static Map<String, ArchitectClientSideSession> securitySessions;
private AbstractPoolingSPListener deletionListener;
+
+ /**
+ * The executor to use as the foreground thread manager. If this is
not null
+ * and there is no EDT available this executor will be used to ensure
the
+ * system is single threaded.
+ */
+ private final ThreadPoolExecutor foregroundThreadExecutor;
+
+ /**
+ * This is false except for testing purposes when the single thread
executor
+ * may want to be used if the test is running headless.
+ */
+ private final boolean useThreadPool;
+
+ /**
+ * The thread used by {...@link #foregroundThreadExecutor} to keep
Architect
+ * single threaded if we are using the {...@link
#foregroundThreadExecutor}.
+ */
+ private Thread foregroundExecutorThread = null;
static {
securitySessions = new HashMap<String,
ArchitectClientSideSession>();
@@ -141,9 +164,37 @@
public ArchitectClientSideSession(ArchitectSessionContext context,
String name, ProjectLocation projectLocation) throws SQLObjectException
{
+ this(context, name, projectLocation, false);
+ }
+
+ /**
+ * This constructor is only used for testing. This constructor allows
users
+ * to specify an executor to use as the foreground thread instead of
using
+ * the normal EDT. This is handy for ensuring all of the events occur
on the
+ * correct thread and updates do not conflict with persists. If the
executor
+ * is null then the foreground thread will just execute the runnables
on the
+ * current thread.
+ */
+ public ArchitectClientSideSession(ArchitectSessionContext context,
+ String name, ProjectLocation projectLocation, boolean
useThreadPool) throws SQLObjectException {
super(context, name, new ArchitectSwingProject());
this.projectLocation = projectLocation;
+ this.useThreadPool = useThreadPool;
+ this.foregroundThreadExecutor = new ThreadPoolExecutor(1, 1, 5,
TimeUnit.MINUTES,
+ new LinkedBlockingQueue<Runnable>(), new ThreadFactory() {
+ @Override
+ public Thread newThread(Runnable r) {
+ if (foregroundExecutorThread == null) {
+ foregroundExecutorThread = new Thread(r);
+ return foregroundExecutorThread;
+ } else {
+ throw new RuntimeException("We only want to
make one thread. " +
+ "This should never be reached.");
+ }
+ }
+ });
+ foregroundThreadExecutor.allowCoreThreadTimeOut(false);
dataSourceCollectionUpdater = new
ArchitectDataSourceCollectionUpdater(projectLocation);
this.isEnterpriseSession = true;
@@ -502,10 +553,21 @@
// have a WabitSwingSession instead.
if (getContext() instanceof ArchitectSwingSessionContext) {
SwingUtilities.invokeLater(runner);
+ } else if (useThreadPool) {
+ foregroundThreadExecutor.execute(runner);
} else {
super.runInForeground(runner);
}
}
+
+ @Override
+ public boolean isForegroundThread() {
+ if (useThreadPool) {
+ return Thread.currentThread().equals(foregroundExecutorThread);
+ } else {
+ return super.isForegroundThread();
+ }
+ }
/**
* Exposes the shared cookie store so we don't spawn useless sessions