| author | André Wegmüller | 2013-01-22 11:10:16 (EST) |
|---|---|---|
| committer | Stephan Leicht Vogt | 2013-01-24 09:28:08 (EST) |
| commit | af8473fc78a42d5bd5bff1e6b6aeb31c0a76691f (patch) (side-by-side diff) | |
| tree | b9c20c1b6a797b9ef04aa9269bbcd5b748bc6d59 | |
| parent | b98075879af267758ba827a3687a5dae5883b05e (diff) | |
| download | org.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
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 --- a/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 --- a/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 --- a/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 --- a/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 --- a/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 --- a/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 --- a/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 --- a/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 --- a/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 --- a/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 --- a/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 --- a/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(); + } + } + +} |

