aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndré Wegmüller2013-01-22 11:10:16 (EST)
committerStephan Leicht Vogt2013-01-24 09:28:08 (EST)
commitaf8473fc78a42d5bd5bff1e6b6aeb31c0a76691f (patch)
treeb9c20c1b6a797b9ef04aa9269bbcd5b748bc6d59
parentb98075879af267758ba827a3687a5dae5883b05e (diff)
downloadorg.eclipse.scout.rt-af8473fc78a42d5bd5bff1e6b6aeb31c0a76691f.zip
org.eclipse.scout.rt-af8473fc78a42d5bd5bff1e6b6aeb31c0a76691f.tar.gz
org.eclipse.scout.rt-af8473fc78a42d5bd5bff1e6b6aeb31c0a76691f.tar.bz2
Bug 396252 - Multiple (Swing) Scout features in a single Eclipse
application Applies patch by André Wegmüller
-rw-r--r--org.eclipse.scout.rt.client/src/org/eclipse/scout/rt/client/AbstractClientSession.java2
-rw-r--r--org.eclipse.scout.rt.ui.swing.test.fragment/.classpath7
-rw-r--r--org.eclipse.scout.rt.ui.swing.test.fragment/META-INF/MANIFEST.MF12
-rw-r--r--org.eclipse.scout.rt.ui.swing.test.fragment/build.properties4
-rw-r--r--org.eclipse.scout.rt.ui.swing.test.fragment/src/org/eclipse/scout/rt/ui/swing/ExtensibleSwingApplicationTest.java162
-rw-r--r--org.eclipse.scout.rt.ui.swing/plugin.xml1
-rw-r--r--org.eclipse.scout.rt.ui.swing/schema/appextensions.exsd109
-rw-r--r--org.eclipse.scout.rt.ui.swing/src/org/eclipse/scout/rt/ui/swing/AbstractSwingApplication.java228
-rw-r--r--org.eclipse.scout.rt.ui.swing/src/org/eclipse/scout/rt/ui/swing/AbstractSwingApplicationExtension.java94
-rw-r--r--org.eclipse.scout.rt.ui.swing/src/org/eclipse/scout/rt/ui/swing/BaseSwingApplication.java244
-rw-r--r--org.eclipse.scout.rt.ui.swing/src/org/eclipse/scout/rt/ui/swing/ExtensibleSwingApplication.java211
-rw-r--r--org.eclipse.scout.rt.ui.swing/src/org/eclipse/scout/rt/ui/swing/extension/ISwingApplicationExtension.java83
-rw-r--r--org.eclipse.scout.rt.ui.swing/src/org/eclipse/scout/rt/ui/swing/extension/app/ISwingApplicationExtension.java58
-rw-r--r--org.eclipse.scout.rt.ui.swing/src/org/eclipse/scout/rt/ui/swing/extension/app/internal/ISwingApplicationExtensions.java70
-rw-r--r--org.eclipse.scout.rt.ui.swing/src/org/eclipse/scout/rt/ui/swing/extension/app/internal/SwingApplicationExtensions.java120
15 files changed, 1214 insertions, 191 deletions
diff --git a/org.eclipse.scout.rt.client/src/org/eclipse/scout/rt/client/AbstractClientSession.java b/org.eclipse.scout.rt.client/src/org/eclipse/scout/rt/client/AbstractClientSession.java
index 3f2c106..af3aaf6 100644
--- a/org.eclipse.scout.rt.client/src/org/eclipse/scout/rt/client/AbstractClientSession.java
+++ b/org.eclipse.scout.rt.client/src/org/eclipse/scout/rt/client/AbstractClientSession.java
@@ -265,6 +265,8 @@ public abstract class AbstractClientSession implements IClientSession {
}
}
+ // TODO AWE (swingAppExt) : ist es gewollt, dass diese Methode im Swing thread aufgerufen wird?
+
@ConfigOperation
@Order(10)
protected void execLoadSession() throws ProcessingException {
diff --git a/org.eclipse.scout.rt.ui.swing.test.fragment/.classpath b/org.eclipse.scout.rt.ui.swing.test.fragment/.classpath
new file mode 100644
index 0000000..098194c
--- /dev/null
+++ b/org.eclipse.scout.rt.ui.swing.test.fragment/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/org.eclipse.scout.rt.ui.swing.test.fragment/META-INF/MANIFEST.MF b/org.eclipse.scout.rt.ui.swing.test.fragment/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..527f049
--- /dev/null
+++ b/org.eclipse.scout.rt.ui.swing.test.fragment/META-INF/MANIFEST.MF
@@ -0,0 +1,12 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Fragment
+Bundle-SymbolicName: org.eclipse.scout.rt.ui.swing.test.fragment;singleton:=true
+Bundle-Version: 3.9.0.qualifier
+Bundle-Vendor: Eclipse Scout Project
+Fragment-Host: org.eclipse.scout.rt.ui.swing
+Bundle-RequiredExecutionEnvironment: JavaSE-1.7
+Require-Bundle: org.easymock,
+ org.junit,
+ org.eclipse.scout.rt.testing.client,
+ org.eclipse.scout.rt.testing.shared
diff --git a/org.eclipse.scout.rt.ui.swing.test.fragment/build.properties b/org.eclipse.scout.rt.ui.swing.test.fragment/build.properties
new file mode 100644
index 0000000..34d2e4d
--- /dev/null
+++ b/org.eclipse.scout.rt.ui.swing.test.fragment/build.properties
@@ -0,0 +1,4 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .
diff --git a/org.eclipse.scout.rt.ui.swing.test.fragment/src/org/eclipse/scout/rt/ui/swing/ExtensibleSwingApplicationTest.java b/org.eclipse.scout.rt.ui.swing.test.fragment/src/org/eclipse/scout/rt/ui/swing/ExtensibleSwingApplicationTest.java
new file mode 100644
index 0000000..d62f4c1
--- /dev/null
+++ b/org.eclipse.scout.rt.ui.swing.test.fragment/src/org/eclipse/scout/rt/ui/swing/ExtensibleSwingApplicationTest.java
@@ -0,0 +1,162 @@
+/**
+ *
+ */
+package org.eclipse.scout.rt.ui.swing;
+
+import static org.junit.Assert.assertEquals;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.verify;
+import static org.easymock.EasyMock.replay;
+
+import java.util.Arrays;
+import java.util.Collections;
+
+import org.easymock.EasyMock;
+import org.eclipse.equinox.app.IApplication;
+import org.eclipse.scout.rt.client.IClientSession;
+import org.eclipse.scout.rt.ui.swing.extension.ISwingApplicationExtension;
+import org.junit.Test;
+
+/**
+ * @author awe
+ *
+ */
+public class ExtensibleSwingApplicationTest {
+
+ ExtensibleSwingApplication app;
+
+ @Test
+ public void testStop() {
+ ISwingApplicationExtension ext = EasyMock.createMock(ISwingApplicationExtension.class);
+ IClientSession cs = EasyMock.createMock(IClientSession.class);
+ expect(ext.getClientSession()).andReturn(cs);
+ cs.stopSession();
+ EasyMock.expectLastCall();
+ replay(ext, cs);
+ app = new ExtensibleSwingApplication(Collections.singletonList(ext));
+
+ app.stop();
+ verify(ext, cs);
+ }
+
+ /**
+ * When one of the extensions returns anything other than null, start() is aborted.
+ * @throws Exception
+ */
+ @Test
+ public void testStart_ExtensionAbort() throws Exception {
+ assertStart(IApplication.EXIT_OK, null, IApplication.EXIT_OK);
+ assertStart(IApplication.EXIT_RELAUNCH, IApplication.EXIT_RELAUNCH, IApplication.EXIT_OK);
+ // assertStart(?, null, null) causes the super.start() method to be called
+ // which results in a call to startSubject. These cases are testet in the testStartInSubject* methods
+ }
+
+ private void assertStart(Object expected, Object firstReturnValue, Object secondReturnValue) throws Exception {
+ ISwingApplicationExtension ext1 = EasyMock.createMock(ISwingApplicationExtension.class);
+ ISwingApplicationExtension ext2 = EasyMock.createMock(ISwingApplicationExtension.class);
+ expect(ext1.execStart(null, null)).andReturn(firstReturnValue);
+ expect(ext2.execStart(null, null)).andReturn(secondReturnValue);
+ replay(ext1, ext2);
+ app = new ExtensibleSwingApplication(Arrays.asList(ext1, ext2));
+ assertEquals(expected, app.start(null));
+ }
+
+ @Test
+ public void testStartInSubject_Abort() throws Exception {
+ ISwingApplicationExtension ext = EasyMock.createMock(ISwingApplicationExtension.class);
+ expect(ext.execStartInSubject(null, null)).andReturn(IApplication.EXIT_OK);
+ replay(ext);
+ app = new ExtensibleSwingApplication(Collections.singletonList(ext));
+ assertEquals(IApplication.EXIT_OK, app.startInSubject(null));
+ }
+
+ /*
+
+ Cannot test this because showLoadError() opens a Swing message box which blocks the application.
+
+ @Test
+ public void testStartInSubject_CheckClientSession_Abort() throws Exception {
+ ISwingApplicationExtension ext = EasyMock.createMock(ISwingApplicationExtension.class);
+ IClientSession cs = EasyMock.createMock(IClientSession.class);
+ expect(ext.execStartInSubject(null, null)).andReturn(null);
+ expect(ext.getClientSession()).andReturn(cs);
+ expect(cs.isActive()).andReturn(false);
+ expect(cs.getLoadError()).andReturn(new IOException());
+ replay(ext, cs);
+ app = new ExtensibleSwingApplication(Collections.singletonList(ext));
+ assertEquals(IApplication.EXIT_OK, app.startInSubject(null));
+ verify(ext, cs);
+ }
+ */
+
+ @Test
+ public void testStartGUI() throws Exception {
+ ISwingApplicationExtension ext = EasyMock.createMock(ISwingApplicationExtension.class);
+ ISwingEnvironment env = EasyMock.createMock(ISwingEnvironment.class);
+ IClientSession cs = EasyMock.createMock(IClientSession.class);
+ expect(ext.getEnvironment()).andReturn(env);
+ expect(ext.getClientSession()).andReturn(cs);
+ env.showGUI(cs);
+ EasyMock.expectLastCall();
+ replay(ext, env, cs);
+ app = new ExtensibleSwingApplication(Collections.singletonList(ext));
+ app.startGUI();
+ verify(ext, env, cs);
+ }
+
+ @Test
+ public void testRunWhileActive_NoExtensions() throws Exception {
+ app = new ExtensibleSwingApplication(Collections.<ISwingApplicationExtension>emptyList());
+ assertEquals(IApplication.EXIT_OK, Integer.valueOf(app.runWhileActive()));
+ }
+
+ /**
+ * This test requires multiple threads. First we lookup for an active client session.
+ * Than the main thread waits on the lock object of this client session. We need a second thread
+ * to call the notifyAll() method on that lock object a bit later. After that call the session is
+ * not active anymore (the 3rd call for isActive() does return false). So the application should
+ * terminate with the exit code of the last session that was terminated.
+ * @throws Exception
+ */
+ @Test
+ public void testRunWhileActive_OneExtension() throws Exception {
+ final Object stateLock = new Object();
+ ISwingApplicationExtension ext = EasyMock.createMock(ISwingApplicationExtension.class);
+ IClientSession cs = EasyMock.createMock(IClientSession.class);
+ expect(ext.getClientSession()).andReturn(cs).anyTimes();
+ expect(cs.isActive()).andReturn(true).times(2);
+ expect(cs.isActive()).andReturn(false);
+ expect(cs.getStateLock()).andReturn(stateLock).anyTimes();
+ expect(cs.getExitCode()).andReturn(IApplication.EXIT_RELAUNCH);
+ replay(ext, cs);
+
+ Thread t = new Thread() {
+ @Override
+ public void run() {
+ try {
+ Thread.sleep(250);
+ } catch (InterruptedException e) {
+ }
+ synchronized (stateLock) {
+ stateLock.notifyAll();
+ }
+ }
+ };
+ t.start();
+
+ app = new ExtensibleSwingApplication(Collections.singletonList(ext));
+ assertEquals(IApplication.EXIT_RELAUNCH, Integer.valueOf(app.runWhileActive()));
+ }
+
+ @Test
+ public void testInitializeSwing() throws Exception {
+ ISwingApplicationExtension ext = EasyMock.createMock(ISwingApplicationExtension.class);
+ ext.initializeSwing();
+ EasyMock.expectLastCall();
+ replay(ext);
+ app = new ExtensibleSwingApplication(Collections.singletonList(ext));
+ app.initializeSwing();
+ verify(ext);
+ }
+
+}
diff --git a/org.eclipse.scout.rt.ui.swing/plugin.xml b/org.eclipse.scout.rt.ui.swing/plugin.xml
index 0220d53..6768038 100644
--- a/org.eclipse.scout.rt.ui.swing/plugin.xml
+++ b/org.eclipse.scout.rt.ui.swing/plugin.xml
@@ -3,6 +3,7 @@
<plugin>
<extension-point id="org.eclipse.scout.rt.ui.swing.formfields" name="Form Fields" schema="schema/formfields.exsd"/>
<extension-point id="org.eclipse.scout.rt.ui.swing.scouticons" name="Scout Icons" schema="schema/scouticons.exsd"/>
+ <extension-point schema="schema/appextensions.exsd" name="Swing Application Extensions" id="org.eclipse.scout.rt.ui.swing.appextensions"/>
<extension
point="org.eclipse.scout.rt.ui.swing.formfields">
<formField
diff --git a/org.eclipse.scout.rt.ui.swing/schema/appextensions.exsd b/org.eclipse.scout.rt.ui.swing/schema/appextensions.exsd
new file mode 100644
index 0000000..11c9fa6
--- /dev/null
+++ b/org.eclipse.scout.rt.ui.swing/schema/appextensions.exsd
@@ -0,0 +1,109 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!-- Schema file written by PDE -->
+<schema targetNamespace="org.eclipse.scout.rt.ui.swing" xmlns="http://www.w3.org/2001/XMLSchema">
+<annotation>
+ <appInfo>
+ <meta.schema plugin="org.eclipse.scout.rt.ui.swing" id="appextensions" name="Swing Application Extensions"/>
+ </appInfo>
+ <documentation>
+ This extension-point is used to contribute swing application extensions (SAE). A SAE allows to use multiple scout applications in a single Eclipse application (.product). Each SAE has its own client session, swing environment and desktop. Communication between different scout applications is done by interface-services. Since each SAE has its own client session, a call is always executed in the right thread.
+ </documentation>
+ </annotation>
+
+ <element name="extension">
+ <annotation>
+ <appInfo>
+ <meta.element />
+ </appInfo>
+ </annotation>
+ <complexType>
+ <sequence>
+ <element ref="swingApplicationExtension"/>
+ </sequence>
+ <attribute name="point" type="string" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="id" type="string">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="name" type="string">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ <appInfo>
+ <meta.attribute translatable="true"/>
+ </appInfo>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <element name="swingApplicationExtension">
+ <complexType>
+ <attribute name="class" type="string" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ <appInfo>
+ <meta.attribute kind="java" basedOn=":org.eclipse.scout.rt.ui.swing.ISwingApplicationExtension"/>
+ </appInfo>
+ </annotation>
+ </attribute>
+ <attribute name="ranking" type="string">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="since"/>
+ </appInfo>
+ <documentation>
+ [Enter the first release in which this extension point appears.]
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="examples"/>
+ </appInfo>
+ <documentation>
+ [Enter extension point usage example here.]
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="apiinfo"/>
+ </appInfo>
+ <documentation>
+ [Enter API information here.]
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="implementation"/>
+ </appInfo>
+ <documentation>
+ [Enter information about supplied implementation of this extension point.]
+ </documentation>
+ </annotation>
+
+
+</schema>
diff --git a/org.eclipse.scout.rt.ui.swing/src/org/eclipse/scout/rt/ui/swing/AbstractSwingApplication.java b/org.eclipse.scout.rt.ui.swing/src/org/eclipse/scout/rt/ui/swing/AbstractSwingApplication.java
index 5a8dbf9..911f21f 100644
--- a/org.eclipse.scout.rt.ui.swing/src/org/eclipse/scout/rt/ui/swing/AbstractSwingApplication.java
+++ b/org.eclipse.scout.rt.ui.swing/src/org/eclipse/scout/rt/ui/swing/AbstractSwingApplication.java
@@ -10,201 +10,38 @@
******************************************************************************/
package org.eclipse.scout.rt.ui.swing;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.PrintStream;
-import java.net.URL;
-import java.security.AccessController;
-import java.security.PrivilegedExceptionAction;
-import java.util.Hashtable;
-import java.util.Locale;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import javax.security.auth.Subject;
-import javax.swing.JOptionPane;
-import javax.swing.SwingUtilities;
-
-import org.eclipse.core.runtime.IProgressMonitor;
-import org.eclipse.core.runtime.Platform;
-import org.eclipse.equinox.app.IApplication;
import org.eclipse.equinox.app.IApplicationContext;
-import org.eclipse.scout.commons.LocaleUtility;
-import org.eclipse.scout.commons.StringUtility;
-import org.eclipse.scout.commons.logger.IScoutLogger;
-import org.eclipse.scout.commons.logger.ScoutLogManager;
-import org.eclipse.scout.commons.prefs.UserScope;
-import org.eclipse.scout.commons.security.SimplePrincipal;
import org.eclipse.scout.rt.client.IClientSession;
-import org.eclipse.scout.rt.client.services.common.exceptionhandler.ErrorHandler;
-import org.eclipse.scout.rt.client.services.common.exceptionhandler.UserInterruptedException;
-import org.eclipse.scout.rt.shared.ui.UiDeviceType;
-import org.eclipse.scout.rt.shared.ui.UiLayer;
-import org.eclipse.scout.rt.shared.ui.UserAgent;
-import org.eclipse.scout.rt.ui.swing.ext.job.SwingProgressHandler;
-import org.eclipse.scout.rt.ui.swing.splash.SplashProgressMonitor;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.ServiceRegistration;
-import org.osgi.framework.Version;
-public abstract class AbstractSwingApplication implements IApplication {
- private static final IScoutLogger LOG = ScoutLogManager.getLogger(AbstractSwingApplication.class);
+/**
+ * This class is the base class for a typical Scout Swing application. See <code>ExtensibleSwingApplication</code> when
+ * you must run multiple Scout Swing features in a single eclipse application.
+ *
+ * @see ExtensibleSwingApplication
+ * @author awe
+ */
+public abstract class AbstractSwingApplication extends BaseSwingApplication {
private ISwingEnvironment m_env;
- private SplashProgressMonitor m_monitor;
- private ServiceRegistration m_monitorReg;
-
- public AbstractSwingApplication() {
- if (!Platform.inDevelopmentMode()) {
- try {
- URL instArea = Platform.getInstanceLocation().getURL();
- File sysoutRedirector = new File(instArea.getFile(), System.getProperty("user.name", "anonymous") + "-sysout.log");
- File syserrRedirector = new File(instArea.getFile(), System.getProperty("user.name", "anonymous") + "-syserr.log");
- sysoutRedirector.getParentFile().mkdirs();
- syserrRedirector.getParentFile().mkdirs();
- System.setOut(new PrintStream(new FileOutputStream(sysoutRedirector, true)));
- System.setErr(new PrintStream(new FileOutputStream(syserrRedirector, true)));
- }
- catch (Throwable t) {
- // nop
- }
- }
- // check jre>=1.6.0
- Matcher m = Pattern.compile("([0-9]+\\.[0-9]+\\.[0-9]+)(_.*)?").matcher(System.getProperty("java.version", "0.0.0_00"));
- if (m.matches() && new Version(m.group(1)).compareTo(new Version(1, 6, 0)) < 0) {
- System.out.println("Swing requires at least Java 1.6.0 Current is " + m.group(1));
- System.exit(-1);
- }
- try {
- // The default @{link Locale} has to be set prior to SwingEnvironment is created, because UIDefaultsInjector resolves NLS texts.
- execInitLocale();
-
- //attach default job handler
- SwingProgressHandler.install();
-
- SwingUtilities.invokeAndWait(
- new Runnable() {
- @Override
- public void run() {
- m_env = createSwingEnvironment();
- }
- }
- );
- }
- catch (Exception e) {
- LOG.warn(null, e);
- System.exit(0);
- }
- m_monitor = new SplashProgressMonitor(m_env, showSplashScreenProgressInPercentage());
- // register progress as osgi service
- if (Platform.getProduct() != null && Platform.getProduct().getDefiningBundle() != null) {
- BundleContext ctx = Platform.getProduct().getDefiningBundle().getBundleContext();
- m_monitorReg = ctx.registerService(IProgressMonitor.class.getName(), m_monitor, new Hashtable<String, Object>());
- }
- m_monitor.showSplash();
- }
-
- protected ISwingEnvironment createSwingEnvironment() {
- return new DefaultSwingEnvironment();
- }
-
- /**
- * @return Returns <code>true</code> if the splash screen shows the current status in percentage as well (if required
- * data is available). Default is <code>false</code>.
- */
- protected boolean showSplashScreenProgressInPercentage() {
- return false;
- }
protected abstract IClientSession getClientSession();
- public ISwingEnvironment getSwingEnvironment() {
- return m_env;
- }
-
- public final IProgressMonitor getProgressMonitor() {
- return m_monitor;
+ public AbstractSwingApplication() {
+ initialize();
}
- /**
- * This abstract template application creates a JAAS subject based on the system property "user.name"
- * and supports for initializing the {@link Locale} in {@link #execInitLocale()}
- * <p>
- * The start is then delegated to {@link #startInSubject(IApplicationContext)}
- * <p>
- * Normally {@link #startInSubject(IApplicationContext)} is overriden
- */
@Override
- public Object start(final IApplicationContext context) throws Exception {
- if (Subject.getSubject(AccessController.getContext()) != null) {
- //there is a subject context
- return exit(startInSubject(context));
- }
- else {
- Subject subject = new Subject();
- subject.getPrincipals().add(new SimplePrincipal(System.getProperty("user.name")));
- return Subject.doAs(subject, new PrivilegedExceptionAction<Object>() {
- @Override
- public Object run() throws Exception {
- return exit(startInSubject(context));
- }
- });
- }
- }
-
- /**
- * Exit delegate to handle os-specific exit behaviour.
- * <p>
- * Mac OS X normally only closes the window, but we want to close the app (with Quit).
- */
- protected Object exit(Object code) {
- if (Platform.OS_MACOSX.equals(Platform.getOS())) {
- System.exit(0);
- }
- return code;
- }
-
- protected void execInitLocale() {
- Locale locale = LocaleUtility.parse(new UserScope().getNode(org.eclipse.scout.rt.client.Activator.PLUGIN_ID).get("locale", null));
- if (locale != null) {
- Locale.setDefault(locale);
- }
- }
-
- protected UserAgent initUserAgent() {
- return UserAgent.create(UiLayer.SWING, UiDeviceType.DESKTOP);
- }
-
protected Object startInSubject(IApplicationContext context) throws Exception {
- final IClientSession clientSession = getClientSession();
- if (!clientSession.isActive()) {
- showLoadError(clientSession.getLoadError());
+ if (!isClientSessionValid(getClientSession())) {
return EXIT_OK;
}
- // Postcondition: session is active and loaded
- context.applicationRunning();
- if (m_monitorReg != null) {
- m_monitorReg.unregister();
- }
- m_monitor.done();
- m_monitor = null;
- try {
- SwingUtilities.invokeAndWait(
- new Runnable() {
- @Override
- public void run() {
- //start gui
- m_env.showGUI(clientSession);
- execSwingStarted(clientSession);
- }
- }
- );
- }
- catch (Exception e) {
- LOG.warn(null, e);
- System.exit(0);
- }
+ return super.startInSubject(context);
+ }
+
+ @Override
+ int runWhileActive() throws InterruptedException {
while (true) {
+ IClientSession clientSession = getClientSession();
synchronized (clientSession.getStateLock()) {
if (clientSession.isActive()) {
clientSession.getStateLock().wait();
@@ -216,20 +53,15 @@ public abstract class AbstractSwingApplication implements IApplication {
}
}
- protected void showLoadError(Throwable error) {
- ErrorHandler handler = new ErrorHandler(error);
- if (!(handler.getText().indexOf(UserInterruptedException.class.getSimpleName()) >= 0)) {
- SwingUtility.showMessageDialogSynthCapable(
- null,
- StringUtility.join("\n\n", handler.getText(), handler.getDetail()),
- handler.getTitle(),
- JOptionPane.ERROR_MESSAGE
- );
- }
+ @Override
+ void startGUI() {
+ IClientSession clientSession = getClientSession();
+ m_env.showGUI(clientSession);
+ execSwingStarted(clientSession);
}
/**
- * Called just after the application model was created and is showing in the gui .
+ * Called just after the application model was created and is showing in the gui.
* <p>
* This method is called in the swing thread.
*/
@@ -237,6 +69,20 @@ public abstract class AbstractSwingApplication implements IApplication {
}
@Override
+ ISwingEnvironment getSwingEnvironment() {
+ return m_env;
+ }
+
+ @Override
+ void initializeSwing() {
+ m_env = createSwingEnvironment();
+ }
+
+ protected ISwingEnvironment createSwingEnvironment() {
+ return new DefaultSwingEnvironment();
+ }
+
+ @Override
public void stop() {
getClientSession().stopSession();
}
diff --git a/org.eclipse.scout.rt.ui.swing/src/org/eclipse/scout/rt/ui/swing/AbstractSwingApplicationExtension.java b/org.eclipse.scout.rt.ui.swing/src/org/eclipse/scout/rt/ui/swing/AbstractSwingApplicationExtension.java
new file mode 100644
index 0000000..fb267c3
--- /dev/null
+++ b/org.eclipse.scout.rt.ui.swing/src/org/eclipse/scout/rt/ui/swing/AbstractSwingApplicationExtension.java
@@ -0,0 +1,94 @@
+/*******************************************************************************
+ * Copyright (c) 2010 BSI Business Systems Integration AG.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * BSI Business Systems Integration AG - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.scout.rt.ui.swing;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.equinox.app.IApplicationContext;
+import org.eclipse.scout.rt.client.IClientSession;
+import org.eclipse.scout.rt.client.ui.desktop.IDesktop;
+import org.eclipse.scout.rt.shared.ui.UiDeviceType;
+import org.eclipse.scout.rt.shared.ui.UiLayer;
+import org.eclipse.scout.rt.shared.ui.UserAgent;
+import org.eclipse.scout.rt.ui.swing.extension.ISwingApplicationExtension;
+
+/**
+ * Abstract base class for swing application extensions.
+ *
+ * @author awe
+ */
+public abstract class AbstractSwingApplicationExtension implements ISwingApplicationExtension {
+
+ private String m_extensionId;
+
+ private IClientSession m_clientSession;
+
+ private ISwingEnvironment m_environment;
+
+ public AbstractSwingApplicationExtension(String extensionId) {
+ m_extensionId = extensionId;
+ }
+
+ @Override
+ public String getExtensionId() {
+ return m_extensionId;
+ }
+
+ @Override
+ public void initializeSwing() {
+ m_environment = createEnvironment();
+ }
+
+ private UserAgent getUserAgent() {
+ return UserAgent.create(UiLayer.SWING, UiDeviceType.DESKTOP);
+ }
+
+ /**
+ * Implement to provide a project specific client session instance.
+ *
+ * @param userAgent
+ * @return
+ */
+ abstract protected IClientSession createClientSession(UserAgent userAgent);
+
+ /**
+ * Implement to provide a project specific Swing environment.
+ *
+ * @return
+ */
+ abstract protected ISwingEnvironment createEnvironment();
+
+ @Override
+ public Object execStart(IApplicationContext context, IProgressMonitor progressMonitor) throws Exception {
+ return null;
+ }
+
+ @Override
+ public Object execStartInSubject(IApplicationContext context, IProgressMonitor progressMonitor) throws Exception {
+ m_clientSession = createClientSession(getUserAgent());
+ return null;
+ }
+
+ @Override
+ public IClientSession getClientSession() {
+ return m_clientSession;
+ }
+
+ @Override
+ public IDesktop getDesktop() {
+ return m_clientSession.getDesktop();
+ }
+
+ @Override
+ public ISwingEnvironment getEnvironment() {
+ return m_environment;
+ }
+
+}
diff --git a/org.eclipse.scout.rt.ui.swing/src/org/eclipse/scout/rt/ui/swing/BaseSwingApplication.java b/org.eclipse.scout.rt.ui.swing/src/org/eclipse/scout/rt/ui/swing/BaseSwingApplication.java
new file mode 100644
index 0000000..e050e59
--- /dev/null
+++ b/org.eclipse.scout.rt.ui.swing/src/org/eclipse/scout/rt/ui/swing/BaseSwingApplication.java
@@ -0,0 +1,244 @@
+/*******************************************************************************
+ * Copyright (c) 2010 BSI Business Systems Integration AG.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * BSI Business Systems Integration AG - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.scout.rt.ui.swing;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.PrintStream;
+import java.net.URL;
+import java.security.AccessController;
+import java.security.PrivilegedExceptionAction;
+import java.util.Hashtable;
+import java.util.Locale;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.security.auth.Subject;
+import javax.swing.JOptionPane;
+import javax.swing.SwingUtilities;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.equinox.app.IApplication;
+import org.eclipse.equinox.app.IApplicationContext;
+import org.eclipse.scout.commons.LocaleUtility;
+import org.eclipse.scout.commons.StringUtility;
+import org.eclipse.scout.commons.logger.IScoutLogger;
+import org.eclipse.scout.commons.logger.ScoutLogManager;
+import org.eclipse.scout.commons.prefs.UserScope;
+import org.eclipse.scout.commons.security.SimplePrincipal;
+import org.eclipse.scout.rt.client.IClientSession;
+import org.eclipse.scout.rt.client.services.common.exceptionhandler.ErrorHandler;
+import org.eclipse.scout.rt.client.services.common.exceptionhandler.UserInterruptedException;
+import org.eclipse.scout.rt.shared.ui.UiDeviceType;
+import org.eclipse.scout.rt.shared.ui.UiLayer;
+import org.eclipse.scout.rt.shared.ui.UserAgent;
+import org.eclipse.scout.rt.ui.swing.ext.job.SwingProgressHandler;
+import org.eclipse.scout.rt.ui.swing.splash.SplashProgressMonitor;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.framework.Version;
+
+/**
+ * This abstract class is the base class for all Scout Swing (Eclipse) applications.
+ *
+ * @author awe
+ */
+abstract class BaseSwingApplication implements IApplication {
+
+ private static final IScoutLogger LOG = ScoutLogManager.getLogger(AbstractSwingApplication.class);
+
+ private SplashProgressMonitor m_monitor;
+
+ private ServiceRegistration m_monitorReg;
+
+ void initialize() {
+ if (!Platform.inDevelopmentMode()) {
+ try {
+ URL instArea = Platform.getInstanceLocation().getURL();
+ File sysoutRedirector = new File(instArea.getFile(), System.getProperty("user.name", "anonymous") + "-sysout.log");
+ File syserrRedirector = new File(instArea.getFile(), System.getProperty("user.name", "anonymous") + "-syserr.log");
+ sysoutRedirector.getParentFile().mkdirs();
+ syserrRedirector.getParentFile().mkdirs();
+ System.setOut(new PrintStream(new FileOutputStream(sysoutRedirector, true)));
+ System.setErr(new PrintStream(new FileOutputStream(syserrRedirector, true)));
+ }
+ catch (Throwable t) {
+ // nop
+ }
+ }
+ // check jre>=1.6.0
+ Matcher m = Pattern.compile("([0-9]+\\.[0-9]+\\.[0-9]+)(_.*)?").matcher(System.getProperty("java.version", "0.0.0_00"));
+ if (m.matches() && new Version(m.group(1)).compareTo(new Version(1, 6, 0)) < 0) {
+ System.out.println("Swing requires at least Java 1.6.0 Current is " + m.group(1));
+ System.exit(-1);
+ }
+ try {
+ // The default @{link Locale} has to be set prior to SwingEnvironment is created, because UIDefaultsInjector resolves NLS texts.
+ execInitLocale();
+
+ //attach default job handler
+ SwingProgressHandler.install();
+
+ SwingUtilities.invokeAndWait(
+ new Runnable() {
+ @Override
+ public void run() {
+ initializeSwing();
+ }
+ }
+ );
+ }
+ catch (Exception e) {
+ LOG.warn(null, e);
+ System.exit(0);
+ }
+ m_monitor = new SplashProgressMonitor(getSwingEnvironment(), showSplashScreenProgressInPercentage());
+ // register progress as osgi service
+ if (Platform.getProduct() != null && Platform.getProduct().getDefiningBundle() != null) {
+ BundleContext ctx = Platform.getProduct().getDefiningBundle().getBundleContext();
+ m_monitorReg = ctx.registerService(IProgressMonitor.class.getName(), m_monitor, new Hashtable<String, Object>());
+ }
+ m_monitor.showSplash();
+ }
+
+ /**
+ * This method is used to perform initialization in the Swing thread.
+ */
+ abstract void initializeSwing();
+
+ /**
+ * Returns the Swing environment used to display the splash screen.
+ *
+ * @return
+ */
+ abstract ISwingEnvironment getSwingEnvironment();
+
+ /**
+ * @return Returns <code>true</code> if the splash screen shows the current status in percentage as well (if required
+ * data is available). Default is <code>false</code>.
+ */
+ protected boolean showSplashScreenProgressInPercentage() {
+ return false;
+ }
+
+ public final IProgressMonitor getProgressMonitor() {
+ return m_monitor;
+ }
+
+ /**
+ * This abstract template application creates a JAAS subject based on the system property "user.name"
+ * and supports for initializing the {@link Locale} in {@link #execInitLocale()}
+ * <p>
+ * The start is then delegated to {@link #startInSubject(IApplicationContext)}
+ * <p>
+ * Normally {@link #startInSubject(IApplicationContext)} is overriden
+ */
+ @Override
+ public Object start(final IApplicationContext context) throws Exception {
+ if (Subject.getSubject(AccessController.getContext()) != null) {
+ // there is a subject context
+ return exit(startInSubject(context));
+ }
+ else {
+ Subject subject = new Subject();
+ subject.getPrincipals().add(new SimplePrincipal(System.getProperty("user.name")));
+ return Subject.doAs(subject, new PrivilegedExceptionAction<Object>() {
+ @Override
+ public Object run() throws Exception {
+ return exit(startInSubject(context));
+ }
+ });
+ }
+ }
+
+ /**
+ * Exit delegate to handle os-specific exit behaviour.
+ * <p>
+ * Mac OS X normally only closes the window, but we want to close the app (with Quit).
+ */
+ protected Object exit(Object code) {
+ if (Platform.OS_MACOSX.equals(Platform.getOS())) {
+ System.exit(0);
+ }
+ return code;
+ }
+
+ protected void execInitLocale() {
+ Locale locale = LocaleUtility.parse(new UserScope().getNode(org.eclipse.scout.rt.client.Activator.PLUGIN_ID).get("locale", null));
+ if (locale != null) {
+ Locale.setDefault(locale);
+ }
+ }
+
+ protected UserAgent initUserAgent() {
+ return UserAgent.create(UiLayer.SWING, UiDeviceType.DESKTOP);
+ }
+
+ boolean isClientSessionValid(IClientSession clientSession) {
+ if (clientSession.isActive()) {
+ return true;
+ }
+ showLoadError(clientSession.getLoadError());
+ return false;
+ }
+
+ Object startInSubject(IApplicationContext context) throws Exception {
+ // Post-condition: session is active and loaded
+ context.applicationRunning();
+ if (m_monitorReg != null) {
+ m_monitorReg.unregister();
+ }
+ m_monitor.done();
+ m_monitor = null;
+ try {
+ SwingUtilities.invokeAndWait(
+ new Runnable() {
+ @Override
+ public void run() {
+ startGUI();
+ }
+ }
+ );
+ }
+ catch (Exception e) {
+ LOG.warn(null, e);
+ System.exit(0);
+ }
+ return runWhileActive();
+ }
+
+ /**
+ * This method blocks the main thread as long as there is an active client session.
+ *
+ * @return
+ * @throws InterruptedException
+ */
+ abstract int runWhileActive() throws InterruptedException;
+
+ /**
+ * This method starts the GUI.
+ */
+ abstract void startGUI();
+
+ protected void showLoadError(Throwable error) {
+ ErrorHandler handler = new ErrorHandler(error);
+ if (!(handler.getText().indexOf(UserInterruptedException.class.getSimpleName()) >= 0)) {
+ SwingUtility.showMessageDialogSynthCapable(
+ null,
+ StringUtility.join("\n\n", handler.getText(), handler.getDetail()),
+ handler.getTitle(),
+ JOptionPane.ERROR_MESSAGE
+ );
+ }
+ }
+
+}
diff --git a/org.eclipse.scout.rt.ui.swing/src/org/eclipse/scout/rt/ui/swing/ExtensibleSwingApplication.java b/org.eclipse.scout.rt.ui.swing/src/org/eclipse/scout/rt/ui/swing/ExtensibleSwingApplication.java
new file mode 100644
index 0000000..541f0bf
--- /dev/null
+++ b/org.eclipse.scout.rt.ui.swing/src/org/eclipse/scout/rt/ui/swing/ExtensibleSwingApplication.java
@@ -0,0 +1,211 @@
+/*******************************************************************************
+ * Copyright (c) 2010 BSI Business Systems Integration AG.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * BSI Business Systems Integration AG - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.scout.rt.ui.swing;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExtension;
+import org.eclipse.core.runtime.IExtensionPoint;
+import org.eclipse.core.runtime.IExtensionRegistry;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.equinox.app.IApplication;
+import org.eclipse.equinox.app.IApplicationContext;
+import org.eclipse.scout.commons.logger.IScoutLogger;
+import org.eclipse.scout.commons.logger.ScoutLogManager;
+import org.eclipse.scout.rt.client.IClientSession;
+import org.eclipse.scout.rt.ui.swing.extension.ISwingApplicationExtension;
+
+/**
+ * <p>
+ * Extends the AbstractSwingApplication and provides support for multiple Swing-Scout applications in a single Eclipse
+ * application. This class cannot be extended. Use the ISwingApplicationExtension capabilities instead.
+ * </p>
+ * <p>
+ * Use the ranking attribute of the extension point <code>org.eclipse.scout.rt.ui.swing.appextensions</code> to
+ * configure the order of the contributed extensions. The extension with the highest ranking is used as
+ * default-extension, which provides the swing-environment used to display the splash-screen.
+ * </p>
+ *
+ * @author awe
+ */
+final public class ExtensibleSwingApplication extends BaseSwingApplication {
+
+ private static class P_RankedExtension implements Comparable<P_RankedExtension> {
+
+ int ranking;
+
+ ISwingApplicationExtension extension;
+
+ public P_RankedExtension(int ranking, ISwingApplicationExtension extension) {
+ this.ranking = ranking;
+ this.extension = extension;
+ }
+
+ @Override
+ public int compareTo(P_RankedExtension o) {
+ return o.ranking - ranking;
+ }
+
+ }
+
+ private static final String EXTENSION_POINT = Activator.PLUGIN_ID + ".appextensions";
+
+ private static final IScoutLogger LOG = ScoutLogManager.getLogger(ExtensibleSwingApplication.class);
+
+ private List<ISwingApplicationExtension> m_extensions = new ArrayList<ISwingApplicationExtension>();
+
+ private ISwingApplicationExtension defaultExtension;
+
+ public ExtensibleSwingApplication() {
+ readExtensionPoint();
+ initialize();
+ }
+
+ /**
+ * This constructor should be only used for unit testing.
+ *
+ * @param extensions
+ */
+ ExtensibleSwingApplication(List<ISwingApplicationExtension> extensions) {
+ m_extensions = extensions;
+ }
+
+ private void readExtensionPoint() {
+ List<P_RankedExtension> rankedExtensions = new ArrayList<P_RankedExtension>();
+ Set<String> extensionIdSet = new HashSet<String>();
+
+ IExtensionRegistry registry = Platform.getExtensionRegistry();
+ IExtensionPoint xp = registry.getExtensionPoint(EXTENSION_POINT);
+ for (IExtension extension : xp.getExtensions()) {
+ for (IConfigurationElement element : extension.getConfigurationElements()) {
+ try {
+ ISwingApplicationExtension swingAppExtension = (ISwingApplicationExtension) element.createExecutableExtension("class");
+ String extensionId = swingAppExtension.getExtensionId();
+ if (extensionIdSet.contains(extensionId)) {
+ LOG.error("Already registred a swing application extension with extensionId=" + extensionId + ". ExtensionId must be unique");
+ }
+ else {
+ int ranking = 0;
+ String rankingString = element.getAttribute("ranking");
+ if (rankingString != null) {
+ ranking = Integer.parseInt(rankingString);
+ }
+ rankedExtensions.add(new P_RankedExtension(ranking, swingAppExtension));
+ LOG.debug("Added swing application extension " + swingAppExtension + " (ranking=" + ranking + ")");
+ }
+ }
+ catch (CoreException e) {
+ LOG.error("failed to create swing application extension instance. element=" + element, e);
+ }
+ }
+ }
+ if (rankedExtensions.isEmpty()) {
+ throw new IllegalStateException("no swing application extension contributed! at least one extension is required");
+ }
+ Collections.sort(rankedExtensions); // order by configured ranking
+ LOG.info("Registered " + rankedExtensions.size() + " swing application extensions:");
+ for (P_RankedExtension re : rankedExtensions) {
+ m_extensions.add(re.extension);
+ LOG.info("- " + re.extension);
+ }
+ defaultExtension = m_extensions.get(0);
+ LOG.info("Default swing application extension: " + defaultExtension);
+ }
+
+ @Override
+ ISwingEnvironment getSwingEnvironment() {
+ return defaultExtension.getEnvironment();
+ }
+
+ @Override
+ void initializeSwing() {
+ for (ISwingApplicationExtension ext : m_extensions) {
+ ext.initializeSwing();
+ }
+ }
+
+ @Override
+ final protected Object startInSubject(IApplicationContext context) throws Exception {
+ for (ISwingApplicationExtension ext : m_extensions) {
+ // 1. check if extension wants to exit while start up
+ Object exitCode = ext.execStartInSubject(context, getProgressMonitor());
+ if (exitCode != null) {
+ return exitCode;
+ }
+ // 2. check if extension has an active (valid) client session
+ if (!isClientSessionValid(ext.getClientSession())) {
+ return EXIT_OK;
+ }
+ }
+ return super.startInSubject(context);
+ }
+
+ @Override
+ void startGUI() {
+ for (ISwingApplicationExtension ext : m_extensions) {
+ ext.getEnvironment().showGUI(ext.getClientSession());
+ }
+ }
+
+ @Override
+ int runWhileActive() throws InterruptedException {
+ int exitCode = IApplication.EXIT_OK;
+ while (true) {
+ // 1. find an active client session
+ ISwingApplicationExtension activeExtension = null;
+ for (ISwingApplicationExtension ext : m_extensions) {
+ if (ext.getClientSession().isActive()) {
+ activeExtension = ext;
+ break;
+ }
+ }
+
+ // 2. all extensions terminated (= inactive)? -> terminate
+ if (activeExtension == null) {
+ return exitCode;
+ }
+
+ // 3. wait until a currently active session becomes inactive
+ IClientSession clientSession = activeExtension.getClientSession();
+ synchronized (clientSession.getStateLock()) {
+ if (clientSession.isActive()) {
+ clientSession.getStateLock().wait();
+ exitCode = clientSession.getExitCode();
+ }
+ }
+ }
+ }
+
+ @Override
+ public Object start(IApplicationContext context) throws Exception {
+ for (ISwingApplicationExtension ext : m_extensions) {
+ Object exitCode = ext.execStart(context, getProgressMonitor());
+ if (exitCode != null) {
+ return exitCode;
+ }
+ }
+ return super.start(context);
+ }
+
+ @Override
+ public void stop() {
+ for (ISwingApplicationExtension ext : m_extensions) {
+ ext.getClientSession().stopSession();
+ }
+ }
+
+}
diff --git a/org.eclipse.scout.rt.ui.swing/src/org/eclipse/scout/rt/ui/swing/extension/ISwingApplicationExtension.java b/org.eclipse.scout.rt.ui.swing/src/org/eclipse/scout/rt/ui/swing/extension/ISwingApplicationExtension.java
new file mode 100644
index 0000000..e39896f
--- /dev/null
+++ b/org.eclipse.scout.rt.ui.swing/src/org/eclipse/scout/rt/ui/swing/extension/ISwingApplicationExtension.java
@@ -0,0 +1,83 @@
+/*******************************************************************************
+ * Copyright (c) 2010 BSI Business Systems Integration AG.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * BSI Business Systems Integration AG - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.scout.rt.ui.swing.extension;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.equinox.app.IApplicationContext;
+import org.eclipse.scout.rt.client.IClientSession;
+import org.eclipse.scout.rt.client.ui.desktop.IDesktop;
+import org.eclipse.scout.rt.ui.swing.ISwingEnvironment;
+
+/**
+ * Instances of this interface are used to launch multiple scout swing-applications in a single eclipse .product.
+ * Each swing application extension has its own client session, swing environment and desktop.
+ *
+ * @author awe
+ */
+public interface ISwingApplicationExtension {
+
+ /**
+ * Returns the unique ID of this swing application extension.
+ *
+ * @return
+ */
+ String getExtensionId();
+
+ /**
+ * Instantiates swing environment (called by the Swing / AWT thread).
+ */
+ void initializeSwing();
+
+ /**
+ * Hook method to execute something when the <code>start</code> methods runs.
+ *
+ * @param context
+ * @param progressMonitor
+ * @throws Exception
+ * @return exitCode. when null, the execStart method of the next extension is called, when != null the start up
+ * exits with the given exitCode, no other extensions are called.
+ */
+ Object execStart(IApplicationContext context, IProgressMonitor progressMonitor) throws Exception;
+
+ /**
+ * Hook method to execute something when the <code>startInSubject</code> method runs. This methods is called
+ * within a <code>Subject.doAs()</code> call. By default you'd create an instance of a client session here.
+ *
+ * @param context
+ * @param progressMonitor
+ * @throws Exception
+ * @return exitCode. when null, the execStartInSubject method of the next extension is called, when != null the
+ * start up exits with the given exitCode, no other extensions are called.
+ */
+ Object execStartInSubject(IApplicationContext context, IProgressMonitor progressMonitor) throws Exception;
+
+ /**
+ * Returns the client session instance.
+ *
+ * @return
+ */
+ IClientSession getClientSession();
+
+ /**
+ * Returns the desktop instance.
+ *
+ * @return
+ */
+ IDesktop getDesktop();
+
+ /**
+ * Returns the swing environment instance.
+ *
+ * @return
+ */
+ ISwingEnvironment getEnvironment();
+
+}
diff --git a/org.eclipse.scout.rt.ui.swing/src/org/eclipse/scout/rt/ui/swing/extension/app/ISwingApplicationExtension.java b/org.eclipse.scout.rt.ui.swing/src/org/eclipse/scout/rt/ui/swing/extension/app/ISwingApplicationExtension.java
new file mode 100644
index 0000000..c022b5f
--- /dev/null
+++ b/org.eclipse.scout.rt.ui.swing/src/org/eclipse/scout/rt/ui/swing/extension/app/ISwingApplicationExtension.java
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * Copyright (c) 2010 BSI Business Systems Integration AG.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * BSI Business Systems Integration AG - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.scout.rt.ui.swing.extension.app;
+
+import org.eclipse.scout.rt.client.IClientSession;
+import org.eclipse.scout.rt.client.ui.desktop.IDesktop;
+import org.eclipse.scout.rt.ui.swing.ISwingEnvironment;
+
+/**
+ * Instances of this interface are used to launch multiple scout swing-applications in a single eclipse .product.
+ * Each swing application extension has its own client session, swing environment and desktop.
+ *
+ * @author awe
+ */
+public interface ISwingApplicationExtension {
+
+ /**
+ * Returns the unique ID of this swing application extension.
+ *
+ * @return
+ */
+ String getExtensionId();
+
+ /**
+ * Instantiates client session, swing environment and desktop.
+ */
+ void start();
+
+ /**
+ * Returns the client session instance.
+ *
+ * @return
+ */
+ IClientSession getClientSession();
+
+ /**
+ * Returns the desktop instance.
+ *
+ * @return
+ */
+ IDesktop getDesktop();
+
+ /**
+ * Returns the swing environment instance.
+ *
+ * @return
+ */
+ ISwingEnvironment getEnvironment();
+
+}
diff --git a/org.eclipse.scout.rt.ui.swing/src/org/eclipse/scout/rt/ui/swing/extension/app/internal/ISwingApplicationExtensions.java b/org.eclipse.scout.rt.ui.swing/src/org/eclipse/scout/rt/ui/swing/extension/app/internal/ISwingApplicationExtensions.java
new file mode 100644
index 0000000..0f44fd0
--- /dev/null
+++ b/org.eclipse.scout.rt.ui.swing/src/org/eclipse/scout/rt/ui/swing/extension/app/internal/ISwingApplicationExtensions.java
@@ -0,0 +1,70 @@
+/*******************************************************************************
+ * Copyright (c) 2010 BSI Business Systems Integration AG.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * BSI Business Systems Integration AG - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.scout.rt.ui.swing.extension.app.internal;
+
+import org.eclipse.scout.rt.client.IClientSession;
+import org.eclipse.scout.rt.ui.swing.ISwingEnvironment;
+
+/**
+ * Instances of this interface manage the contributed swing application extensions (see extension-point
+ * org.eclipse.scout.rt.ui.swing.appextensions).
+ *
+ * @author awe
+ */
+public interface ISwingApplicationExtensions {
+
+ /**
+ * Reads contributed swing application extensions (SAE) from the extension points and sets the default SAE,
+ * which is the SAE with the greatest ranking. Calls the <code>start()</code> method of each SAE.
+ */
+ void start();
+
+ /**
+ * Returns the swing environment of the currently active extension.
+ *
+ * @return
+ */
+ ISwingEnvironment getEnvironment(); // TODO AWE: (swingAppExt) vermutlich kann man diese methode entfernen (getEnvironment)
+
+ /**
+ * Returns the client session of the currently active extension.
+ *
+ * @return
+ */
+ IClientSession getClientSession(); // TODO AWE: (swingAppExt) vermutlich kann man diese methode entfernen (getClientSession)
+ // wird eigentlich nur für den splash screen gebraucht. Die regel könnte einfach sein, dass die extension mit dem höchsten
+ // ranking als default und somit für diese zwecke verwendet wird.
+
+ /**
+ * Shows the GUI of all registered swing app extensions.
+ */
+ void showGUI();
+
+ /**
+ * Whether or not start up has been successful. If not, getStartUpError() will return the cause of the error.
+ *
+ * @return
+ */
+ boolean startUpSuccessful();
+
+ /**
+ * When startUpSuccessful returns false this method returns the cause of the start up error or null otherwise.
+ *
+ * @return
+ */
+ Throwable getStartUpError();
+
+ /**
+ * Stops all registered swing application extensions.
+ */
+ void stop();
+
+}
diff --git a/org.eclipse.scout.rt.ui.swing/src/org/eclipse/scout/rt/ui/swing/extension/app/internal/SwingApplicationExtensions.java b/org.eclipse.scout.rt.ui.swing/src/org/eclipse/scout/rt/ui/swing/extension/app/internal/SwingApplicationExtensions.java
new file mode 100644
index 0000000..677617b
--- /dev/null
+++ b/org.eclipse.scout.rt.ui.swing/src/org/eclipse/scout/rt/ui/swing/extension/app/internal/SwingApplicationExtensions.java
@@ -0,0 +1,120 @@
+/*******************************************************************************
+ * Copyright (c) 2010 BSI Business Systems Integration AG.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * BSI Business Systems Integration AG - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.scout.rt.ui.swing.extension.app.internal;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExtension;
+import org.eclipse.core.runtime.IExtensionPoint;
+import org.eclipse.core.runtime.IExtensionRegistry;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.scout.commons.logger.IScoutLogger;
+import org.eclipse.scout.commons.logger.ScoutLogManager;
+import org.eclipse.scout.rt.client.IClientSession;
+import org.eclipse.scout.rt.ui.swing.Activator;
+import org.eclipse.scout.rt.ui.swing.ISwingEnvironment;
+import org.eclipse.scout.rt.ui.swing.extension.app.ISwingApplicationExtension;
+
+/**
+ * This class manages the contributed swing application extensions (see extension-point
+ * org.eclipse.scout.rt.ui.swing.appextensions).
+ *
+ * @author awe
+ */
+public class SwingApplicationExtensions implements ISwingApplicationExtensions {
+
+ public static final String EXTENSION_POINT = Activator.PLUGIN_ID + ".appextensions";
+
+ private static final IScoutLogger LOG = ScoutLogManager.getLogger(SwingApplicationExtensions.class);
+
+ private Map<String, ISwingApplicationExtension> m_swingAppExtensionsMap = new HashMap<String, ISwingApplicationExtension>();
+
+ private ISwingApplicationExtension m_activeSwingAppExtension;
+
+ @Override
+ public void start() {
+ IExtensionRegistry registry = Platform.getExtensionRegistry();
+ IExtensionPoint xp = registry.getExtensionPoint(EXTENSION_POINT);
+ int greatestRanking = 0;
+ for (IExtension extension : xp.getExtensions()) {
+ for (IConfigurationElement element : extension.getConfigurationElements()) {
+ try {
+ ISwingApplicationExtension swingAppExtension = (ISwingApplicationExtension) element.createExecutableExtension("class");
+ int ranking = 0;
+ String rankingString = element.getAttribute("ranking");
+ if (rankingString != null) {
+ ranking = Integer.parseInt(rankingString);
+ }
+ if (ranking >= greatestRanking) {
+ greatestRanking = ranking;
+ m_activeSwingAppExtension = swingAppExtension;
+ }
+ String extensionId = swingAppExtension.getExtensionId();
+ if (m_swingAppExtensionsMap.containsKey(extensionId)) {
+ LOG.error("Already registred a swing application extension with extensionId=" + extensionId + ". ExtensionId must be unique");
+ }
+ else {
+ swingAppExtension.start();
+ m_swingAppExtensionsMap.put(extensionId, swingAppExtension);
+ LOG.debug("Added swing application extension " + swingAppExtension);
+ }
+ }
+ catch (CoreException e) {
+ LOG.error("failed to create swing application extension instance. element=" + element, e);
+ }
+ }
+ }
+ if (m_swingAppExtensionsMap.isEmpty()) {
+ throw new IllegalStateException("no swing application extension contributed! at least one extension is required");
+ // TODO AWE: (swingAppExt) können/sollen wir hier einfach einen default hinzufügen, wenn die map leer ist?
+ // von der änderung betroffen ist ja auch der code der von der SDK generiert wird. Das mit einem scout-core
+ // entwickler anschauen
+ }
+ }
+
+ @Override
+ public ISwingEnvironment getEnvironment() {
+ return m_activeSwingAppExtension.getEnvironment();
+ }
+
+ @Override
+ public IClientSession getClientSession() {
+ return m_activeSwingAppExtension.getClientSession();
+ }
+
+ @Override
+ public void showGUI() { // TODO AWE: (swingAppExt) checken
+ for (ISwingApplicationExtension ext : m_swingAppExtensionsMap.values()) {
+ ext.getEnvironment().showGUI(ext.getClientSession());
+ }
+ }
+
+ @Override
+ public boolean startUpSuccessful() {
+ return true; // TODO AWE: (swingAppExt) impl.
+ }
+
+ @Override
+ public Throwable getStartUpError() {
+ return null; // TODO AWE: (swingAppExt) impl.
+ }
+
+ @Override
+ public void stop() {
+ for (ISwingApplicationExtension ext : m_swingAppExtensionsMap.values()) {
+ ext.getClientSession().stopSession();
+ }
+ }
+
+}