Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Watson2005-12-14 22:17:05 +0000
committerThomas Watson2005-12-14 22:17:05 +0000
commit9441b2c67bb7fa2d30d615771f73b524db34bcd4 (patch)
treed3113393aacf3e0b96439913b73cd1f7f849137f
parent432262c875593f48c808546b051c38f69181e051 (diff)
downloadrt.equinox.bundles-9441b2c67bb7fa2d30d615771f73b524db34bcd4.tar.gz
rt.equinox.bundles-9441b2c67bb7fa2d30d615771f73b524db34bcd4.tar.xz
rt.equinox.bundles-9441b2c67bb7fa2d30d615771f73b524db34bcd4.zip
intial release of app admin impl
-rwxr-xr-xbundles/org.eclipse.equinox.app/.classpath7
-rwxr-xr-xbundles/org.eclipse.equinox.app/.cvsignore1
-rwxr-xr-xbundles/org.eclipse.equinox.app/.project28
-rwxr-xr-xbundles/org.eclipse.equinox.app/.settings/org.eclipse.core.resources.prefs3
-rwxr-xr-xbundles/org.eclipse.equinox.app/.settings/org.eclipse.jdt.core.prefs77
-rwxr-xr-xbundles/org.eclipse.equinox.app/.settings/org.eclipse.jdt.ui.prefs3
-rwxr-xr-xbundles/org.eclipse.equinox.app/.settings/org.eclipse.pde.core.prefs4
-rwxr-xr-xbundles/org.eclipse.equinox.app/META-INF/MANIFEST.MF27
-rwxr-xr-xbundles/org.eclipse.equinox.app/about.html60
-rwxr-xr-xbundles/org.eclipse.equinox.app/build.properties16
-rwxr-xr-xbundles/org.eclipse.equinox.app/plugin.properties14
-rwxr-xr-xbundles/org.eclipse.equinox.app/plugin.xml6
-rwxr-xr-xbundles/org.eclipse.equinox.app/schema/applications.exsd166
-rwxr-xr-xbundles/org.eclipse.equinox.app/schema/containers.exsd117
-rwxr-xr-xbundles/org.eclipse.equinox.app/src/org/eclipse/equinox/app/IAppContext.java73
-rwxr-xr-xbundles/org.eclipse.equinox.app/src/org/eclipse/equinox/app/IApplication.java62
-rwxr-xr-xbundles/org.eclipse.equinox.app/src/org/eclipse/equinox/app/IContainer.java40
-rwxr-xr-xbundles/org.eclipse.equinox.app/src/org/eclipse/equinox/internal/app/Activator.java58
-rwxr-xr-xbundles/org.eclipse.equinox.app/src/org/eclipse/equinox/internal/app/AppCommands.java233
-rwxr-xr-xbundles/org.eclipse.equinox.app/src/org/eclipse/equinox/internal/app/AppManager.java552
-rwxr-xr-xbundles/org.eclipse.equinox.app/src/org/eclipse/equinox/internal/app/ContainerManager.java446
-rwxr-xr-xbundles/org.eclipse.equinox.app/src/org/eclipse/equinox/internal/app/EclipseAppDescriptor.java155
-rwxr-xr-xbundles/org.eclipse.equinox.app/src/org/eclipse/equinox/internal/app/EclipseAppHandle.java132
-rwxr-xr-xbundles/org.eclipse.equinox.app/src/org/eclipse/equinox/internal/app/EclipseScheduledApplication.java129
-rwxr-xr-xbundles/org.eclipse.equinox.app/src/org/eclipse/equinox/internal/app/MainSingletonApplication.java70
-rwxr-xr-xbundles/org.eclipse.equinox.app/src/org/eclipse/equinox/internal/app/MainSingletonContainer.java71
-rwxr-xr-xbundles/org.eclipse.equinox.app/src/org/eclipse/equinox/internal/app/Messages.java42
-rwxr-xr-xbundles/org.eclipse.equinox.app/src/org/eclipse/equinox/internal/app/SingletonContainerMgr.java58
-rwxr-xr-xbundles/org.eclipse.equinox.app/src/org/eclipse/equinox/internal/app/messages.properties26
-rwxr-xr-xbundles/org.eclipse.equinox.app/src/org/osgi/service/application/ApplicationAdminPermission.java389
-rwxr-xr-xbundles/org.eclipse.equinox.app/src/org/osgi/service/application/ApplicationDescriptor.java468
-rwxr-xr-xbundles/org.eclipse.equinox.app/src/org/osgi/service/application/ApplicationHandle.java180
-rwxr-xr-xbundles/org.eclipse.equinox.app/src/org/osgi/service/application/ScheduledApplication.java89
33 files changed, 3802 insertions, 0 deletions
diff --git a/bundles/org.eclipse.equinox.app/.classpath b/bundles/org.eclipse.equinox.app/.classpath
new file mode 100755
index 000000000..751c8f2e5
--- /dev/null
+++ b/bundles/org.eclipse.equinox.app/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/bundles/org.eclipse.equinox.app/.cvsignore b/bundles/org.eclipse.equinox.app/.cvsignore
new file mode 100755
index 000000000..ba077a403
--- /dev/null
+++ b/bundles/org.eclipse.equinox.app/.cvsignore
@@ -0,0 +1 @@
+bin
diff --git a/bundles/org.eclipse.equinox.app/.project b/bundles/org.eclipse.equinox.app/.project
new file mode 100755
index 000000000..c1246c349
--- /dev/null
+++ b/bundles/org.eclipse.equinox.app/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.eclipse.equinox.app</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/bundles/org.eclipse.equinox.app/.settings/org.eclipse.core.resources.prefs b/bundles/org.eclipse.equinox.app/.settings/org.eclipse.core.resources.prefs
new file mode 100755
index 000000000..16532b29e
--- /dev/null
+++ b/bundles/org.eclipse.equinox.app/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,3 @@
+#Tue May 25 15:00:03 EDT 2004
+encoding/<project>=ISO-8859-1
+eclipse.preferences.version=1
diff --git a/bundles/org.eclipse.equinox.app/.settings/org.eclipse.jdt.core.prefs b/bundles/org.eclipse.equinox.app/.settings/org.eclipse.jdt.core.prefs
new file mode 100755
index 000000000..9509abc7e
--- /dev/null
+++ b/bundles/org.eclipse.equinox.app/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,77 @@
+#Wed Dec 14 13:44:11 CST 2005
+eclipse.preferences.version=1
+org.eclipse.jdt.core.builder.cleanOutputFolder=clean
+org.eclipse.jdt.core.builder.duplicateResourceTask=warning
+org.eclipse.jdt.core.builder.invalidClasspath=abort
+org.eclipse.jdt.core.builder.resourceCopyExclusionFilter=*.launch
+org.eclipse.jdt.core.circularClasspath=error
+org.eclipse.jdt.core.classpath.exclusionPatterns=enabled
+org.eclipse.jdt.core.classpath.multipleOutputLocations=enabled
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=disabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.2
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.4
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.doc.comment.support=enabled
+org.eclipse.jdt.core.compiler.maxProblemPerUnit=1000
+org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=warning
+org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
+org.eclipse.jdt.core.compiler.problem.deprecation=warning
+org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
+org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=enabled
+org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
+org.eclipse.jdt.core.compiler.problem.emptyStatement=warning
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=warning
+org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
+org.eclipse.jdt.core.compiler.problem.finalParameterBound=ignore
+org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=error
+org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning
+org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
+org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore
+org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=warning
+org.eclipse.jdt.core.compiler.problem.invalidJavadoc=warning
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=private
+org.eclipse.jdt.core.compiler.problem.localVariableHiding=warning
+org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning
+org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingJavadocComments=ignore
+org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=enabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=public
+org.eclipse.jdt.core.compiler.problem.missingJavadocTags=ignore
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=enabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=public
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
+org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning
+org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning
+org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=warning
+org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
+org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning
+org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
+org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning
+org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
+org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=warning
+org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
+org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
+org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=warning
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=warning
+org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning
+org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
+org.eclipse.jdt.core.compiler.problem.unsafeTypeOperation=warning
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=enabled
+org.eclipse.jdt.core.compiler.problem.unusedImport=error
+org.eclipse.jdt.core.compiler.problem.unusedLocal=warning
+org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=enabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=enabled
+org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning
+org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
+org.eclipse.jdt.core.compiler.source=1.3
+org.eclipse.jdt.core.incompatibleJDKLevel=ignore
+org.eclipse.jdt.core.incompleteClasspath=error
diff --git a/bundles/org.eclipse.equinox.app/.settings/org.eclipse.jdt.ui.prefs b/bundles/org.eclipse.equinox.app/.settings/org.eclipse.jdt.ui.prefs
new file mode 100755
index 000000000..a0bbd3f76
--- /dev/null
+++ b/bundles/org.eclipse.equinox.app/.settings/org.eclipse.jdt.ui.prefs
@@ -0,0 +1,3 @@
+#Wed Dec 14 13:44:11 CST 2005
+eclipse.preferences.version=1
+internal.default.compliance=default
diff --git a/bundles/org.eclipse.equinox.app/.settings/org.eclipse.pde.core.prefs b/bundles/org.eclipse.equinox.app/.settings/org.eclipse.pde.core.prefs
new file mode 100755
index 000000000..e993d41da
--- /dev/null
+++ b/bundles/org.eclipse.equinox.app/.settings/org.eclipse.pde.core.prefs
@@ -0,0 +1,4 @@
+#Fri Dec 02 16:20:40 CST 2005
+eclipse.preferences.version=1
+pluginProject.equinox=false
+pluginProject.extensions=true
diff --git a/bundles/org.eclipse.equinox.app/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.app/META-INF/MANIFEST.MF
new file mode 100755
index 000000000..1b66219af
--- /dev/null
+++ b/bundles/org.eclipse.equinox.app/META-INF/MANIFEST.MF
@@ -0,0 +1,27 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %pluginName
+Bundle-SymbolicName: org.eclipse.equinox.app; singleton:=true
+Bundle-Version: 1.0.0.qualifier
+Bundle-Vendor: %providerName
+Bundle-Activator: org.eclipse.equinox.internal.app.Activator
+Bundle-Localization: plugin
+Export-Package: org.eclipse.equinox.app;version="1.0",
+ org.eclipse.equinox.internal.app;x-friends:="org.eclipse.core.runtime",
+ org.osgi.service.application;version="1.0"
+Import-Package: org.osgi.framework;version="1.3",
+ org.osgi.service.condpermadmin,
+ org.osgi.service.event;version="1.0.0";resolution:=optional,
+ org.osgi.service.packageadmin; version="1.2",
+ org.osgi.util.tracker,
+ org.eclipse.equinox.registry,
+ org.eclipse.osgi.framework.log,
+ org.eclipse.core.runtime.adaptor,
+ org.eclipse.osgi.service.debug,
+ org.eclipse.osgi.service.datalocation,
+ org.eclipse.osgi.service.environment,
+ org.eclipse.osgi.service.runnable,
+ org.eclipse.osgi.util,
+ org.eclipse.osgi.framework.console; resolution:=optional
+Require-Bundle: org.eclipse.equinox.common
+DynamicImport-Package: org.osgi.service.event;version="1.0.0"
diff --git a/bundles/org.eclipse.equinox.app/about.html b/bundles/org.eclipse.equinox.app/about.html
new file mode 100755
index 000000000..bd87495af
--- /dev/null
+++ b/bundles/org.eclipse.equinox.app/about.html
@@ -0,0 +1,60 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
+<html>
+<head>
+<title>About</title>
+<meta http-equiv=Content-Type content="text/html; charset=ISO-8859-1">
+</head>
+<body lang="EN-US">
+<h2>About This Content</h2>
+
+<p>September 26, 2005</p>
+<h3>License</h3>
+
+<p>The Eclipse Foundation makes available all content in this plug-in (&quot;Content&quot;). Unless otherwise indicated below, the Content is provided to you under the terms and conditions of the
+Eclipse Public License Version 1.0 (&quot;EPL&quot;). A copy of the EPL is available at <a href="http://www.eclipse.org/legal/epl-v10.html" target="_blank">http://www.eclipse.org/legal/epl-v10.html</a>.
+For purposes of the EPL, &quot;Program&quot; will mean the Content.</p>
+
+<p>If you did not receive this Content directly from the Eclipse Foundation, the Content is being redistributed by another party (&quot;Redistributor&quot;) and different terms and conditions may
+apply to your use of any object code in the Content. Check the Redistributor's license that was provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise
+indicated below, the terms and conditions of the EPL still apply to any source code in the Content.</p>
+
+<h3>Third Party Content</h3>
+
+<p>The Content includes items that have been sourced from third parties as follows:</p>
+
+<p><b>OSGi Materials</b></p>
+
+<p>All files in the following sub-directories (and their sub-directories):</p>
+
+<ul>
+ <li>org/osgi</li>
+</ul>
+
+<p>shall be defined as the &quot;OSGi Materials.&quot; The OSGi Materials are:</p>
+
+<blockquote>
+Copyright (c) 2000, 2005
+<br /><br />
+OSGi Alliance
+Bishop Ranch 6<br/>
+2400 Camino Ramon, Suite 375<br/>
+San Ramon, CA 94583 USA
+<br /><br />
+All Rights Reserved.
+</blockquote>
+
+<p>The OSGi Materials are provided to you under the terms and conditions of the
+Eclipse Public License Version 1.0 (&quot;EPL&quot;). For purposes of the EPL, &quot;Program&quot; will mean the OSGi Materials.</p>
+
+<p>Implementation of certain elements of the OSGi Materials may be subject to third party intellectual property rights, including without limitation, patent rights (such a third party may
+or may not be a member of the OSGi Alliance). The OSGi Alliance and its members are not responsible and shall not be held responsible in any manner for identifying or failing to identify any or all such third party
+intellectual property rights.</p>
+
+<small>OSGi&trade; is a trademark, registered trademark, or service mark of The OSGi Alliance in the US and other countries. Java is a trademark,
+registered trademark, or service mark of Sun Microsystems, Inc. in the US and other countries. All other trademarks, registered trademarks, or
+service marks used in the Content are the property of their respective owners and are hereby recognized.</small>
+
+<small>Java and all Java-based trademarks and logos are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries.</small>
+
+</body>
+</html>
diff --git a/bundles/org.eclipse.equinox.app/build.properties b/bundles/org.eclipse.equinox.app/build.properties
new file mode 100755
index 000000000..cefe3b679
--- /dev/null
+++ b/bundles/org.eclipse.equinox.app/build.properties
@@ -0,0 +1,16 @@
+###############################################################################
+# Copyright (c) 2005 IBM Corporation and others.
+# 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:
+# IBM Corporation - initial API and implementation
+###############################################################################
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .,\
+ about.html
+src.includes = about.html
diff --git a/bundles/org.eclipse.equinox.app/plugin.properties b/bundles/org.eclipse.equinox.app/plugin.properties
new file mode 100755
index 000000000..31bd8dc65
--- /dev/null
+++ b/bundles/org.eclipse.equinox.app/plugin.properties
@@ -0,0 +1,14 @@
+###############################################################################
+# Copyright (c) 2003, 2004 IBM Corporation and others.
+# 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:
+# IBM Corporation - initial API and implementation
+###############################################################################
+pluginName = Equinox Application Container
+providerName = Eclipse.org
+applicationsName = Applications
+containersName = Containers
diff --git a/bundles/org.eclipse.equinox.app/plugin.xml b/bundles/org.eclipse.equinox.app/plugin.xml
new file mode 100755
index 000000000..dfdd4ea67
--- /dev/null
+++ b/bundles/org.eclipse.equinox.app/plugin.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.0"?>
+<plugin>
+ <extension-point id="applications" name="%applicationsName" schema="schema/applications.exsd"/>
+ <extension-point id="containers" name="%containersName" schema="schema/containers.exsd"/>
+</plugin>
diff --git a/bundles/org.eclipse.equinox.app/schema/applications.exsd b/bundles/org.eclipse.equinox.app/schema/applications.exsd
new file mode 100755
index 000000000..4d31c8310
--- /dev/null
+++ b/bundles/org.eclipse.equinox.app/schema/applications.exsd
@@ -0,0 +1,166 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!-- Schema file written by PDE -->
+<schema targetNamespace="org.eclipse.equinox.app">
+<annotation>
+ <appInfo>
+ <meta.schema plugin="org.eclipse.equinox.app" id="applications" name="Applications"/>
+ </appInfo>
+ <documentation>
+ Platform runtime supports plug-ins which would like to
+declare main entry points. That is, programs which
+would like to run using the platform runtime but yet
+control all aspects of execution can declare themselves
+as an application. Declared applications can be run
+directly from the main platform launcher by specifying
+the &lt;i&gt;-application&lt;/i&gt; argument where the parameter
+is the id of an extension supplied to the
+applications extension point described here.
+This application is instantiated and run by the platform.
+Platform clients can also use the platform to lookup and
+run multiple applications.
+ </documentation>
+ </annotation>
+
+ <element name="extension">
+ <complexType>
+ <sequence>
+ <element ref="application"/>
+ </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>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <element name="application">
+ <complexType>
+ <sequence>
+ <element ref="run" minOccurs="0" maxOccurs="1"/>
+ </sequence>
+ <attribute name="type" type="string" use="default" value="">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <element name="run">
+ <complexType>
+ <sequence>
+ <element ref="parameter" minOccurs="0" maxOccurs="unbounded"/>
+ </sequence>
+ <attribute name="class" type="string" use="required">
+ <annotation>
+ <documentation>
+ the fully-qualified name of a class which implements
+&lt;samp&gt;org.eclipse.equinox.application.IApplication&lt;/samp&gt;.
+ </documentation>
+ <appInfo>
+ <meta.attribute kind="java" basedOn="org.eclipse.equinox.app.IApplication"/>
+ </appInfo>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <element name="parameter">
+ <annotation>
+ <appInfo>
+ <meta.element labelAttribute="name"/>
+ </appInfo>
+ </annotation>
+ <complexType>
+ <attribute name="name" type="string" use="required">
+ <annotation>
+ <documentation>
+ the name of this parameter made available to instances of the specified application class
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="value" type="string" use="required">
+ <annotation>
+ <documentation>
+ the value of this parameter made available to instances of the specified application class
+ </documentation>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="examples"/>
+ </appInfo>
+ <documentation>
+ Following is an example of an application declaration:
+&lt;p&gt;
+&lt;pre&gt;
+ &lt;extension id=&quot;coolApplication&quot; point=&quot;org.eclipse.equinox.applications&quot;&gt;
+ &lt;application&gt;
+ &lt;run class=&quot;com.xyz.applications.Cool&quot;&gt;
+ &lt;parameter name=&quot;optimize&quot; value=&quot;true&quot;/&gt;
+ &lt;/run&gt;
+ &lt;/application&gt;
+ &lt;/extension&gt;
+&lt;/pre&gt;
+&lt;/p&gt;
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="apiInfo"/>
+ </appInfo>
+ <documentation>
+ The value of the class attribute must represent an
+implementor of
+&lt;samp&gt;org.eclipse.equinox.IPlatformRunnable&lt;/samp&gt;.
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="implementation"/>
+ </appInfo>
+ <documentation>
+ The platform supplies a number of applications including the platform workbench itself.
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="copyright"/>
+ </appInfo>
+ <documentation>
+ Copyright (c) 2004, 2005 IBM Corporation and others.&lt;br&gt;
+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
+&lt;a
+href=&quot;http://www.eclipse.org/legal/epl-v10.html&quot;&gt;http://www.eclipse.org/legal/epl-v10.html&lt;/a&gt;
+ </documentation>
+ </annotation>
+
+</schema>
diff --git a/bundles/org.eclipse.equinox.app/schema/containers.exsd b/bundles/org.eclipse.equinox.app/schema/containers.exsd
new file mode 100755
index 000000000..97431780e
--- /dev/null
+++ b/bundles/org.eclipse.equinox.app/schema/containers.exsd
@@ -0,0 +1,117 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!-- Schema file written by PDE -->
+<schema targetNamespace="org.eclipse.equinox.app">
+<annotation>
+ <appInfo>
+ <meta.schema plugin="org.eclipse.equinox.app" id="containers" name="Containers"/>
+ </appInfo>
+ <documentation>
+ [Enter description of this extension point.]
+ </documentation>
+ </annotation>
+
+ <element name="extension">
+ <complexType>
+ <sequence>
+ <element ref="container"/>
+ </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>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <element name="container">
+ <complexType>
+ <sequence>
+ <element ref="run"/>
+ </sequence>
+ <attribute name="type" type="string" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <element name="run">
+ <complexType>
+ <attribute name="class" type="string" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ <appInfo>
+ <meta.attribute kind="java" basedOn="org.eclipse.equinox.app.IContainer"/>
+ </appInfo>
+ </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>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="copyright"/>
+ </appInfo>
+ <documentation>
+
+ </documentation>
+ </annotation>
+
+</schema>
diff --git a/bundles/org.eclipse.equinox.app/src/org/eclipse/equinox/app/IAppContext.java b/bundles/org.eclipse.equinox.app/src/org/eclipse/equinox/app/IAppContext.java
new file mode 100755
index 000000000..50a221635
--- /dev/null
+++ b/bundles/org.eclipse.equinox.app/src/org/eclipse/equinox/app/IAppContext.java
@@ -0,0 +1,73 @@
+/*******************************************************************************
+ * Copyright (c) 2005 IBM Corporation and others.
+ * 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:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.equinox.app;
+
+import java.util.Map;
+import org.eclipse.equinox.registry.IConfigurationElement;
+import org.osgi.service.application.ApplicationDescriptor;
+
+/**
+ * A context is used by a container to launch an application using
+ * {@link IContainer#launch(IAppContext)}. The initial status of a
+ * context is {@link #ACTIVE} when it is given to a container to
+ * launch an application.
+ * @see IApplication
+ * @see IContainer
+ * <p>
+ * Clients may not implement this interface.
+ * </p>
+ * @since 3.2
+ */
+public interface IAppContext {
+ /**
+ * Indicates the application is active for this context
+ */
+ public int ACTIVE = 0x01;
+ /**
+ * Indicates the application is stopping for this context
+ */
+ public int STOPPING = 0x02;
+ /**
+ * Indicates the application is stopped for this context
+ */
+ public int STOPPED = 0x04;
+ /**
+ * Returns the arguments to use when launching an application.
+ * @return the arguments to use when launching an applicaiton, null may be returned.
+ */
+ public Map getArguments();
+ /**
+ * Returns the application descriptor for this context.
+ * @return the application descriptor for this context.
+ */
+ public ApplicationDescriptor getApplicationDescriptor();
+ /**
+ * Returns the configuration element for this context.
+ * @return the configuration element for this context.
+ */
+ public IConfigurationElement getConfiguration();
+
+ /**
+ * Called by the container when an application is stopping or has stopped.
+ * @param status may be {@link #STOPPING} or {@link #STOPPED}
+ * @see #STOPPING
+ * @see #STOPPED
+ * @throws IllegalArgumentException if {@link #ACTIVE} is used as the status.
+ */
+ public void setAppStatus(int status);
+
+ /**
+ * Returns the current app status according to the context
+ * @return the current app status according to the context
+ */
+ public int getAppStatus();
+}
diff --git a/bundles/org.eclipse.equinox.app/src/org/eclipse/equinox/app/IApplication.java b/bundles/org.eclipse.equinox.app/src/org/eclipse/equinox/app/IApplication.java
new file mode 100755
index 000000000..402eb6209
--- /dev/null
+++ b/bundles/org.eclipse.equinox.app/src/org/eclipse/equinox/app/IApplication.java
@@ -0,0 +1,62 @@
+/*******************************************************************************
+ * Copyright (c) 2005 IBM Corporation and others.
+ * 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:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.equinox.app;
+/**
+ * Bootstrap type for an application. An IApplication represent executable
+ * entry points into an application. An IApplication can be configured into
+ * the Platform's <code>org.eclipse.equinox.applications</code> extension-point.
+ *
+ * <p>
+ * Clients may implement this interface.
+ * </p>
+ *
+ * @since 3.2
+ */
+public interface IApplication {
+
+ /**
+ * Exit object indicating normal termination
+ */
+ public static final Integer EXIT_OK = new Integer(0);
+
+ /**
+ * Exit object requesting platform restart
+ */
+ public static final Integer EXIT_RESTART = new Integer(23);
+
+ /**
+ * Exit object requesting that the command passed back be executed. Typically
+ * this is used to relaunch Eclipse with different command line arguments.
+ */
+ public static final Integer EXIT_RELAUNCH = new Integer(24);
+
+ /**
+ * Runs this application with the given args and returns a result.
+ * The content of the args is unchecked and should conform to the expectations of
+ * the runnable being invoked. Typically this is a <code>String</code> array. <p>
+ *
+ * Applications can return any object they like. If an <code>Integer</code> is returned
+ * it is treated as the program exit code if Eclipse is exiting.
+ * @return the return value of the application
+ * @see #EXIT_OK
+ * @see #EXIT_RESTART
+ * @see #EXIT_RELAUNCH
+ * @param args the argument(s) to pass to the application
+ * @exception Exception if there is a problem running this application.
+ */
+ public Object run(Object args) throws Exception;
+ /**
+ * Forces a running application to exit. This method must block until the
+ * running application has completely stopped.
+ */
+ public void stop();
+}
diff --git a/bundles/org.eclipse.equinox.app/src/org/eclipse/equinox/app/IContainer.java b/bundles/org.eclipse.equinox.app/src/org/eclipse/equinox/app/IContainer.java
new file mode 100755
index 000000000..e8e08098d
--- /dev/null
+++ b/bundles/org.eclipse.equinox.app/src/org/eclipse/equinox/app/IContainer.java
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * Copyright (c) 2005 IBM Corporation and others.
+ * 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:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.equinox.app;
+
+/**
+ * A container is used to launch applications.
+ * @see IAppContext
+ * @see IApplication
+ * <p>
+ * Clients may implement this interface.
+ * </p>
+ * @since 3.2
+ */
+public interface IContainer {
+ /**
+ * Launch an application with the given application context. The given
+ * context must have its {@link IAppContext#setAppStatus(int)} method called by
+ * the container when the returned application has stopped.
+ * @param context the application context to launch an application with
+ * @return the application which was launched
+ * @throws Exception if any errors occur while launching the application
+ */
+ IApplication launch(IAppContext context) throws Exception;
+ /**
+ * returns true if this container only allows one running application
+ * at a time.
+ * @return true if this container only allows one running application
+ * at a time.
+ */
+ boolean isSingletonContainer();
+}
diff --git a/bundles/org.eclipse.equinox.app/src/org/eclipse/equinox/internal/app/Activator.java b/bundles/org.eclipse.equinox.app/src/org/eclipse/equinox/internal/app/Activator.java
new file mode 100755
index 000000000..4825db0d5
--- /dev/null
+++ b/bundles/org.eclipse.equinox.app/src/org/eclipse/equinox/internal/app/Activator.java
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * Copyright (c) 2005 IBM Corporation and others.
+ * 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:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.equinox.internal.app;
+
+import org.eclipse.osgi.service.debug.DebugOptions;
+import org.osgi.framework.*;
+
+public class Activator implements BundleActivator {
+ public static final String PI_APP = "org.eclipse.equinox.app"; //$NON-NLS-1$
+ public static boolean DEBUG = false;
+ private ContainerManager containerMgr;
+ public void start(BundleContext context) throws Exception {
+ getDebugOptions(context);
+ // set the app manager context before starting the containerMgr
+ AppManager.setBundleContext(context);
+ // create and start the app containerMgr
+ containerMgr = new ContainerManager(context);
+ containerMgr.startContainer();
+ // start the app commands for the console
+ try {
+ AppCommands.create(context);
+ } catch(NoClassDefFoundError e) {
+ // catch incase CommandProvider is not available
+ }
+ }
+
+ public void stop(BundleContext context) throws Exception {
+ // stop the app commands for the console
+ try {
+ AppCommands.destroy(context);
+ } catch(NoClassDefFoundError e) {
+ // catch incase CommandProvider is not available
+ }
+ // stop the app containerMgr
+ containerMgr.stopContainer();
+ containerMgr = null;
+ // un set the app manager context after the containerMgr has been stopped
+ AppManager.setBundleContext(null);
+ }
+
+ private void getDebugOptions(BundleContext context) {
+ ServiceReference debugRef = context.getServiceReference(DebugOptions.class.getName());
+ if (debugRef == null)
+ return;
+ DebugOptions debugOptions = (DebugOptions) context.getService(debugRef);
+ DEBUG = debugOptions.getBooleanOption(PI_APP + "/debug", false); //$NON-NLS-1$
+ context.ungetService(debugRef);
+ }
+}
diff --git a/bundles/org.eclipse.equinox.app/src/org/eclipse/equinox/internal/app/AppCommands.java b/bundles/org.eclipse.equinox.app/src/org/eclipse/equinox/internal/app/AppCommands.java
new file mode 100755
index 000000000..b82d97d34
--- /dev/null
+++ b/bundles/org.eclipse.equinox.app/src/org/eclipse/equinox/internal/app/AppCommands.java
@@ -0,0 +1,233 @@
+/*******************************************************************************
+ * Copyright (c) 2005 IBM Corporation and others.
+ * 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:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.app;
+
+import java.util.*;
+import org.eclipse.osgi.framework.console.CommandInterpreter;
+import org.eclipse.osgi.framework.console.CommandProvider;
+import org.osgi.framework.*;
+import org.osgi.service.application.*;
+import org.osgi.util.tracker.ServiceTracker;
+
+public class AppCommands implements CommandProvider {
+ private final static String LAUNCHABLE_APP_FILTER = "(&(application.locked=false)(application.launchable=true)(application.visible=true))"; //$NON-NLS-1$
+ private final static String ACTIVE_APP_FILTER = "(!(application.state=STOPPING))"; //$NON-NLS-1$
+
+ private static AppCommands instance;
+ private BundleContext context;
+ private ServiceTracker appDescTracker;
+ private ServiceTracker appHandleTracker;
+ private ServiceTracker schedAppTracker;
+ private Filter launchableApp;
+ private Filter activeApp;
+ private ServiceRegistration sr;
+
+ static synchronized void create(BundleContext context) {
+ if (instance != null)
+ return;
+ instance = new AppCommands();
+ instance.start(context);
+ }
+
+ static synchronized void destroy(BundleContext context) {
+ if (instance == null)
+ return;
+ instance.stop(context);
+ instance = null;
+ }
+
+ protected AppCommands() {
+ // empty
+ }
+
+ public void start(BundleContext ctx) {
+ this.context = ctx;
+ try {
+ appDescTracker = new ServiceTracker(ctx, ApplicationDescriptor.class.getName(), null);
+ appDescTracker.open();
+ appHandleTracker = new ServiceTracker(ctx, ApplicationHandle.class.getName(), null);
+ appHandleTracker.open();
+ schedAppTracker = new ServiceTracker(ctx, ScheduledApplication.class.getName(), null);
+ schedAppTracker.open();
+ launchableApp = ctx.createFilter(LAUNCHABLE_APP_FILTER);
+ activeApp = ctx.createFilter(ACTIVE_APP_FILTER);
+ sr = ctx.registerService(CommandProvider.class.getName(), this, null);
+ } catch (InvalidSyntaxException e) {
+ // should not happen.
+ }
+ }
+
+ public void stop(BundleContext ctx) {
+ sr.unregister();
+ if (appDescTracker != null)
+ appDescTracker.close();
+ if (appHandleTracker != null)
+ appHandleTracker.close();
+ if (schedAppTracker != null)
+ schedAppTracker.close();
+ }
+
+ public String getHelp() {
+ return null;
+ }
+
+ private Dictionary getServiceProps(ServiceReference ref) {
+ String[] keys = ref.getPropertyKeys();
+ Hashtable props = new Hashtable(keys.length);
+ for (int i = 0; i < keys.length; i++)
+ props.put(keys[i], ref.getProperty(keys[i]));
+ return props;
+ }
+
+ public void _apps(CommandInterpreter intp) {
+ ServiceReference[] apps = appDescTracker.getServiceReferences();
+ if (apps == null) {
+ intp.println("No applications found."); //$NON-NLS-1$
+ return;
+ }
+ for (int i = 0; i < apps.length; i++) {
+ intp.print(apps[i].getProperty(ApplicationDescriptor.APPLICATION_PID));
+ intp.print(" ["); //$NON-NLS-1$
+ intp.print(launchableApp.match(getServiceProps(apps[i])) ? "enabled" : "disabled"); //$NON-NLS-1$ //$NON-NLS-2$
+ intp.println("]"); //$NON-NLS-1$
+ }
+ }
+
+ public void _activeApps(CommandInterpreter intp) {
+ ServiceReference[] active = appHandleTracker.getServiceReferences();
+ if (active == null) {
+ intp.println("No active applications found"); //$NON-NLS-1$
+ return;
+ }
+ for (int i = 0; i < active.length; i++) {
+ intp.print(active[i].getProperty(ApplicationHandle.APPLICATION_PID));
+ intp.print(" ["); //$NON-NLS-1$
+ intp.print(activeApp.match(getServiceProps(active[i])) ? "running" : "stopping"); //$NON-NLS-1$ //$NON-NLS-2$
+ intp.println("]"); //$NON-NLS-1$
+ }
+ }
+
+ public void _startApp(CommandInterpreter intp) throws Exception {
+ String appId = intp.nextArgument();
+ ServiceReference[] apps = appDescTracker.getServiceReferences();
+ if (apps != null)
+ for (int i = 0; i < apps.length; i++)
+ if (appId.equals(apps[i].getProperty(ApplicationDescriptor.APPLICATION_PID))) {
+ if (launchableApp.match(getServiceProps(apps[i]))) {
+ ApplicationDescriptor appDesc = (ApplicationDescriptor) context.getService(apps[i]);
+ try {
+ appDesc.launch(new HashMap(0));
+ intp.println("Launched application: " + appId); //$NON-NLS-1$
+ } finally {
+ context.ungetService(apps[i]);
+ }
+ } else {
+ intp.println("Application is not enabled: " + appId); //$NON-NLS-1$
+ }
+ return;
+ }
+ intp.println("No application with the id \"" + appId + "\" exists."); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ public void _stopApp(CommandInterpreter intp) throws Exception {
+ String runningId = intp.nextArgument();
+ ServiceReference[] runningApps = appHandleTracker.getServiceReferences();
+ if (runningApps != null)
+ for (int i = 0; i < runningApps.length; i++)
+ if (runningId.equals(runningApps[i].getProperty(ApplicationHandle.APPLICATION_PID))) {
+ if (activeApp.match(getServiceProps(runningApps[i]))) {
+ try {
+ ApplicationHandle appDesc = (ApplicationHandle) context.getService(runningApps[i]);
+ appDesc.destroy();
+ intp.println("Stopped application: " + runningId); //$NON-NLS-1$
+ } finally {
+ context.ungetService(runningApps[i]);
+ }
+ } else {
+ intp.println("Applicationi is already stopping: " + runningId); //$NON-NLS-1$
+ }
+ return;
+ }
+ intp.println("No running application with the id \"" + runningId + "\" exists."); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ public void _lockApp(CommandInterpreter intp) throws Exception {
+ String appId = intp.nextArgument();
+ ServiceReference[] apps = appDescTracker.getServiceReferences();
+ if (apps != null)
+ for (int i = 0; i < apps.length; i++)
+ if (appId.equals(apps[i].getProperty(ApplicationDescriptor.APPLICATION_PID))) {
+ try {
+ ApplicationDescriptor appDesc = (ApplicationDescriptor) context.getService(apps[i]);
+ appDesc.lock();
+ intp.println("Locked application: " + appId); //$NON-NLS-1$
+ } finally {
+ context.ungetService(apps[i]);
+ }
+ return;
+ }
+ intp.println("No application with the id \"" + appId + "\" exists."); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ public void _unlockApp(CommandInterpreter intp) throws Exception {
+ String appId = intp.nextArgument();
+ ServiceReference[] apps = appDescTracker.getServiceReferences();
+ if (apps != null)
+ for (int i = 0; i < apps.length; i++)
+ if (appId.equals(apps[i].getProperty(ApplicationDescriptor.APPLICATION_PID))) {
+ try {
+ ApplicationDescriptor appDesc = (ApplicationDescriptor) context.getService(apps[i]);
+ appDesc.unlock();
+ intp.println("Unlocked application: " + appId); //$NON-NLS-1$
+ } finally {
+ context.ungetService(apps[i]);
+ }
+ return;
+ }
+ intp.println("No application with the id \"" + appId + "\" exists."); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ public void _schedApp(CommandInterpreter intp) throws Exception {
+ String appId = intp.nextArgument();
+ ServiceReference[] apps = appDescTracker.getServiceReferences();
+ if (apps != null)
+ for (int i = 0; i < apps.length; i++)
+ if (appId.equals(apps[i].getProperty(ApplicationDescriptor.APPLICATION_PID))) {
+ try {
+ ApplicationDescriptor appDesc = (ApplicationDescriptor) context.getService(apps[i]);
+ String filter = intp.nextArgument();
+ boolean recure = Boolean.valueOf(intp.nextArgument()).booleanValue();
+ appDesc.schedule(null, "org/osgi/application/timer", filter, recure); //$NON-NLS-1$
+ intp.println("scheduled application: " + appId); //$NON-NLS-1$
+ } finally {
+ context.ungetService(apps[i]);
+ }
+ return;
+ }
+ intp.println("No application with the id \"" + appId + "\" exists."); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ public void _unschedApp(CommandInterpreter intp) throws Exception {
+ String schedId = intp.nextArgument();
+ ServiceReference[] scheds = schedAppTracker.getServiceReferences();
+ if (scheds != null)
+ for (int i = 0; i < scheds.length; i++) {
+ ScheduledApplication schedApp = (ScheduledApplication) context.getService(scheds[i]);
+ try {
+ if (schedId.equals(schedApp.getApplicationDescriptor().getApplicationId()))
+ schedApp.remove();
+ } finally {
+ context.ungetService(scheds[i]);
+ }
+ }
+
+ }
+}
diff --git a/bundles/org.eclipse.equinox.app/src/org/eclipse/equinox/internal/app/AppManager.java b/bundles/org.eclipse.equinox.app/src/org/eclipse/equinox/internal/app/AppManager.java
new file mode 100755
index 000000000..686abf9ac
--- /dev/null
+++ b/bundles/org.eclipse.equinox.app/src/org/eclipse/equinox/internal/app/AppManager.java
@@ -0,0 +1,552 @@
+/*******************************************************************************
+ * Copyright (c) 2005 IBM Corporation and others.
+ * 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:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.equinox.internal.app;
+
+import java.io.*;
+import java.util.*;
+import org.eclipse.core.runtime.adaptor.FileManager;
+import org.eclipse.osgi.service.datalocation.Location;
+import org.eclipse.osgi.service.environment.EnvironmentInfo;
+import org.osgi.framework.*;
+import org.osgi.service.application.ApplicationDescriptor;
+import org.osgi.service.application.ScheduledApplication;
+import org.osgi.service.event.*;
+import org.osgi.service.packageadmin.PackageAdmin;
+import org.osgi.util.tracker.ServiceTracker;
+/**
+ * Managers all persistent data for ApplicationDescriptors (lock status,
+ * scheduled applications etc.)
+ */
+public class AppManager {
+ private static final String PROP_CONFIG_AREA = "osgi.configuration.area"; //$NON-NLS-1$
+
+ private static final String FILTER_PREFIX = "(&(objectClass=org.eclipse.osgi.service.datalocation.Location)(type="; //$NON-NLS-1$
+ static final String FILE_APPLOCKS = ".locks"; //$NON-NLS-1$
+ static final String FILE_APPSCHEDULED = ".scheduled"; //$NON-NLS-1$
+ private static final String EVENT_HANDLER = "org.osgi.service.event.EventHandler"; //$NON-NLS-1$
+ private static final String EVENT_TIMER_TOPIC = "org/osgi/application/timer"; //$NON-NLS-1$
+
+ private static final int DATA_VERSION = 1;
+ private static final byte NULL = 0;
+ private static final int OBJECT = 1;
+
+ // obsolete command line args
+ private static final String NO_PACKAGE_PREFIXES = "-noPackagePrefixes"; //$NON-NLS-1$
+ private static final String CLASSLOADER_PROPERTIES = "-classloaderProperties"; //$NON-NLS-1$
+ private static final String PLUGINS = "-plugins"; //$NON-NLS-1$
+ private static final String FIRST_USE = "-firstUse"; //$NON-NLS-1$
+ private static final String NO_UPDATE = "-noUpdate"; //$NON-NLS-1$
+ private static final String NEW_UPDATES = "-newUpdates"; //$NON-NLS-1$
+ private static final String UPDATE = "-update"; //$NON-NLS-1$
+ private static final String BOOT = "-boot"; //$NON-NLS-1$
+
+ // command line args not used by app container
+ private static final String KEYRING = "-keyring"; //$NON-NLS-1$
+ private static final String PASSWORD = "-password"; //$NON-NLS-1$
+
+ // command line args used by app container
+ private static final String PRODUCT = "-product"; //$NON-NLS-1$
+ private static final String FEATURE = "-feature"; //$NON-NLS-1$
+ private static final String APPLICATION = "-application"; //$NON-NLS-1$
+
+ private static BundleContext context;
+ private static ServiceTracker configuration;
+ private static ServiceTracker pkgAdminTracker;
+ private static Collection locks = new ArrayList();
+ private static Map scheduledApps = new HashMap();
+ static ArrayList timerApps = new ArrayList();
+ private static FileManager fileManager;
+ private static boolean dirty;
+ private static boolean scheduling = false;
+ static boolean shutdown = false;
+ private static int nextScheduledID = 1;
+ private static Thread timerThread;
+ private static String[] appArgs;
+
+ static synchronized void setBundleContext(BundleContext context) {
+ if (context != null) {
+ AppManager.context = context;
+ init();
+ } else {
+ shutdown();
+ AppManager.context = context;
+ }
+ }
+
+ private static void init() {
+ shutdown = false;
+ appArgs = processCommandLine();
+ initPackageAdmin();
+ initConfiguration();
+ loadData(FILE_APPLOCKS);
+ }
+
+ private static void shutdown() {
+ shutdown = true;
+ stopTimer();
+ saveData();
+ if (fileManager != null) {
+ fileManager.close();
+ fileManager = null;
+ }
+ closeConfiguration();
+ closePackageAdmin();
+ appArgs = null;
+ }
+
+ private static void initPackageAdmin() {
+ closePackageAdmin(); // just incase
+ pkgAdminTracker = new ServiceTracker(context, PackageAdmin.class.getName(), null);
+ pkgAdminTracker.open();
+ }
+
+ private static void closePackageAdmin() {
+ if (pkgAdminTracker != null)
+ pkgAdminTracker.close();
+ pkgAdminTracker = null;
+ }
+
+ private static void initConfiguration() {
+ closeConfiguration(); // just incase
+ Filter filter = null;
+ try {
+ filter = context.createFilter(FILTER_PREFIX + PROP_CONFIG_AREA + "))"); //$NON-NLS-1$
+ } catch (InvalidSyntaxException e) {
+ // ignore this. It should never happen as we have tested the above format.
+ }
+ configuration = new ServiceTracker(context, filter, null);
+ configuration.open();
+ }
+
+ private static void closeConfiguration() {
+ if (configuration != null)
+ configuration.close();
+ configuration = null;
+ }
+
+ /**
+ * Used by {@link ApplicationDescriptor} to determine if an application is locked.
+ * @param desc the application descriptor
+ * @return true if the application is persistently locked.
+ */
+ public synchronized static boolean isLocked(ApplicationDescriptor desc) {
+ return locks.contains(desc.getApplicationId());
+ }
+
+ /**
+ * Used by {@link ApplicationDescriptor} to determine lock and unlock and application.
+ * @param desc the application descriptor
+ * @param locked the locked flag
+ */
+ public synchronized static void saveLock(ApplicationDescriptor desc, boolean locked) {
+ if (locked) {
+ if (!locks.contains(desc.getApplicationId())) {
+ dirty = true;
+ locks.add(desc.getApplicationId());
+ }
+ } else if (locks.remove(desc.getApplicationId())) {
+ dirty = true;
+ }
+ }
+
+ synchronized static void removeScheduledApp(EclipseScheduledApplication scheduledApp) {
+ if (scheduledApps.remove(scheduledApp.getID()) != null) {
+ timerApps.remove(scheduledApp);
+ dirty = true;
+ }
+ }
+
+ /**
+ * Used by {@link ScheduledApplication} to persistently schedule an application launch
+ * @param descriptor
+ * @param arguments
+ * @param topic
+ * @param eventFilter
+ * @param recurring
+ * @return the scheduled application
+ * @throws InvalidSyntaxException
+ */
+ public synchronized static ScheduledApplication addScheduledApp(ApplicationDescriptor descriptor, Map arguments, String topic, String eventFilter, boolean recurring) throws InvalidSyntaxException {
+ if (!scheduling && !checkSchedulingSupport())
+ throw new UnsupportedOperationException("Cannot support scheduling without org.osgi.service.event package"); //$NON-NLS-1$
+ // check the event filter for correct syntax
+ context.createFilter(eventFilter);
+ EclipseScheduledApplication result = new EclipseScheduledApplication(context, getNextScheduledID(), descriptor.getApplicationId(), arguments, topic, eventFilter, recurring);
+ addScheduledApp(result);
+ dirty = true;
+ return result;
+ }
+
+ static void addScheduledApp(EclipseScheduledApplication scheduledApp) {
+ if (EVENT_TIMER_TOPIC.equals(scheduledApp.getTopic())) {
+ timerApps.add(scheduledApp);
+ if (timerThread == null)
+ startTimer();
+ }
+ scheduledApps.put(scheduledApp.getID(), scheduledApp);
+ Hashtable serviceProps = new Hashtable();
+ if (scheduledApp.getTopic() != null)
+ serviceProps.put(EventConstants.EVENT_TOPIC ,new String[] {scheduledApp.getTopic()});
+ if (scheduledApp.getEventFilter() != null)
+ serviceProps.put(EventConstants.EVENT_FILTER, scheduledApp.getEventFilter());
+ ServiceRegistration sr = context.registerService(new String[] {ScheduledApplication.class.getName(), EVENT_HANDLER}, scheduledApp, serviceProps);
+ scheduledApp.setServiceRegistration(sr);
+ }
+
+ private static Integer getNextScheduledID() {
+ if (nextScheduledID == Integer.MAX_VALUE)
+ nextScheduledID = 0;
+ Integer result = new Integer(nextScheduledID++);
+ while (scheduledApps.get(result) != null && nextScheduledID < Integer.MAX_VALUE)
+ result = new Integer(nextScheduledID++);
+ if (nextScheduledID == Integer.MAX_VALUE)
+ throw new IllegalStateException("Maximum number of scheduled applications reached"); //$NON-NLS-1$
+ return result;
+ }
+
+ private static boolean checkSchedulingSupport() {
+ // cannot support scheduling without the event admin package
+ try {
+ Class.forName(EVENT_HANDLER);
+ scheduling = true;
+ return true;
+ } catch (ClassNotFoundException e) {
+ scheduling = false;
+ return false;
+ }
+ }
+
+ synchronized static boolean loadData(String fileName) {
+ try {
+ Location location = (Location) configuration.getService();
+ if (location == null)
+ return false;
+ File theStorageDir = new File(location.getURL().getPath() + '/' + Activator.PI_APP);
+ boolean readOnly = location.isReadOnly();
+ fileManager = new FileManager(theStorageDir, readOnly ? "none" : null, readOnly); //$NON-NLS-1$
+ fileManager.open(!readOnly);
+ File dataFile = fileManager.lookup(fileName, false);
+ if (dataFile == null || !dataFile.isFile()) {
+ Location parent = location.getParentLocation();
+ if (parent != null) {
+ theStorageDir = new File(parent.getURL().getPath() + '/' + Activator.PI_APP);
+ FileManager tmp = new FileManager(theStorageDir, "none", true); //$NON-NLS-1$
+ tmp.open(false);
+ dataFile = tmp.lookup(fileName, false);
+ tmp.close();
+ }
+ }
+ if (dataFile == null || !dataFile.isFile())
+ return true;
+ if (fileName.equals(FILE_APPLOCKS))
+ loadLocks(dataFile);
+ else if (fileName.equals(FILE_APPSCHEDULED))
+ loadSchedules(dataFile);
+ } catch (IOException e) {
+ return false;
+ }
+ return true;
+ }
+
+ private static void loadLocks(File locksData) throws IOException {
+ ObjectInputStream in = null;
+ try {
+ in = new ObjectInputStream(new FileInputStream(locksData));
+ int dataVersion = in.readInt();
+ if (dataVersion != DATA_VERSION)
+ return;
+ int numLocks = in.readInt();
+ for (int i = 0; i < numLocks; i++)
+ locks.add(in.readUTF());
+ } finally {
+ if (in != null)
+ in.close();
+ }
+ }
+
+ private static void loadSchedules(File schedulesData) throws IOException {
+ ObjectInputStream in = null;
+ try {
+ in = new ObjectInputStream(new FileInputStream(schedulesData));
+ int dataVersion = in.readInt();
+ if (dataVersion != DATA_VERSION)
+ return;
+ int numScheds = in.readInt();
+ for (int i = 0; i < numScheds; i++) {
+ Integer id = new Integer(in.readInt());
+ String appPid = readString(in, false);
+ String topic = readString(in, false);
+ String eventFilter = readString(in, false);
+ boolean recurring = in.readBoolean();
+ Map args = (Map) in.readObject();
+ EclipseScheduledApplication schedApp = new EclipseScheduledApplication(context, id, appPid, args, topic, eventFilter, recurring);
+ addScheduledApp(schedApp);
+ }
+ } catch (InvalidSyntaxException e) {
+ throw new IOException(e.getMessage());
+ } catch (NoClassDefFoundError e) {
+ throw new IOException(e.getMessage());
+ } catch (ClassNotFoundException e) {
+ throw new IOException(e.getMessage());
+ } finally {
+ if (in != null)
+ in.close();
+ }
+ }
+
+ private synchronized static void saveData() {
+ if (!dirty || fileManager.isReadOnly())
+ return;
+ try {
+ File locksData = fileManager.createTempFile(FILE_APPLOCKS);
+ saveLocks(locksData);
+ File schedulesData = fileManager.createTempFile(FILE_APPSCHEDULED);
+ saveSchedules(schedulesData);
+ fileManager.lookup(FILE_APPLOCKS, true);
+ fileManager.lookup(FILE_APPSCHEDULED, true);
+ fileManager.update(new String[] {FILE_APPLOCKS, FILE_APPSCHEDULED}, new String[] {locksData.getName(), schedulesData.getName()});
+ } catch (IOException e) {
+ // TODO should log this!!
+ }
+ dirty = false;
+ }
+
+ private static void saveLocks(File locksData) throws IOException {
+ ObjectOutputStream out = null;
+ try {
+ out = new ObjectOutputStream(new FileOutputStream(locksData));
+ out.writeInt(DATA_VERSION);
+ out.writeInt(locks.size());
+ for (Iterator iterLocks = locks.iterator(); iterLocks.hasNext();)
+ out.writeUTF((String) iterLocks.next());
+ } finally {
+ if (out != null)
+ out.close();
+ }
+ }
+
+ private static void saveSchedules(File schedulesData) throws IOException {
+ ObjectOutputStream out = null;
+ try {
+ out = new ObjectOutputStream(new FileOutputStream(schedulesData));
+ out.writeInt(DATA_VERSION);
+ out.writeInt(scheduledApps.size());
+ for (Iterator apps = scheduledApps.values().iterator(); apps.hasNext();) {
+ EclipseScheduledApplication app = (EclipseScheduledApplication) apps.next();
+ out.writeInt(app.getID().intValue());
+ writeStringOrNull(out, app.getAppPid());
+ writeStringOrNull(out, app.getTopic());
+ writeStringOrNull(out, app.getEventFilter());
+ out.writeBoolean(app.isRecurring());
+ out.writeObject(app.getArguments());
+ }
+ } finally {
+ if (out != null)
+ out.close();
+ }
+ }
+
+ private static void startTimer() {
+ timerThread = new Thread(new AppTimer(), "app schedule timer"); //$NON-NLS-1$
+ timerThread.start();
+ }
+
+ private static void stopTimer() {
+ if (timerThread != null)
+ timerThread.interrupt();
+ timerThread = null;
+ }
+
+ private static class AppTimer implements Runnable {
+ public void run() {
+ int lastMin = -1;
+ while (!shutdown) {
+ try {
+ Thread.sleep(30000); // sleeping 30 secs instead of 60 to try to avoid skipping minutes
+ Calendar cal = Calendar.getInstance();
+ int minute = cal.get(Calendar.MINUTE);
+ if (minute == lastMin)
+ continue;
+ lastMin = minute;
+ Hashtable props = new Hashtable();
+ props.put("year", new Integer(cal.get(Calendar.YEAR))); //$NON-NLS-1$
+ props.put("month", new Integer(cal.get(Calendar.MONTH))); //$NON-NLS-1$
+ props.put("day_of_month", new Integer(cal.get(Calendar.DAY_OF_MONTH))); //$NON-NLS-1$
+ props.put("day_of_week", new Integer(cal.get(Calendar.DAY_OF_WEEK))); //$NON-NLS-1$
+ props.put("hour_of_day", new Integer(cal.get(Calendar.HOUR_OF_DAY))); //$NON-NLS-1$
+ props.put("minute", new Integer(minute)); //$NON-NLS-1$
+ Event timerEvent = new Event(EVENT_TIMER_TOPIC, props);
+ synchronized (AppManager.class) {
+ // poor mans implementation of dispatching events; the spec will not allow us to use event admin to dispatch the virtual timer events; boo!!
+ if (timerApps.size() == 0)
+ continue;
+ EclipseScheduledApplication[] apps = (EclipseScheduledApplication[]) timerApps.toArray(new EclipseScheduledApplication[timerApps.size()]);
+ for (int i = 0; i < apps.length; i++) {
+ try {
+ String filterString = apps[i].getEventFilter();
+ Filter filter = filterString == null ? null : FrameworkUtil.createFilter(filterString);
+ if (filter == null || filter.match(props))
+ apps[i].handleEvent(timerEvent);
+ } catch (Throwable t) {
+ t.printStackTrace();
+ // TODO should log this
+ }
+ }
+ }
+ } catch (InterruptedException e) {
+ // do nothing;
+ }
+ }
+ }
+ }
+
+ private static String readString(ObjectInputStream in, boolean intern) throws IOException {
+ byte type = in.readByte();
+ if (type == NULL)
+ return null;
+ return intern ? in.readUTF().intern() : in.readUTF();
+ }
+
+ private static void writeStringOrNull(ObjectOutputStream out, String string) throws IOException {
+ if (string == null)
+ out.writeByte(NULL);
+ else {
+ out.writeByte(OBJECT);
+ out.writeUTF(string);
+ }
+ }
+
+ public static Bundle getBundle(String symbolicName) {
+ PackageAdmin packageAdmin = (PackageAdmin) pkgAdminTracker.getService();
+ if (packageAdmin == null)
+ return null;
+ Bundle[] bundles = packageAdmin.getBundles(symbolicName, null);
+ if (bundles == null)
+ return null;
+ //Return the first bundle that is not installed or uninstalled
+ for (int i = 0; i < bundles.length; i++) {
+ if ((bundles[i].getState() & (Bundle.INSTALLED | Bundle.UNINSTALLED)) == 0) {
+ return bundles[i];
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the default application args that should be used to launch an application
+ * when args are not supplied.
+ * @return the default application args.
+ */
+ public static String[] getApplicationArgs() {
+ return appArgs;
+ }
+
+ private static String[] processCommandLine() {
+ ServiceReference infoRef = context.getServiceReference(EnvironmentInfo.class.getName());
+ if (infoRef == null)
+ return null;
+ EnvironmentInfo envInfo = (EnvironmentInfo) context.getService(infoRef);
+ if (envInfo == null)
+ return null;
+ String[] args = envInfo.getNonFrameworkArgs();
+ context.ungetService(infoRef);
+ if (args == null)
+ return args;
+ if (args.length == 0)
+ return args;
+
+ int[] configArgs = new int[args.length];
+ //need to initialize the first element to something that could not be an index.
+ configArgs[0] = -1;
+ int configArgIndex = 0;
+ for (int i = 0; i < args.length; i++) {
+ boolean found = false;
+ // check for args without parameters (i.e., a flag arg)
+
+ // consume obsolete args
+ if (args[i].equalsIgnoreCase(CLASSLOADER_PROPERTIES))
+ found = true; // ignored
+ if (args[i].equalsIgnoreCase(NO_PACKAGE_PREFIXES))
+ found = true; // ignored
+ if (args[i].equalsIgnoreCase(PLUGINS))
+ found = true; // ignored
+ if (args[i].equalsIgnoreCase(FIRST_USE))
+ found = true; // ignored
+ if (args[i].equalsIgnoreCase(NO_UPDATE))
+ found = true; // ignored
+ if (args[i].equalsIgnoreCase(NEW_UPDATES))
+ found = true; // ignored
+ if (args[i].equalsIgnoreCase(UPDATE))
+ found = true; // ignored
+
+ // done checking for args. Remember where an arg was found
+ if (found) {
+ configArgs[configArgIndex++] = i;
+ continue;
+ }
+ // check for args with parameters
+ if (i == args.length - 1 || args[i + 1].startsWith("-")) //$NON-NLS-1$
+ continue;
+ String arg = args[++i];
+
+ // consume args not used by app container
+ if (args[i - 1].equalsIgnoreCase(KEYRING))
+ found = true;
+ if (args[i - 1].equalsIgnoreCase(PASSWORD))
+ found = true;
+
+ // consume obsolete args for compatibilty
+ if (args[i - 1].equalsIgnoreCase(CLASSLOADER_PROPERTIES))
+ found = true; // ignore
+ if (args[i - 1].equalsIgnoreCase(BOOT))
+ found = true; // ignore
+
+
+ // look for the product to run
+ // treat -feature as a synonym for -product for compatibility.
+ if (args[i - 1].equalsIgnoreCase(PRODUCT) || args[i - 1].equalsIgnoreCase(FEATURE)) {
+ // use the long way to set the property to compile against eeminimum
+ System.getProperties().setProperty(ContainerManager.PROP_PRODUCT, arg);
+ found = true;
+ }
+
+ // look for the application to run.
+ if (args[i - 1].equalsIgnoreCase(APPLICATION)) {
+ // use the long way to set the property to compile against eeminimum
+ System.getProperties().setProperty(ContainerManager.PROP_ECLIPSE_APPLICATION, arg);
+ found = true;
+ }
+
+ // done checking for args. Remember where an arg was found
+ if (found) {
+ configArgs[configArgIndex++] = i - 1;
+ configArgs[configArgIndex++] = i;
+ }
+ }
+
+ // remove all the arguments consumed by this argument parsing
+ if (configArgIndex == 0) {
+ appArgs = args;
+ return args;
+ }
+ appArgs = new String[args.length - configArgIndex];
+ configArgIndex = 0;
+ int j = 0;
+ for (int i = 0; i < args.length; i++) {
+ if (i == configArgs[configArgIndex])
+ configArgIndex++;
+ else
+ appArgs[j++] = args[i];
+ }
+ return appArgs;
+ }
+}
diff --git a/bundles/org.eclipse.equinox.app/src/org/eclipse/equinox/internal/app/ContainerManager.java b/bundles/org.eclipse.equinox.app/src/org/eclipse/equinox/internal/app/ContainerManager.java
new file mode 100755
index 000000000..189479aac
--- /dev/null
+++ b/bundles/org.eclipse.equinox.app/src/org/eclipse/equinox/internal/app/ContainerManager.java
@@ -0,0 +1,446 @@
+/*******************************************************************************
+ * Copyright (c) 2005 IBM Corporation and others.
+ * 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:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.equinox.internal.app;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.*;
+import org.eclipse.core.runtime.*;
+import org.eclipse.equinox.app.IContainer;
+import org.eclipse.equinox.registry.*;
+import org.eclipse.osgi.framework.log.FrameworkLog;
+import org.eclipse.osgi.framework.log.FrameworkLogEntry;
+import org.eclipse.osgi.util.NLS;
+import org.osgi.framework.*;
+import org.osgi.service.application.ApplicationDescriptor;
+import org.osgi.service.application.ApplicationHandle;
+import org.osgi.util.tracker.ServiceTracker;
+
+/*
+ * A MEG application container that understands eclipse applications. This
+ * container will discover installed eclipse applications and register the
+ * appropriate ApplicatoinDescriptor service with the service registry.
+ */
+public class ContainerManager implements IRegistryChangeListener, SynchronousBundleListener {
+ private static final String PI_RUNTIME = "org.eclipse.core.runtime"; //$NON-NLS-1$
+ private static final String PT_APPLICATIONS = "applications"; //$NON-NLS-1$
+ private static final String PT_APP_TYPE = "type"; //$NON-NLS-1$
+ private static final String PT_RUN = "run"; //$NON-NLS-1$
+ private static final String PT_PRODUCTS = "products"; //$NON-NLS-1$
+ private static final String PT_CONTAINERS = "containers"; //$NON-NLS-1$
+ private static final String ATTR_APPLICATION = "application"; //$NON-NLS-1$
+ static final String APP_TYPE_MAIN_SINGLETON = "main.singleton"; //$NON-NLS-1$
+
+ public static final String PROP_PRODUCT = "eclipse.product"; //$NON-NLS-1$
+ public static final String PROP_ECLIPSE_APPLICATION = "eclipse.application"; //$NON-NLS-1$
+ public static final String PROP_ECLIPSE_APPLICATION_ARGS = "eclipse.application.args"; //$NON-NLS-1$
+ public static final String PROP_ECLIPSE_APPLICATION_NODEFAULT = "eclipse.application.noDefault"; //$NON-NLS-1$
+
+ BundleContext context;
+ // A map of ApplicationDescriptors keyed by eclipse application ID
+ private HashMap apps = new HashMap();
+ // A map of containers keyed by application type
+ private HashMap containers = new HashMap();
+ // tracks the extension registry
+ private ServiceTracker registryTracker;
+ // tracks the FrameworkLog
+ private ServiceTracker frameworkLog;
+ private boolean missingProductReported;
+
+ public ContainerManager(BundleContext context) {
+ this.context = context;
+ }
+
+ void startContainer() {
+ frameworkLog = new ServiceTracker(context, FrameworkLog.class.getName(), null);
+ frameworkLog.open();
+ registryTracker = new ServiceTracker(context, IExtensionRegistry.class.getName(), null);
+ registryTracker.open();
+ getExtensionRegistry().addRegistryChangeListener(this);
+ registerAppDecriptors();
+ getContainers();
+ // need to load scheduled app data after app descriptors have been registered
+ AppManager.loadData(AppManager.FILE_APPSCHEDULED);
+ containers.put(APP_TYPE_MAIN_SINGLETON, new SingletonContainerMgr(new MainSingletonContainer(this), APP_TYPE_MAIN_SINGLETON, this));
+ // need to listen for system bundle stopping
+ context.addBundleListener(this);
+ }
+
+ void stopContainer() {
+ // stop all applications first
+ stopAllApplications();
+ context.removeBundleListener(this);
+ getExtensionRegistry().removeRegistryChangeListener(this);
+ registryTracker.close();
+ registryTracker = null;
+ frameworkLog.close();
+ frameworkLog = null;
+ // flush the apps and containers
+ apps.clear();
+ containers.clear();
+ }
+
+ /*
+ * Returns the ApplicationDescriptor for the given application ID. An
+ * ApplicationDescriptor object will get created if it does not already
+ * exist in the cache and the create flag is true. When an ApplicationDescriptor
+ * is created for the first time it is registered as an OSGi service.
+ */
+ private EclipseAppDescriptor getAppDescriptor(String applicationId) {
+ EclipseAppDescriptor result = null;
+ synchronized (apps) {
+ result = (EclipseAppDescriptor) apps.get(applicationId);
+ }
+ if (result == null) {
+ registerAppDecriptors(); // try again just in case we are waiting for an event
+ synchronized (apps) {
+ result = (EclipseAppDescriptor) apps.get(applicationId);
+ }
+ }
+ return result;
+ }
+
+ EclipseAppDescriptor[] getAppDescriptorsByType(String type) {
+ ArrayList result = new ArrayList();
+ synchronized (apps) {
+ for (Iterator iApps = apps.values().iterator(); iApps.hasNext();) {
+ EclipseAppDescriptor app = (EclipseAppDescriptor) iApps.next();
+ if (type.equals(app.getType()))
+ result.add(app);
+ }
+ }
+ return (EclipseAppDescriptor[]) result.toArray(new EclipseAppDescriptor[result.size()]);
+ }
+
+ private IContainer getContainer(String type) {
+ synchronized (containers) {
+ return (IContainer) containers.get(type);
+ }
+ }
+
+ private EclipseAppDescriptor createAppDescriptor(IExtension appExtension) {
+ synchronized (apps) {
+ EclipseAppDescriptor appDescriptor = (EclipseAppDescriptor) apps.get(appExtension.getUniqueIdentifier());
+ if (appDescriptor != null)
+ return appDescriptor;
+ // the appDescriptor does not exist for the app ID; create it
+ IConfigurationElement[] configs = appExtension.getConfigurationElements();
+ String type = null;
+ if (configs.length > 0)
+ type = configs[0].getAttribute(PT_APP_TYPE);
+ appDescriptor = new EclipseAppDescriptor(appExtension.getNamespace(), appExtension.getUniqueIdentifier(), type, this);
+ // register the appDescriptor as a service
+ ServiceRegistration sr = (ServiceRegistration) AccessController.doPrivileged(new RegisterService(ApplicationDescriptor.class.getName(), appDescriptor, appDescriptor.getServiceProperties()));
+ appDescriptor.setServiceRegistration(sr);
+ // save the app descriptor in the cache
+ apps.put(appExtension.getUniqueIdentifier(), appDescriptor);
+ return appDescriptor;
+ }
+ }
+
+ private EclipseAppDescriptor removeAppDescriptor(String applicationId) {
+ synchronized (apps) {
+ EclipseAppDescriptor appDescriptor = (EclipseAppDescriptor) apps.remove(applicationId);
+ if (appDescriptor == null)
+ return null;
+ appDescriptor.unregister();
+ return appDescriptor;
+ }
+ }
+
+ /*
+ * Gives access to the RegisterService privileged action.
+ */
+ PrivilegedAction getRegServiceAction(String serviceClass, Object serviceObject, Dictionary serviceProps) {
+ return new RegisterService(serviceClass, serviceObject, serviceProps);
+ }
+
+ /*
+ * PrivilegedAction used to register ApplicationDescriptor and ApplicationHandle services
+ */
+ private class RegisterService implements PrivilegedAction {
+ String serviceClass;
+ Object serviceObject;
+ Dictionary serviceProps;
+ RegisterService(String serviceClass, Object serviceObject, Dictionary serviceProps) {
+ this.serviceClass = serviceClass;
+ this.serviceObject = serviceObject;
+ this.serviceProps = serviceProps;
+ }
+ public Object run() {
+ return context.registerService(serviceClass, serviceObject, serviceProps);
+ }
+ }
+
+ EclipseAppDescriptor findDefaultApp() {
+ String applicationId = System.getProperty(PROP_ECLIPSE_APPLICATION);
+ if (applicationId == null) {
+ //Derive the application from the product information
+ applicationId = getProductAppId();
+ if (applicationId != null)
+ // use the long way to set the property to compile against eeminimum
+ System.getProperties().setProperty(PROP_ECLIPSE_APPLICATION, applicationId);
+ }
+ if (applicationId == null)
+ // the application id is not set; return a descriptor that will throw an exception
+ return new EclipseAppDescriptor(Activator.PI_APP, Activator.PI_APP + ".missingapp", null, this, new RuntimeException(Messages.application_noIdFound)); //$NON-NLS-1$
+ EclipseAppDescriptor defaultApp = getAppDescriptor(applicationId);
+ if (defaultApp == null)
+ // the application id is not available in the registry; return a descriptor that will throw an exception
+ return new EclipseAppDescriptor(applicationId, applicationId, null, this, new RuntimeException(NLS.bind(Messages.application_notFound, applicationId, getAvailableAppsMsg(getExtensionRegistry()))));
+ return defaultApp;
+ }
+
+ /*
+ * Registers an ApplicationDescriptor service for each eclipse application
+ * available in the extension registry.
+ */
+ private void registerAppDecriptors() {
+ IExtension[] availableApps = getAvailableApps(getExtensionRegistry(), PI_RUNTIME);
+ for (int i = 0; i < availableApps.length; i++)
+ createAppDescriptor(availableApps[i]);
+ availableApps = getAvailableApps(getExtensionRegistry(), Activator.PI_APP);
+ for (int i = 0; i < availableApps.length; i++)
+ createAppDescriptor(availableApps[i]);
+ }
+
+ private void getContainers() {
+ IExtensionPoint extPoint = getExtensionRegistry().getExtensionPoint(Activator.PI_APP, PT_CONTAINERS);
+ if (extPoint == null)
+ return;
+ IExtension[] availableContainers = extPoint.getExtensions();
+ for (int i = 0; i < availableContainers.length; i++)
+ createContainer(availableContainers[i]);
+ }
+
+ private IContainer createContainer(IExtension containerExt) {
+ synchronized (containers) {
+ IConfigurationElement[] configs = containerExt.getConfigurationElements();
+ if (configs.length == 0)
+ return null;
+ String type = configs[0].getAttribute(PT_APP_TYPE);
+ if (type == null)
+ return null; // ignore containers which do not spec a type
+ IContainer container = (IContainer) containers.get(type);
+ if (container != null)
+ return container;
+ try {
+ container = (IContainer) configs[0].createExecutableExtension(PT_RUN);
+ if (container.isSingletonContainer())
+ container = new SingletonContainerMgr(container, type, this);
+ containers.put(type, container);
+ return container;
+ } catch (CoreException e) {
+ // TODO should log this
+ e.printStackTrace();
+ }
+ return null;
+ }
+ }
+
+ private IContainer removeContainer(IExtension containerExt) {
+ IConfigurationElement[] configs = containerExt.getConfigurationElements();
+ if (configs.length == 0)
+ return null;
+ String type = configs[0].getAttribute(PT_APP_TYPE);
+ if (type == null)
+ return null;
+ synchronized (containers) {
+ return (IContainer) containers.get(type);
+ }
+ }
+
+ /*
+ * Returns a list of all the available application IDs which are available
+ * in the extension registry.
+ */
+ private IExtension[] getAvailableApps(IExtensionRegistry registry, String pi) {
+ IExtensionPoint point = registry.getExtensionPoint(pi + '.' + PT_APPLICATIONS);
+ if (point == null)
+ return new IExtension[0];
+ return point.getExtensions();
+ }
+
+ private String getAvailableAppsMsg(IExtensionRegistry registry) {
+ IExtension[] availableApps = getAvailableApps(registry, PI_RUNTIME);
+ String availableAppsMsg = "<NONE>"; //$NON-NLS-1$
+ if (availableApps.length != 0) {
+ availableAppsMsg = availableApps[0].getUniqueIdentifier();
+ for (int i = 1; i < availableApps.length; i++)
+ availableAppsMsg = availableAppsMsg + ", " + availableApps[i].getUniqueIdentifier(); //$NON-NLS-1$
+ }
+ availableApps = getAvailableApps(registry, Activator.PI_APP);
+ if (availableApps.length != 0) {
+ if (!availableAppsMsg.equals("<NONE>")) //$NON-NLS-1$
+ availableAppsMsg = availableAppsMsg + ", "; //$NON-NLS-1$
+ availableAppsMsg = availableAppsMsg + availableApps[0].getUniqueIdentifier();
+ for (int i = 1; i < availableApps.length; i++)
+ availableAppsMsg = availableAppsMsg + ", " + availableApps[i].getUniqueIdentifier(); //$NON-NLS-1$
+ }
+ return availableAppsMsg;
+ }
+
+ /*
+ * Returns the application extension for the specified applicaiton ID.
+ * A RuntimeException is thrown if the extension does not exist for the
+ * given application ID.
+ */
+ IExtension getAppExtension(String applicationId) {
+ IExtensionRegistry registry = getExtensionRegistry();
+ IExtension applicationExtension = registry.getExtension(PI_RUNTIME, PT_APPLICATIONS, applicationId);
+ if (applicationExtension == null)
+ applicationExtension = registry.getExtension(Activator.PI_APP, PT_APPLICATIONS, applicationId);
+ if (applicationExtension == null)
+ throw new RuntimeException(NLS.bind(Messages.application_notFound, applicationId, getAvailableAppsMsg(registry)));
+ return applicationExtension;
+ }
+
+ private IExtensionRegistry getExtensionRegistry() {
+ return (IExtensionRegistry) registryTracker.getService();
+ }
+
+ private FrameworkLog getFrameworkLog() {
+ return (FrameworkLog) frameworkLog.getService();
+ }
+
+ BundleContext getBundleContext() {
+ return context;
+ }
+
+ private String getProductAppId() {
+ String productId = System.getProperty(PROP_PRODUCT);
+ if (productId == null)
+ return null;
+ IConfigurationElement[] entries = getExtensionRegistry().getConfigurationElementsFor(PI_RUNTIME, PT_PRODUCTS, productId);
+ if (entries.length > 0)
+ // There should only be one product with the given id so just take the first element
+ return entries[0].getAttribute(ATTR_APPLICATION);
+ IConfigurationElement[] elements = getExtensionRegistry().getConfigurationElementsFor(PI_RUNTIME, PT_PRODUCTS);
+ List logEntries = null;
+ for (int i = 0; i < elements.length; i++) {
+ IConfigurationElement element = elements[i];
+ if (element.getName().equalsIgnoreCase("provider")) { //$NON-NLS-1$
+ try {
+ Object provider = element.createExecutableExtension(PT_RUN);
+ Object[] products = (Object[]) ContainerManager.execMethod(provider, "getProducts", null, null); //$NON-NLS-1$
+ for (int j = 0; j < products.length; j++) {
+ Object provided = products[j];
+ if (productId.equalsIgnoreCase((String) ContainerManager.execMethod(provided, "getId", null, null))) //$NON-NLS-1$
+ return (String) ContainerManager.execMethod(provided, "getApplication", null, null); //$NON-NLS-1$
+ }
+ } catch (CoreException e) {
+ if (logEntries == null)
+ logEntries = new ArrayList(3);
+ logEntries.add(new FrameworkLogEntry(Activator.PI_APP, NLS.bind(Messages.provider_invalid, element.getParent().toString()), 0, e, null));
+ }
+ }
+ }
+ if (logEntries != null)
+ getFrameworkLog().log(new FrameworkLogEntry(Activator.PI_APP, Messages.provider_invalid_general, 0, null, (FrameworkLogEntry[]) logEntries.toArray()));
+
+ if (!missingProductReported) {
+ getFrameworkLog().log(new FrameworkLogEntry(Activator.PI_APP, NLS.bind(Messages.product_notFound, productId), 0, null, null));
+ missingProductReported = true;
+ }
+ return null;
+ }
+
+ public static Object execMethod(Object obj, String methodName, Class argType, Object arg) {
+ try {
+ Method method = obj.getClass().getMethod(methodName, argType == null ? null : new Class[] {argType});
+ return method.invoke(obj, arg == null ? null : new Object[] {arg});
+ } catch (NoSuchMethodException e) {
+ throw new IllegalArgumentException(e.getMessage());
+ } catch (IllegalArgumentException e) {
+ throw new IllegalArgumentException(e.getMessage());
+ } catch (IllegalAccessException e) {
+ throw new IllegalArgumentException(e.getMessage());
+ } catch (InvocationTargetException e) {
+ throw new IllegalArgumentException(e.getMessage());
+ }
+ }
+
+ void launch(EclipseAppHandle appHandle) throws Exception {
+ String type = ((EclipseAppDescriptor)appHandle.getApplicationDescriptor()).getType();
+ if (type == null)
+ type = APP_TYPE_MAIN_SINGLETON;
+ IContainer container = getContainer(type);
+ if (container != null) {
+ if (container instanceof SingletonContainerMgr)
+ ((SingletonContainerMgr) container).lock(appHandle);
+ appHandle.setApplication(container.launch(appHandle));
+ } else
+ throw new UnsupportedOperationException(NLS.bind(Messages.container_notFound, appHandle.getApplicationDescriptor().getApplicationId(), type));
+ }
+
+ public void registryChanged(IRegistryChangeEvent event) {
+ processAppDeltas(event.getExtensionDeltas(PI_RUNTIME, PT_APPLICATIONS));
+ processAppDeltas(event.getExtensionDeltas(Activator.PI_APP, PT_APPLICATIONS));
+ processContainerDeltas(event.getExtensionDeltas(Activator.PI_APP, PT_CONTAINERS));
+ }
+
+ private void processAppDeltas(IExtensionDelta[] deltas) {
+ for (int i = 0; i < deltas.length; i++) {
+ switch (deltas[i].getKind()) {
+ case IExtensionDelta.ADDED :
+ createAppDescriptor(deltas[i].getExtension());
+ break;
+ case IExtensionDelta.REMOVED :
+ removeAppDescriptor(deltas[i].getExtension().getUniqueIdentifier());
+ break;
+ }
+ }
+ }
+
+ private void processContainerDeltas(IExtensionDelta[] deltas) {
+ for (int i = 0; i < deltas.length; i++) {
+ switch (deltas[i].getKind()) {
+ case IExtensionDelta.ADDED :
+ createContainer(deltas[i].getExtension());
+ break;
+ case IExtensionDelta.REMOVED :
+ removeContainer(deltas[i].getExtension());
+ break;
+ }
+ }
+ }
+
+ public void bundleChanged(BundleEvent event) {
+ // if this is not the system bundle stopping then ignore the event
+ if ((BundleEvent.STOPPING & event.getType()) == 0 || event.getBundle().getBundleId() != 0)
+ return;
+ // The system bundle is stopping; better stop all applications now
+ stopAllApplications();
+ }
+
+ private void stopAllApplications() {
+ // get a stapshot of running applications
+ try {
+ ServiceReference[] runningRefs = context.getServiceReferences(ApplicationHandle.class.getName(), "(!(application.state=STOPPING))"); //$NON-NLS-1$
+ if (runningRefs == null)
+ return;
+ for (int i = 0; i <runningRefs.length; i++) {
+ ApplicationHandle handle = (ApplicationHandle) context.getService(runningRefs[i]);
+ try {
+ handle.destroy();
+ } catch (Throwable t) {
+ // TODO should log this
+ }
+ }
+ } catch (InvalidSyntaxException e) {
+ // do nothing; we already tested the filter string above
+ }
+
+ }
+}
diff --git a/bundles/org.eclipse.equinox.app/src/org/eclipse/equinox/internal/app/EclipseAppDescriptor.java b/bundles/org.eclipse.equinox.app/src/org/eclipse/equinox/internal/app/EclipseAppDescriptor.java
new file mode 100755
index 000000000..5bccabd7a
--- /dev/null
+++ b/bundles/org.eclipse.equinox.app/src/org/eclipse/equinox/internal/app/EclipseAppDescriptor.java
@@ -0,0 +1,155 @@
+/*******************************************************************************
+ * Copyright (c) 2005 IBM Corporation and others.
+ * 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:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.equinox.internal.app;
+
+import java.security.AccessController;
+import java.util.*;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.application.ApplicationDescriptor;
+import org.osgi.service.application.ApplicationHandle;
+import org.osgi.service.condpermadmin.BundleSignerCondition;
+import org.osgi.service.condpermadmin.ConditionInfo;
+
+/*
+ * An ApplicationDescriptor for an eclispe application.
+ */
+public class EclipseAppDescriptor extends ApplicationDescriptor {
+ private ServiceRegistration sr;
+ private Boolean locked = Boolean.FALSE;
+ private SingletonContainerMgr singletonMgr;
+ private ContainerManager containerMgr;
+ private String namespace;
+ private String type;
+ private RuntimeException error;
+
+ protected EclipseAppDescriptor(String namespace, String pid, String type, ContainerManager containerMgr) {
+ super(pid);
+ this.type = type == null ? ContainerManager.APP_TYPE_MAIN_SINGLETON : type;
+ this.namespace = namespace;
+ this.containerMgr = containerMgr;
+ this.locked = AppManager.isLocked(this) ? Boolean.TRUE : Boolean.FALSE;
+ }
+
+ protected EclipseAppDescriptor(String namespace, String pid, String type, ContainerManager containerMgr, RuntimeException error) {
+ this(namespace, pid, type, containerMgr);
+ this.error = error;
+ }
+ protected Map getPropertiesSpecific(String locale) {
+ // just use the service properties; for now we do not localize any properties
+ return getServiceProperties();
+ }
+
+ protected synchronized ApplicationHandle launchSpecific(Map arguments) throws Exception {
+ // we only allow one application to run at a time; do not launch an application
+ // if one is already launched or if this application is locked.
+ if (locked.booleanValue())
+ throw new IllegalStateException("Cannot launch a locked application."); //$NON-NLS-1$
+ // initialize the appHandle
+ EclipseAppHandle appHandle = createAppHandle();
+ appHandle.setArguments(arguments);
+ try {
+ // use the containerMgr to launch the application on the main thread.
+ containerMgr.launch(appHandle);
+ } catch (Throwable t) {
+ // be sure to destroy the appHandle if an error occurs
+ appHandle.destroy();
+ if (t instanceof Exception)
+ throw (Exception)t;
+ throw (Error) t;
+ }
+ return appHandle;
+ }
+
+ protected synchronized void lockSpecific() {
+ locked = Boolean.TRUE;
+ // make sure the service properties are updated with the latest lock info
+ refreshProperties();
+ }
+
+ protected synchronized void unlockSpecific() {
+ locked = Boolean.FALSE;
+ // make sure the service properties are updated with the latest lock info
+ refreshProperties();
+ }
+
+ /*
+ * Indicates to this descriptor that the appHandle was destroyed
+ */
+ synchronized void appHandleDestroyed() {
+ if (singletonMgr != null)
+ singletonMgr.unlock();
+ }
+
+ void refreshProperties() {
+ if (sr != null)
+ sr.setProperties(getServiceProperties());
+ }
+
+ void setSingletonMgr(SingletonContainerMgr singletonMgr) {
+ this.singletonMgr = singletonMgr;
+ }
+
+ void setServiceRegistration(ServiceRegistration sr) {
+ this.sr = sr;
+ }
+
+ /*
+ * Gets a snapshot of the current service properties.
+ */
+ Hashtable getServiceProperties() {
+ Hashtable props = new Hashtable(10);
+ props.put(ApplicationDescriptor.APPLICATION_PID, getApplicationId());
+ props.put(ApplicationDescriptor.APPLICATION_CONTAINER, Activator.PI_APP);
+ props.put(ApplicationDescriptor.APPLICATION_LOCATION, ""); // TODO what is this for !! //$NON-NLS-1$
+ props.put(ApplicationDescriptor.APPLICATION_LAUNCHABLE, singletonMgr == null ? Boolean.TRUE : singletonMgr.isLocked() ? Boolean.FALSE : Boolean.TRUE);
+ props.put(ApplicationDescriptor.APPLICATION_LOCKED, locked);
+ props.put(ApplicationDescriptor.APPLICATION_VISIBLE, Boolean.TRUE);
+ return props;
+ }
+
+ /*
+ * Returns the appHandle. If it does not exist then one is created.
+ */
+ private synchronized EclipseAppHandle createAppHandle() {
+ // TODO not sure what instance pid should be used; for now just use the appDesciptor pid because apps are singletons anyway
+ EclipseAppHandle newAppHandle;
+ if (error == null)
+ newAppHandle = new EclipseAppHandle(getApplicationId(), this);
+ else
+ newAppHandle = new EclipseAppHandle(error, containerMgr);
+ ServiceRegistration appHandleReg = (ServiceRegistration) AccessController.doPrivileged(containerMgr.getRegServiceAction(ApplicationHandle.class.getName(), newAppHandle, newAppHandle.getServiceProperties()));
+ newAppHandle.setServiceRegistration(appHandleReg);
+ return newAppHandle;
+ }
+
+ ContainerManager getContainerManager() {
+ return containerMgr;
+ }
+
+ String getType() {
+ return type;
+ }
+
+ public boolean matchDNChain(String pattern) {
+ return BundleSignerCondition.getCondition(AppManager.getBundle(namespace), new ConditionInfo(BundleSignerCondition.class.getName(), new String[] {pattern})).isSatisfied();
+ }
+
+ protected boolean isLaunchableSpecific() {
+ // TODO Auto-generated method stub
+ return true;
+ }
+
+ public void unregister() {
+ if (sr != null)
+ sr.unregister();
+ }
+}
diff --git a/bundles/org.eclipse.equinox.app/src/org/eclipse/equinox/internal/app/EclipseAppHandle.java b/bundles/org.eclipse.equinox.app/src/org/eclipse/equinox/internal/app/EclipseAppHandle.java
new file mode 100755
index 000000000..e8ffe6636
--- /dev/null
+++ b/bundles/org.eclipse.equinox.app/src/org/eclipse/equinox/internal/app/EclipseAppHandle.java
@@ -0,0 +1,132 @@
+/*******************************************************************************
+ * Copyright (c) 2005 IBM Corporation and others.
+ * 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:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.equinox.internal.app;
+
+import java.util.*;
+import org.eclipse.equinox.app.IAppContext;
+import org.eclipse.equinox.app.IApplication;
+import org.eclipse.equinox.registry.IConfigurationElement;
+import org.eclipse.equinox.registry.IExtension;
+import org.eclipse.osgi.util.NLS;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.application.ApplicationHandle;
+
+/*
+ * An ApplicationHandle that represents a single instance of a running eclipse application.
+ */
+public class EclipseAppHandle extends ApplicationHandle implements IAppContext {
+ private ServiceRegistration sr;
+ private String state = ApplicationHandle.RUNNING;
+ private int status = IAppContext.ACTIVE;
+ private Object application;
+ private Exception appNotFound;
+ private Map arguments;
+
+ /*
+ * Used to create a dummy handle to throw an exception when run by the ApplicationLauncher.
+ */
+ EclipseAppHandle(Exception appNotFound, ContainerManager containerMgr) {
+ this("", new EclipseAppDescriptor("", "", null, containerMgr)); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ this.appNotFound = appNotFound;
+ }
+
+ /*
+ * Constructs a handle for a single running instance of a eclipse application.
+ */
+ EclipseAppHandle(String instanceId, EclipseAppDescriptor descriptor) {
+ super(instanceId, descriptor);
+ }
+
+ synchronized public String getState() {
+ return state;
+ }
+
+
+ protected void destroySpecific() throws Exception {
+ // when this method is called we must force the application to exit.
+ // first set the status to stopping
+ setAppStatus(IAppContext.STOPPING);
+ // now force the appliction to stop
+ if (application instanceof IApplication)
+ ((IApplication) application).stop();
+ // make sure the app status is stopped
+ setAppStatus(IAppContext.STOPPED);
+ }
+
+ void setServiceRegistration(ServiceRegistration sr) {
+ this.sr = sr;
+ }
+
+ void setApplication(Object application) {
+ this.application = application;
+ }
+
+ /*
+ * Gets a snapshot of the current service properties.
+ */
+ Dictionary getServiceProperties() {
+ Dictionary props = new Hashtable(6);
+ props.put(ApplicationHandle.APPLICATION_PID, getInstanceId());
+ props.put(ApplicationHandle.APPLICATION_STATE, getState());
+ props.put(ApplicationHandle.APPLICATION_DESCRIPTOR, getApplicationDescriptor().getApplicationId());
+ return props;
+ }
+
+ /*
+ * Changes the state of this handle to STOPPING.
+ * Finally the handle is unregistered if the status is STOPPED
+ */
+ public synchronized void setAppStatus(int status) {
+ if ((status & IAppContext.ACTIVE) != 0)
+ throw new IllegalArgumentException("Cannot set app status to ACTIVE"); //$NON-NLS-1$
+ // if status is stopping and the context is already stopping the return
+ if ((status & IAppContext.STOPPING) != 0)
+ if (ApplicationHandle.STOPPING.equals(state))
+ return;
+ // in both cases if the the context is not stopping then set it and
+ // change the service properties to reflect the state change.
+ if (state != ApplicationHandle.STOPPING) {
+ state = ApplicationHandle.STOPPING;
+ sr.setProperties(getServiceProperties());
+ }
+ // if the status is stopped then unregister the service
+ if ((status & IAppContext.STOPPED) != 0 && (this.status & IAppContext.STOPPED) == 0) {
+ sr.unregister();
+ ((EclipseAppDescriptor)getApplicationDescriptor()).appHandleDestroyed();
+ }
+ this.status = status;
+ }
+
+ public int getAppStatus() {
+ return status;
+ }
+
+ public Map getArguments() {
+ return arguments;
+ }
+
+ void setArguments(Map arguments) {
+ this.arguments = arguments;
+ }
+
+ public IConfigurationElement getConfiguration() {
+ IExtension applicationExtension = ((EclipseAppDescriptor)getApplicationDescriptor()).getContainerManager().getAppExtension(getApplicationDescriptor().getApplicationId());
+ IConfigurationElement[] configs = applicationExtension.getConfigurationElements();
+ if (configs.length == 0)
+ throw new RuntimeException(NLS.bind(Messages.application_invalidExtension, getApplicationDescriptor().getApplicationId()));
+ return configs[0];
+ }
+
+ public Exception getLaunchException() {
+ return appNotFound;
+ }
+}
diff --git a/bundles/org.eclipse.equinox.app/src/org/eclipse/equinox/internal/app/EclipseScheduledApplication.java b/bundles/org.eclipse.equinox.app/src/org/eclipse/equinox/internal/app/EclipseScheduledApplication.java
new file mode 100755
index 000000000..8713694a3
--- /dev/null
+++ b/bundles/org.eclipse.equinox.app/src/org/eclipse/equinox/internal/app/EclipseScheduledApplication.java
@@ -0,0 +1,129 @@
+/*******************************************************************************
+ * Copyright (c) 2005 IBM Corporation and others.
+ * 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:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.equinox.internal.app;
+
+import java.security.Guard;
+import java.security.GuardedObject;
+import java.util.HashMap;
+import java.util.Map;
+import org.osgi.framework.*;
+import org.osgi.service.application.ApplicationDescriptor;
+import org.osgi.service.application.ScheduledApplication;
+import org.osgi.service.event.*;
+import org.osgi.util.tracker.ServiceTracker;
+
+public class EclipseScheduledApplication implements ScheduledApplication, EventHandler {
+ private static final String FILTER_PREFIX = "(&(objectclass=" + ApplicationDescriptor.class.getName() + ")(" + ApplicationDescriptor.APPLICATION_PID + "="; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ private static final String FILTER_POSTFIX = "))"; //$NON-NLS-1$
+ private static final String TRIGGERING_EVENT = "org.osgi.triggeringevent"; //$NON-NLS-1$
+
+ private boolean recurring;
+ private String topic;
+ private String eventFilter;
+ private Map args;
+ private String appPid;
+ private Integer id;
+ private ServiceRegistration sr;
+ private ServiceTracker appTracker;
+ private boolean removed = false;
+
+ EclipseScheduledApplication(BundleContext context, Integer id, String appPid, Map args, String topic, String eventFilter, boolean recurring) throws InvalidSyntaxException {
+ this.id = id;
+ this.appPid = appPid;
+ this.args = args;
+ this.topic = topic == null || topic.trim().equals("") || topic.trim().equals("*") ? null : topic; //$NON-NLS-1$ //$NON-NLS-2$
+ this.eventFilter = eventFilter;
+ this.recurring = recurring;
+ appTracker = new ServiceTracker(context, context.createFilter(FILTER_PREFIX + appPid + FILTER_POSTFIX), null);
+ appTracker.open();
+ }
+
+ Integer getID() {
+ return id;
+ }
+
+ String getAppPid() {
+ return appPid;
+ }
+
+ public String getTopic() {
+ return topic;
+ }
+
+ public String getEventFilter() {
+ return eventFilter;
+ }
+
+ public boolean isRecurring() {
+ return recurring;
+ }
+
+ public synchronized ApplicationDescriptor getApplicationDescriptor() {
+ if (removed)
+ throw new IllegalStateException(Messages.EclipseScheduledApplication_7);
+ return (ApplicationDescriptor) appTracker.getService();
+ }
+
+ public Map getArguments() {
+ return args == null ? null : new HashMap(args);
+ }
+
+ private Map getArguments(Event trigger) {
+ Map result = args == null ? new HashMap() : getArguments();
+ result.put(TRIGGERING_EVENT, new GuardedObject(trigger, new TriggerGuard(trigger.getTopic())));
+ return result;
+ }
+
+ public synchronized void remove() {
+ if (removed)
+ return;
+ removed = true;
+ AppManager.removeScheduledApp(this);
+ if (sr != null)
+ sr.unregister();
+ appTracker.close();
+ }
+
+ public synchronized void handleEvent(Event event) {
+ try {
+ ApplicationDescriptor desc = getApplicationDescriptor();
+ if (desc == null)
+ return;
+ desc.launch(getArguments(event));
+ } catch (Exception e) {
+ // TODO should log this
+ return; // return here to aviod removing non-recurring apps when an erorr occurs
+ }
+ if (!isRecurring())
+ remove();
+ }
+
+ void setServiceRegistration(ServiceRegistration sr) {
+ this.sr = sr;
+ if (removed) // just incase we were removed before the sr was set
+ sr.unregister();
+ }
+
+ public class TriggerGuard implements Guard {
+ String eventTopic;
+ public TriggerGuard(String topic) {
+ this.eventTopic = topic;
+ }
+
+ public void checkGuard(Object object) throws SecurityException {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null)
+ sm.checkPermission(new TopicPermission(eventTopic, TopicPermission.SUBSCRIBE));
+ }
+
+ }
+}
diff --git a/bundles/org.eclipse.equinox.app/src/org/eclipse/equinox/internal/app/MainSingletonApplication.java b/bundles/org.eclipse.equinox.app/src/org/eclipse/equinox/internal/app/MainSingletonApplication.java
new file mode 100755
index 000000000..5eca65bf2
--- /dev/null
+++ b/bundles/org.eclipse.equinox.app/src/org/eclipse/equinox/internal/app/MainSingletonApplication.java
@@ -0,0 +1,70 @@
+/*******************************************************************************
+ * Copyright (c) 2005 IBM Corporation and others.
+ * 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:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.app;
+
+import org.eclipse.equinox.app.IAppContext;
+import org.eclipse.equinox.app.IApplication;
+import org.eclipse.osgi.service.runnable.ApplicationRunnable;
+import org.eclipse.osgi.util.NLS;
+
+public class MainSingletonApplication implements ApplicationRunnable {
+ private static final String PROP_ECLIPSE_EXITCODE = "eclipse.exitcode"; //$NON-NLS-1$
+ private Object application;
+ private EclipseAppHandle appContext;
+ private Exception launchException;
+
+ public MainSingletonApplication(EclipseAppHandle appContext) {
+ this.appContext = appContext;
+ this.launchException = appContext.getLaunchException();
+ if (launchException == null)
+ try {
+ application = appContext.getConfiguration().createExecutableExtension("run"); //$NON-NLS-1$
+ } catch (Exception e) {
+ this.launchException = e;
+ }
+ }
+
+ public Object run(Object context) throws Exception {
+ // if the given arg is null then pass in the left over command line args.
+ if (context == null)
+ context = AppManager.getApplicationArgs();
+ Object result;
+ try {
+ if (launchException != null)
+ // this is a dummy handle used to throw an exception on the main thread.
+ throw launchException;
+ if (application instanceof IApplication) {
+ result = ((IApplication) application).run(context);
+ } else
+ result = ContainerManager.execMethod(application, "run", Object.class, context); //$NON-NLS-1$
+ } finally {
+ application = null;
+ // The application exited itself; notify the app context
+ appContext.setAppStatus(IAppContext.STOPPED);
+ }
+ int exitCode = result instanceof Integer ? ((Integer) result).intValue() : 0;
+ // use the long way to set the property to compile against eeminimum
+ System.getProperties().setProperty(PROP_ECLIPSE_EXITCODE, Integer.toString(exitCode));
+ if (Activator.DEBUG)
+ System.out.println(NLS.bind(Messages.application_returned, (new String[] {appContext.getApplicationDescriptor().getApplicationId(), result == null ? "null" : result.toString()}))); //$NON-NLS-1$
+ return result;
+ }
+
+ public void stop() {
+ // we can only handle forced stops if this application is an IApplication
+ if (application instanceof IApplication)
+ ((IApplication)application).stop();
+ }
+
+ IApplication getApplication() {
+ return (IApplication) ((application instanceof IApplication) ? application : null);
+ }
+}
diff --git a/bundles/org.eclipse.equinox.app/src/org/eclipse/equinox/internal/app/MainSingletonContainer.java b/bundles/org.eclipse.equinox.app/src/org/eclipse/equinox/internal/app/MainSingletonContainer.java
new file mode 100755
index 000000000..5a931100d
--- /dev/null
+++ b/bundles/org.eclipse.equinox.app/src/org/eclipse/equinox/internal/app/MainSingletonContainer.java
@@ -0,0 +1,71 @@
+/*******************************************************************************
+ * Copyright (c) 2005 IBM Corporation and others.
+ * 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:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.app;
+
+import org.eclipse.equinox.app.*;
+import org.eclipse.osgi.service.runnable.ApplicationLauncher;
+import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
+
+public class MainSingletonContainer implements IContainer, ServiceTrackerCustomizer {
+ // tracks the application launcher
+ private ServiceTracker appLauncherTracker;
+ // the application launcher used to launch applications on the main thread.
+ private ApplicationLauncher appLauncher;
+ private ContainerManager containerMgr;
+
+ public MainSingletonContainer(ContainerManager containerMgr) {
+ this.containerMgr = containerMgr;
+ appLauncherTracker = new ServiceTracker(containerMgr.getBundleContext(), ApplicationLauncher.class.getName(), this);
+ appLauncherTracker.open();
+ }
+ public IApplication launch(IAppContext appContext) {
+ // use the ApplicationLauncher provided by the framework
+ // to ensure it is launched on the main thread
+ if (appLauncher == null)
+ throw new IllegalStateException();
+ MainSingletonApplication app = new MainSingletonApplication((EclipseAppHandle) appContext);
+ appLauncher.launch(app, appContext.getArguments() == null ? null : appContext.getArguments().get(ContainerManager.PROP_ECLIPSE_APPLICATION_ARGS));
+ return app.getApplication();
+ }
+ public Object addingService(ServiceReference reference) {
+ if (appLauncher != null)
+ return null;
+ appLauncher = (ApplicationLauncher) containerMgr.getBundleContext().getService(reference);
+ if (!Boolean.getBoolean(ContainerManager.PROP_ECLIPSE_APPLICATION_NODEFAULT)) {
+ // find the default application
+ EclipseAppDescriptor defaultDesc = containerMgr.findDefaultApp();
+ // launch the default application
+ try {
+ defaultDesc.launch(null);
+ } catch (Exception e) {
+ // TODO should log this!!
+ }
+ }
+ return appLauncher;
+ }
+ public void modifiedService(ServiceReference reference, Object service) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public synchronized void removedService(ServiceReference reference, Object service) {
+ if (service == appLauncher) {
+ appLauncher = null;
+ containerMgr.getBundleContext().ungetService(reference);
+ }
+ }
+ public boolean isSingletonContainer() {
+ return true;
+ }
+
+}
diff --git a/bundles/org.eclipse.equinox.app/src/org/eclipse/equinox/internal/app/Messages.java b/bundles/org.eclipse.equinox.app/src/org/eclipse/equinox/internal/app/Messages.java
new file mode 100755
index 000000000..dafdaa875
--- /dev/null
+++ b/bundles/org.eclipse.equinox.app/src/org/eclipse/equinox/internal/app/Messages.java
@@ -0,0 +1,42 @@
+/*******************************************************************************
+ * Copyright (c) 2005 IBM Corporation and others.
+ * 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:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.app;
+
+import org.eclipse.osgi.util.NLS;
+
+public class Messages extends NLS{
+ private static final String BUNDLE_NAME = "org.eclipse.equinox.internal.app.messages"; //$NON-NLS-1$
+
+ // application
+ public static String application_invalidExtension;
+ public static String application_noIdFound;
+ public static String application_notFound;
+ public static String application_returned;
+
+ // product
+ public static String provider_invalid;
+ public static String provider_invalid_general;
+ public static String product_notFound;
+
+ // container
+ public static String container_notFound;
+
+ static {
+ // load message values from bundle file
+ reloadMessages();
+ }
+
+ public static void reloadMessages() {
+ NLS.initializeMessages(BUNDLE_NAME, Messages.class);
+ }
+
+ public static String EclipseScheduledApplication_7;
+}
diff --git a/bundles/org.eclipse.equinox.app/src/org/eclipse/equinox/internal/app/SingletonContainerMgr.java b/bundles/org.eclipse.equinox.app/src/org/eclipse/equinox/internal/app/SingletonContainerMgr.java
new file mode 100755
index 000000000..53ea8d49e
--- /dev/null
+++ b/bundles/org.eclipse.equinox.app/src/org/eclipse/equinox/internal/app/SingletonContainerMgr.java
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * Copyright (c) 2005 IBM Corporation and others.
+ * 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:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.app;
+
+import org.eclipse.equinox.app.*;
+
+public class SingletonContainerMgr implements IContainer {
+ private EclipseAppHandle singletonHandle;
+ private String type;
+ private IContainer singletonContainer;
+ private ContainerManager containerManager;
+
+ public SingletonContainerMgr(IContainer singletonContainer, String type, ContainerManager containerManager) {
+ this.singletonContainer = singletonContainer;
+ this.type = type;
+ this.containerManager = containerManager;
+ }
+ public synchronized IApplication launch(IAppContext context) throws Exception {
+ if (context != singletonHandle)
+ throw new IllegalStateException("Only one application of type \"" + type + "\" is allowed to run at a time");
+ return singletonContainer.launch(context);
+ }
+
+ public boolean isSingletonContainer() {
+ return true;
+ }
+
+ synchronized boolean isLocked() {
+ return singletonHandle != null;
+ }
+
+ synchronized void lock(EclipseAppHandle appHandle) {
+ if (singletonHandle != null)
+ throw new IllegalStateException("Only one application of type \"" + type + "\" is allowed to run at a time");
+ singletonHandle = appHandle;
+ refreshAppDescriptors();
+ }
+ synchronized void unlock() {
+ singletonHandle = null;
+ refreshAppDescriptors();
+ }
+
+ private void refreshAppDescriptors() {
+ EclipseAppDescriptor[] singletonApps = containerManager.getAppDescriptorsByType(type);
+ for (int i = 0; i < singletonApps.length; i++) {
+ singletonApps[i].setSingletonMgr(this);
+ singletonApps[i].refreshProperties();
+ }
+ }
+}
diff --git a/bundles/org.eclipse.equinox.app/src/org/eclipse/equinox/internal/app/messages.properties b/bundles/org.eclipse.equinox.app/src/org/eclipse/equinox/internal/app/messages.properties
new file mode 100755
index 000000000..8bd877f38
--- /dev/null
+++ b/bundles/org.eclipse.equinox.app/src/org/eclipse/equinox/internal/app/messages.properties
@@ -0,0 +1,26 @@
+###############################################################################
+# Copyright (c) 2000, 2005 IBM Corporation and others.
+# 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:
+# IBM Corporation - initial API and implementation
+###############################################################################
+### Equinox AppContainer plugin messages
+
+### application
+application_invalidExtension = Invalid (empty) application extension \"{0}\".
+application_noIdFound = No application id has been found.
+application_notFound=Application \"{0}\" could not be found in the registry. The applications available are: {1}.
+application_returned=The application \"{0}\" returned with code: {1}.
+
+### product
+provider_invalid_general = Errors while processing the product providers.
+provider_invalid = Problem creating the provider registered by {0}.
+product_notFound = Product {0} could not be found.
+
+## container
+container_notFound = No container is available to launch the application \"{0}\" of type \"{1}\".
+EclipseScheduledApplication_7=Application has been unregistered
diff --git a/bundles/org.eclipse.equinox.app/src/org/osgi/service/application/ApplicationAdminPermission.java b/bundles/org.eclipse.equinox.app/src/org/osgi/service/application/ApplicationAdminPermission.java
new file mode 100755
index 000000000..56bd13e45
--- /dev/null
+++ b/bundles/org.eclipse.equinox.app/src/org/osgi/service/application/ApplicationAdminPermission.java
@@ -0,0 +1,389 @@
+/*
+ * $Header: /cvshome/build/org.osgi.service.application/src/org/osgi/service/application/ApplicationAdminPermission.java,v 1.27 2005/11/30 20:17:37 ckarai Exp $
+ *
+ * Copyright (c) OSGi Alliance (2004, 2005). 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.
+ */
+
+package org.osgi.service.application;
+
+import java.security.Permission;
+import java.util.Hashtable;
+import java.util.StringTokenizer;
+import java.util.Vector;
+
+import org.osgi.framework.Filter;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.InvalidSyntaxException;
+
+/**
+ * This class implements permissions for manipulating applications and
+ * their instances.
+ * <P>
+ * ApplicationAdminPermission can be targeted to applications that matches the
+ * specified filter.
+ * <P>
+ * ApplicationAdminPermission may be granted for different actions:
+ * <code>lifecycle</code>, <code>schedule</code> and <code>lock</code>.
+ * The permission <code>schedule</code> implies the permission
+ * <code>lifecycle</code>.
+ */
+public class ApplicationAdminPermission extends Permission {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Allows the lifecycle management of the target applications.
+ */
+ public static final String LIFECYCLE_ACTION = "lifecycle";
+
+ /**
+ * Allows scheduling of the target applications. The permission to
+ * schedule an application implies that the scheduler can also
+ * manage the lifecycle of that application i.e. <code>schedule</code>
+ * implies <code>lifecycle</code>
+ */
+ public static final String SCHEDULE_ACTION = "schedule";
+
+ /**
+ * Allows setting/unsetting the locking state of the target applications.
+ */
+ public static final String LOCK_ACTION = "lock";
+
+ private ApplicationDescriptor applicationDescriptor;
+
+ /**
+ * Constructs an ApplicationAdminPermission. The <code>filter</code>
+ * specifies the target application. The <code>filter</code> is an
+ * LDAP-style filter, the recognized properties are <code>signer</code>
+ * and <code>pid</code>. The pattern specified in the <code>signer</code>
+ * is matched with the Distinguished Name chain used to sign the application.
+ * Wildcards in a DN are not matched according to the filter string rules,
+ * but according to the rules defined for a DN chain. The attribute
+ * <code>pid</code> is matched with the PID of the application according to
+ * the filter string rules.
+ * <p>
+ * If the <code>filter</code> is <code>null</code> then it matches
+ * <code>"*"</code>. If
+ * <code>actions</code> is <code>"*"</code> then it identifies all the
+ * possible actions.
+ *
+ * @param filter
+ * filter to identify application. The value <code>null</code>
+ * is equivalent to <code>"*"</code> and it indicates "all application".
+ * @param actions
+ * comma-separated list of the desired actions granted on the
+ * applications or "*" means all the actions. It must not be
+ * <code>null</code>. The order of the actions in the list is
+ * not significant.
+ * @throws InvalidSyntaxException
+ * is thrown if the specified <code>filter</code> is not syntactically
+ * correct.
+ *
+ * @exception NullPointerException
+ * is thrown if the actions parameter is <code>null</code>
+ *
+ * @see ApplicationDescriptor
+ * @see org.osgi.framework.AdminPermission
+ */
+ public ApplicationAdminPermission(String filter, String actions) throws InvalidSyntaxException {
+ super(filter == null ? "*" : filter);
+
+ if( actions == null )
+ throw new NullPointerException( "Action string cannot be null!" );
+
+ this.applicationDescriptor = null;
+ this.filter = (filter == null ? "*" : filter);
+ this.actions = actions;
+
+ if( !filter.equals( "*" ) )
+ FrameworkUtil.createFilter( this.filter ); // check if the filter is valid
+ init();
+ }
+
+ /**
+ * This contructor should be used when creating <code>ApplicationAdminPermission</code>
+ * instance for <code>checkPermission</code> call.
+ * @param application the tareget of the operation, it must not be <code>null</code>
+ * @param actions the required operation. it must not be <code>null</code>
+ * @throws NullPointerException if any of the arguments is null.
+ */
+ public ApplicationAdminPermission(ApplicationDescriptor application, String actions) {
+ super(application.getApplicationId());
+
+ if( application == null || actions == null )
+ throw new NullPointerException( "ApplicationDescriptor and action string cannot be null!" );
+
+ this.filter = application.getApplicationId();
+ this.applicationDescriptor = application;
+ this.actions = actions;
+
+ init();
+ }
+
+ /**
+ * This method can be used in the {@link java.security.ProtectionDomain}
+ * implementation in the <code>implies</code> method to insert the
+ * application ID of the current application into the permission being
+ * checked. This enables the evaluation of the
+ * <code>&lt;&lt;SELF&gt;&gt;</code> pseudo targets.
+ * @param applicationId the ID of the current application.
+ * @return the permission updated with the ID of the current application
+ */
+ public ApplicationAdminPermission setCurrentApplicationId(String applicationId) {
+ if( this.applicationDescriptor == null )
+ throw new NullPointerException("No application descriptor found!");
+
+ ApplicationAdminPermission newPerm = new ApplicationAdminPermission( this.applicationDescriptor,
+ this.actions );
+
+ newPerm.applicationID = applicationId;
+
+ return newPerm;
+ }
+
+ /**
+ * Checks if the specified <code>permission</code> is implied by this permission.
+ * The method returns true under the following conditions:
+ * <UL>
+ * <LI> This permission was created by specifying a filter (see {@link #ApplicationAdminPermission(String, String)})
+ * <LI> The implied <code>otherPermission</code> was created for a particular {@link ApplicationDescriptor}
+ * (see {@link #ApplicationAdminPermission(ApplicationDescriptor, String)})
+ * <LI> The <code>filter</code> of this permission mathes the <code>ApplicationDescriptor</code> specified
+ * in the <code>otherPermission</code>. If the filter in this permission is the
+ * <code>&lt;&lt;SELF&gt;&gt;</code> pseudo target, then the currentApplicationId set in the
+ * <code>otherPermission</code> is compared to the application Id of the target
+ * <code>ApplicationDescriptor</code>.
+ * <LI> The list of permitted actions in this permission contains all actions required in the
+ * <code>otherPermission</code>
+ * </UL>
+ * Otherwise the method returns false.
+ * @param otherPermission the implied permission
+ * @return true if this permission implies the <code>otherPermission</code>, false otherwise.
+ */
+ public boolean implies(Permission otherPermission) {
+ if( otherPermission == null )
+ return false;
+
+ if(!(otherPermission instanceof ApplicationAdminPermission))
+ return false;
+
+ ApplicationAdminPermission other = (ApplicationAdminPermission) otherPermission;
+
+ if( !filter.equals("*") ) {
+ if( filter.equals( "<<SELF>>") ) {
+ if( other.applicationID == null )
+ return false; /* it cannot be, this might be a bug */
+
+ if( !other.applicationID.equals( other.applicationDescriptor.getApplicationId() ) )
+ return false;
+ }
+ else {
+ Hashtable props = new Hashtable();
+ if( other.applicationDescriptor == null )
+ return false;
+ props.put( "pid", other.applicationDescriptor.getApplicationId() );
+ props.put( "signer", new SignerWrapper( other.applicationDescriptor ) );
+
+ Filter flt = getFilter();
+ if( flt == null )
+ return false;
+
+ if( !flt.match( props ) )
+ return false;
+ }
+ }
+
+ if( !actionsVector.containsAll( other.actionsVector ) )
+ return false;
+
+ return true;
+ }
+
+ public boolean equals(Object with) {
+ if( with == null || !(with instanceof ApplicationAdminPermission) )
+ return false;
+
+ ApplicationAdminPermission other = (ApplicationAdminPermission)with;
+
+ // Compare actions:
+ if( other.actionsVector.size() != actionsVector.size() )
+ return false;
+
+ for( int i=0; i != actionsVector.size(); i++ )
+ if( !other.actionsVector.contains( actionsVector.get( i ) ) )
+ return false;
+
+
+ return equal(this.filter, other.filter ) && equal(this.applicationDescriptor, other.applicationDescriptor)
+ && equal(this.applicationID, other.applicationID);
+ }
+
+ /**
+ * Compares parameters for equality. If both object are null, they are considered
+ * equal.
+ * @param a object to compare
+ * @param b other object to compare
+ * @return true if both objects are equal or both are null
+ */
+ private static boolean equal(Object a, Object b) {
+ // This equation is true if both references are null or both point
+ // to the same object. In both cases they are considered as equal.
+ if( a == b ) {
+ return true;
+ }
+
+ return a.equals(b);
+ }
+
+ public int hashCode() {
+ int hc = 0;
+ for( int i=0; i != actionsVector.size(); i++ )
+ hc ^= ((String)actionsVector.get( i )).hashCode();
+ hc ^= (null == this.filter )? 0 : this.filter.hashCode();
+ hc ^= (null == this.applicationDescriptor) ? 0 : this.applicationDescriptor.hashCode();
+ hc ^= (null == this.applicationID) ? 0 : this.applicationID.hashCode();
+ return hc;
+ }
+
+ /**
+ * Returns the actions of this permission.
+ * @return the actions specified when this permission was created
+ */
+ public String getActions() {
+ return actions;
+ }
+
+ private String applicationID;
+
+ private static final Vector ACTIONS = new Vector();
+ private Vector actionsVector;
+ private final String filter;
+ private final String actions;
+ private Filter appliedFilter = null;
+
+ static {
+ ACTIONS.add(LIFECYCLE_ACTION);
+ ACTIONS.add(SCHEDULE_ACTION);
+ ACTIONS.add(LOCK_ACTION);
+ }
+
+ private static Vector actionsVector(String actions) {
+ Vector v = new Vector();
+ StringTokenizer t = new StringTokenizer(actions.toUpperCase(), ",");
+ while (t.hasMoreTokens()) {
+ String action = t.nextToken().trim();
+ v.add(action.toLowerCase());
+ }
+ return v;
+ }
+
+
+ private static class SignerWrapper extends Object {
+ private String pattern;
+ private ApplicationDescriptor appDesc;
+
+ public SignerWrapper(String pattern) {
+ this.pattern = pattern;
+ }
+
+ SignerWrapper(ApplicationDescriptor appDesc) {
+ this.appDesc = appDesc;
+ }
+
+ public boolean equals(Object o) {
+ if (!(o instanceof SignerWrapper))
+ return false;
+ SignerWrapper other = (SignerWrapper) o;
+ ApplicationDescriptor matchAppDesc = (ApplicationDescriptor) (appDesc != null ? appDesc : other.appDesc);
+ String matchPattern = appDesc != null ? other.pattern : pattern;
+ return matchAppDesc.matchDNChain(matchPattern);
+ }
+ }
+
+ private void init() {
+ actionsVector = actionsVector( actions );
+
+ if ( actions.equals("*") )
+ actionsVector = actionsVector( LIFECYCLE_ACTION + "," + SCHEDULE_ACTION + "," + LOCK_ACTION );
+ else if (!ACTIONS.containsAll(actionsVector))
+ throw new IllegalArgumentException("Illegal action!");
+
+ applicationID = null;
+ }
+
+ private Filter getFilter() {
+ String transformedFilter = filter;
+
+ if (appliedFilter == null) {
+ try {
+ int pos = filter.indexOf("signer"); //$NON-NLS-1$
+ if (pos != -1){
+
+ //there may be a signer attribute
+ StringBuffer filterBuf = new StringBuffer(filter);
+ int numAsteriskFound = 0; //use as offset to replace in buffer
+
+ int walkbackPos; //temp pos
+
+ //find occurences of (signer= and escape out *'s
+ while (pos != -1) {
+
+ //walk back and look for '(' to see if this is an attr
+ walkbackPos = pos-1;
+
+ //consume whitespace
+ while(walkbackPos >= 0 && Character.isWhitespace(filter.charAt(walkbackPos))) {
+ walkbackPos--;
+ }
+ if (walkbackPos <0) {
+ //filter is invalid - FilterImpl will throw error
+ break;
+ }
+
+ //check to see if we have unescaped '('
+ if (filter.charAt(walkbackPos) != '(' || (walkbackPos > 0 && filter.charAt(walkbackPos-1) == '\\')) {
+ //'(' was escaped or not there
+ pos = filter.indexOf("signer",pos+6); //$NON-NLS-1$
+ continue;
+ }
+ pos+=6; //skip over 'signer'
+
+ //found signer - consume whitespace before '='
+ while (Character.isWhitespace(filter.charAt(pos))) {
+ pos++;
+ }
+
+ //look for '='
+ if (filter.charAt(pos) != '=') {
+ //attr was signerx - keep looking
+ pos = filter.indexOf("signer",pos); //$NON-NLS-1$
+ continue;
+ }
+ pos++; //skip over '='
+
+ //found signer value - escape '*'s
+ while (!(filter.charAt(pos) == ')' && filter.charAt(pos-1) != '\\')) {
+ if (filter.charAt(pos) == '*') {
+ filterBuf.insert(pos+numAsteriskFound,'\\');
+ numAsteriskFound++;
+ }
+ pos++;
+ }
+
+ //end of signer value - look for more?
+ pos = filter.indexOf("signer",pos); //$NON-NLS-1$
+ } //end while (pos != -1)
+ transformedFilter = filterBuf.toString();
+ } //end if (pos != -1)
+
+ appliedFilter = FrameworkUtil.createFilter( transformedFilter );
+ } catch (InvalidSyntaxException e) {
+ //we will return null
+ }
+ }
+ return appliedFilter;
+ }
+}
diff --git a/bundles/org.eclipse.equinox.app/src/org/osgi/service/application/ApplicationDescriptor.java b/bundles/org.eclipse.equinox.app/src/org/osgi/service/application/ApplicationDescriptor.java
new file mode 100755
index 000000000..be3e5c360
--- /dev/null
+++ b/bundles/org.eclipse.equinox.app/src/org/osgi/service/application/ApplicationDescriptor.java
@@ -0,0 +1,468 @@
+/*
+ * $Header: /cvsroot/eclipse/equinox-incubator/runtime-split/org.eclipse.equinox.appcontainer/src/org/osgi/service/application/ApplicationDescriptor.java,v 1.3 2005/12/14 18:52:01 twatson Exp $
+ *
+ * Copyright (c) OSGi Alliance (2004, 2005). 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.
+ */
+
+package org.osgi.service.application;
+
+import java.io.IOException;
+import java.util.Map;
+
+import org.eclipse.equinox.internal.app.AppManager;
+import org.osgi.framework.Constants;
+import org.osgi.framework.InvalidSyntaxException;
+
+/**
+ * An OSGi service that represents an installed application and stores
+ * information about it. The application descriptor can be used for instance
+ * creation.
+ */
+
+public abstract class ApplicationDescriptor {
+
+ /**
+ * The property key for the localized name of the application.
+ */
+ public static final String APPLICATION_NAME = "application.name";
+
+ /**
+ * The property key for the localized icon of the application.
+ */
+ public static final String APPLICATION_ICON = "application.icon";
+
+ /**
+ * The property key for the unique identifier (PID) of the application.
+ */
+ public static final String APPLICATION_PID = Constants.SERVICE_PID;
+
+ /**
+ * The property key for the version of the application.
+ */
+ public static final String APPLICATION_VERSION = "application.version";
+
+ /**
+ * The property key for the name of the application vendor.
+ */
+ public static final String APPLICATION_VENDOR = Constants.SERVICE_VENDOR;
+
+
+ /**
+ * The property key for the visibility property of the application.
+ */
+ public static final String APPLICATION_VISIBLE = "application.visible";
+
+ /**
+ * The property key for the launchable property of the application.
+ */
+ public static final String APPLICATION_LAUNCHABLE = "application.launchable";
+
+ /**
+ * The property key for the locked property of the application.
+ */
+ public static final String APPLICATION_LOCKED = "application.locked";
+
+ /**
+ * The property key for the localized description of the application.
+ */
+ public static final String APPLICATION_DESCRIPTION = "application.description";
+
+ /**
+ * The property key for the localized documentation of the application.
+ */
+ public static final String APPLICATION_DOCUMENTATION = "application.documentation";
+
+ /**
+ * The property key for the localized copyright notice of the application.
+ */
+ public static final String APPLICATION_COPYRIGHT = "application.copyright";
+
+ /**
+ * The property key for the localized license of the application.
+ */
+ public static final String APPLICATION_LICENSE = "application.license";
+
+ /**
+ * The property key for the application container of the application.
+ */
+ public static final String APPLICATION_CONTAINER = "application.container";
+
+ /**
+ * The property key for the location of the application.
+ */
+ public static final String APPLICATION_LOCATION = "application.location";
+
+
+ /**
+ * Constructs the <code>ApplicationDescriptor</code>.
+ *
+ * @param applicationId
+ * The identifier of the application. Its value is also available
+ * as the <code>service.pid</code> service property of this
+ * <code>ApplicationDescriptor</code> service. This parameter must not
+ * be <code>null</code>.
+ * @throws NullPointerException if the specified <code>applicationId</code> is null.
+ */
+ protected ApplicationDescriptor(String applicationId) {
+ if(null == applicationId ) {
+ throw new NullPointerException("Application ID must not be null!");
+ }
+
+ this.pid = applicationId;
+ locked[0] = isLocked();
+ }
+
+ /**
+ * Returns the identifier of the represented application.
+ *
+ * @return the identifier of the represented application
+ */
+ public final String getApplicationId() {
+ return pid;
+ }
+
+ /**
+ * This method verifies whether the specified <code>pattern</code>
+ * matches the Distinguished Names of any of the certificate chains
+ * used to authenticate this application.
+ * <P>
+ * The <code>pattern</code> must adhere to the
+ * syntax defined in {@link org.osgi.service.application.ApplicationAdminPermission}
+ * for signer attributes.
+ * <p>
+ * This method is used by {@link ApplicationAdminPermission#implies(java.security.Permission)} method
+ * to match target <code>ApplicationDescriptor</code> and filter.
+ *
+ * @param pattern a pattern for a chain of Distinguished Names. It must not be null.
+ * @return <code>true</code> if the specified pattern matches at least
+ * one of the certificate chains used to authenticate this application
+ * @throws NullPointerException if the specified <code>pattern</code> is null.
+ */
+ public abstract boolean matchDNChain( String pattern );
+
+ /**
+ * Returns the properties of the application descriptor as key-value pairs.
+ * The return value contains the locale aware and unaware properties as
+ * well. The returned <code>Map</code> will include the service
+ * properties of this <code>ApplicationDescriptor</code> as well.
+ * <p>
+ * This method will call the <code>getPropertiesSpecific</code> method
+ * to enable the container implementation to insert application model and/or
+ * container implementation specific properties.
+ * <P>
+ * The returned {@link java.util.Map} will contain the standard OSGi service
+ * properties as well
+ * (e.g. service.id, service.vendor etc.) and specialized application
+ * descriptors may offer further service properties. The returned Map contains
+ * a snapshot of the properties. It will not reflect further changes in the
+ * property values nor will the update of the Map change the corresponding
+ * service property.
+ *
+ * @param locale
+ * the locale string, it may be null, the value null means the
+ * default locale. If the provided locale is the empty String
+ * (<code>""</code>)then raw (non-localized) values are returned.
+ *
+ * @return copy of the service properties of this application descriptor service,
+ * according to the specified locale. If locale is null then the
+ * default locale's properties will be returned. (Since service
+ * properties are always exist it cannot return null.)
+ *
+ * @throws IllegalStateException
+ * if the application descriptor is unregistered
+ */
+ public final Map getProperties(String locale) {
+ Map props = getPropertiesSpecific( locale );
+ Boolean containerLocked = (Boolean) props.remove( APPLICATION_LOCKED );
+ synchronized (locked) {
+ if (containerLocked != null && containerLocked.booleanValue() != locked[0]) {
+ if (locked[0])
+ lockSpecific();
+ else
+ unlockSpecific();
+ }
+ }
+ props.put( APPLICATION_LOCKED, new Boolean( locked[0] ) );
+ return props;
+ }
+
+ /**
+ * Container implementations can provide application model specific
+ * and/or container implementation specific properties via this
+ * method.
+ *
+ * Localizable properties must be returned localized if the provided
+ * <code>locale</code> argument is not the empty String. The value
+ * <code>null</code> indicates to use the default locale, for other
+ * values the specified locale should be used.
+ *
+ * The returned {@link java.util.Map} must contain the standard OSGi service
+ * properties as well
+ * (e.g. service.id, service.vendor etc.) and specialized application
+ * descriptors may offer further service properties.
+ * The returned <code>Map</code>
+ * contains a snapshot of the properties. It will not reflect further changes in the
+ * property values nor will the update of the Map change the corresponding
+ * service property.
+
+ * @param locale the locale to be used for localizing the properties.
+ * If <code>null</code> the default locale should be used. If it is
+ * the empty String (<code>""</code>) then raw (non-localized) values
+ * should be returned.
+ *
+ * @return the application model specific and/or container implementation
+ * specific properties of this application descriptor.
+ *
+ * @throws IllegalStateException
+ * if the application descriptor is unregistered
+ */
+ protected abstract Map getPropertiesSpecific(String locale);
+
+ /**
+ * Launches a new instance of an application. The <code>args</code> parameter specifies
+ * the startup parameters for the instance to be launched, it may be null.
+ * <p>
+ * The following steps are made:
+ * <UL>
+ * <LI>Check for the appropriate permission.
+ * <LI>Check the locking state of the application. If locked then return
+ * null otherwise continue.
+ * <LI>Calls the <code>launchSpecific()</code> method to create and start an application
+ * instance.
+ * <LI>Returns the <code>ApplicationHandle</code> returned by the
+ * launchSpecific()
+ * </UL>
+ * The caller has to have ApplicationAdminPermission(applicationPID,
+ * "launch") in order to be able to perform this operation.
+ * <P>
+ * The <code>Map</code> argument of the launch method contains startup
+ * arguments for the
+ * application. The keys used in the Map can be standard or application
+ * specific. OSGi defines the <code>org.osgi.triggeringevent</code>
+ * key to be used to
+ * pass the triggering event to a scheduled application, however
+ * in the future it is possible that other well-known keys will be defined.
+ * To avoid unwanted clashes of keys, the following rules should be applied:
+ * <ul>
+ * <li>The keys starting with the dash (-) character are application
+ * specific, no well-known meaning should be associated with them.</li>
+ * <li>Well-known keys should follow the reverse domain name based naming.
+ * In particular, the keys standardized in OSGi should start with
+ * <code>org.osgi.</code>.</li>
+ * </ul>
+ * <P>
+ * The method is synchonous, it return only when the application instance was
+ * successfully started or the attempt to start it failed.
+ * <P>
+ * This method never returns <code>null</code>. If launching an application fails,
+ * the appropriate exception is thrown.
+ *
+ * @param arguments
+ * Arguments for the newly launched application, may be null
+ *
+ * @return the registered ApplicationHandle, which represents the newly
+ * launched application instance. Never returns <code>null</code>.
+ *
+ * @throws SecurityException
+ * if the caller doesn't have "lifecycle"
+ * ApplicationAdminPermission for the application.
+ * @throws Exception
+ * if starting the application failed
+ * @throws IllegalStateException
+ * if the application descriptor is unregistered
+ */
+ public final ApplicationHandle launch(Map arguments)
+ throws Exception {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm!= null)
+ sm.checkPermission(new ApplicationAdminPermission(this, ApplicationAdminPermission.LIFECYCLE_ACTION));
+ synchronized (locked) {
+ if (locked[0])
+ throw new Exception("Application is locked, can't launch!");
+ }
+ if( !isLaunchableSpecific() )
+ throw new Exception("Cannot launch the application!");
+ return launchSpecific(arguments);
+ }
+
+ /**
+ * Called by launch() to create and start a new instance in an application
+ * model specific way. It also creates and registeres the application handle
+ * to represent the newly created and started instance and registeres it.
+ * The method is synchonous, it return only when the application instance was
+ * successfully started or the attempt to start it failed.
+ * <P>
+ * This method must not return <code>null</code>. If launching the application
+ * failed, and exception must be thrown.
+ *
+ * @param arguments
+ * the startup parameters of the new application instance, may be
+ * null
+ *
+ * @return the registered application model
+ * specific application handle for the newly created and started
+ * instance.
+ *
+ * @throws IllegalStateException
+ * if the application descriptor is unregistered
+ * @throws Exception
+ * if any problem occures.
+ */
+ protected abstract ApplicationHandle launchSpecific(Map arguments)
+ throws Exception;
+
+ /**
+ * This method is called by launch() to verify that according to the
+ * container, the application is launchable.
+ *
+ * @return true, if the application is launchable according to the
+ * container, false otherwise.
+ *
+ * @throws IllegalStateException
+ * if the application descriptor is unregistered
+ */
+ protected abstract boolean isLaunchableSpecific();
+
+ /**
+ * Schedules the application at a specified event. Schedule information
+ * should not get lost even if the framework or the device restarts so it
+ * should be stored in a persistent storage. The method registers a
+ * {@link ScheduledApplication} service in Service Registry, representing
+ * the created scheduling.
+ *
+ * @param arguments
+ * the startup arguments for the scheduled application, may be
+ * null
+ * @param topic
+ * specifies the topic of the triggering event, it may contain a
+ * trailing asterisk as wildcard, the empty string is treated as
+ * "*", must not be null
+ * @param eventFilter
+ * specifies and LDAP filter to filter on the properties of the
+ * triggering event, may be null
+ * @param recurring
+ * if the recurring parameter is false then the application will
+ * be launched only once, when the event firstly occurs. If the
+ * parameter is true then scheduling will take place for every
+ * event occurrence; i.e. it is a recurring schedule
+ *
+ * @return the registered scheduled application service
+ *
+ * @throws NullPointerException
+ * if the topic is <code>null</code>
+ * @throws IOException
+ * may be thrown if writing the information about the scheduled
+ * application requires operation on the permanent storage and
+ * I/O problem occurred.
+ * @throws InvalidSyntaxException
+ * if the specified <code>eventFilter</code> is not syntactically correct
+ * @throws SecurityException
+ * if the caller doesn't have "schedule"
+ * ApplicationAdminPermission for the application.
+ * @throws IllegalStateException
+ * if the application descriptor is unregistered
+ */
+ public final ScheduledApplication schedule(Map arguments, String topic,
+ String eventFilter, boolean recurring) throws IOException, InvalidSyntaxException {
+ isLaunchableSpecific(); // checks if the ApplicationDescriptor was already unregistered
+ return AppManager.addScheduledApp(this, arguments, topic, eventFilter, recurring);
+ }
+
+ /**
+ * Sets the lock state of the application. If an application is locked then
+ * launching a new instance is not possible. It does not affect the already
+ * launched instances.
+ *
+ * @throws SecurityException
+ * if the caller doesn't have "lock" ApplicationAdminPermission
+ * for the application.
+ * @throws IllegalStateException
+ * if the application descriptor is unregistered
+ */
+ public final void lock() {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null)
+ sm.checkPermission(new ApplicationAdminPermission(this, ApplicationAdminPermission.LOCK_ACTION));
+ synchronized (locked) {
+ if (locked[0])
+ return;
+ locked[0] = true;
+ lockSpecific();
+ saveLock(true);
+ }
+ }
+
+ /**
+ * This method is used to notify the container implementation that the
+ * corresponding application has been locked and it should update the
+ * <code>application.locked</code> service property accordingly.
+ * @throws IllegalStateException
+ * if the application descriptor is unregistered
+ */
+ protected abstract void lockSpecific();
+
+ /**
+ * Unsets the lock state of the application.
+ *
+ * @throws SecurityException
+ * if the caller doesn't have "lock" ApplicationAdminPermission
+ * for the application.
+ * @throws IllegalStateException
+ * if the application descriptor is unregistered
+ */
+ public final void unlock() {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null)
+ sm.checkPermission(new ApplicationAdminPermission(this, ApplicationAdminPermission.LOCK_ACTION));
+ synchronized (locked) {
+ if (!locked[0])
+ return;
+ locked[0] = false;
+ unlockSpecific();
+ saveLock(false);
+ }
+ }
+
+ /**
+ * This method is used to notify the container implementation that the
+ * corresponding application has been unlocked and it should update the
+ * <code>application.locked</code> service property accordingly.
+
+ * @throws IllegalStateException
+ * if the application descriptor is unregistered
+ */
+ protected abstract void unlockSpecific();
+
+ /**
+ * @skip
+ */
+ public interface Delegate {
+ void setApplicationDescriptor(ApplicationDescriptor d, String pid );
+
+ boolean isLocked();
+
+ void lock();
+
+ void unlock();
+
+ ScheduledApplication schedule(Map args, String topic, String filter,
+ boolean recurs) throws InvalidSyntaxException;
+
+ void launch(Map arguments) throws Exception;
+ }
+
+ private void saveLock(boolean locked) {
+ AppManager.saveLock(this, locked);
+ }
+
+ private boolean isLocked() {
+ return AppManager.isLocked(this);
+ }
+
+ String pid;
+ private boolean[] locked = {false};
+}
diff --git a/bundles/org.eclipse.equinox.app/src/org/osgi/service/application/ApplicationHandle.java b/bundles/org.eclipse.equinox.app/src/org/osgi/service/application/ApplicationHandle.java
new file mode 100755
index 000000000..aaee13807
--- /dev/null
+++ b/bundles/org.eclipse.equinox.app/src/org/osgi/service/application/ApplicationHandle.java
@@ -0,0 +1,180 @@
+/*
+ * $Header: /cvsroot/eclipse/equinox-incubator/runtime-split/org.eclipse.equinox.appcontainer/src/org/osgi/service/application/ApplicationHandle.java,v 1.2 2005/11/29 19:48:02 twatson Exp $
+ *
+ * Copyright (c) OSGi Alliance (2004, 2005). 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.
+ */
+
+package org.osgi.service.application;
+
+import org.osgi.framework.Constants;
+
+/**
+ * ApplicationHandle is an OSGi service interface which represents an instance
+ * of an application. It provides the functionality to query and manipulate the
+ * lifecycle state of the represented application instance. It defines constants
+ * for the lifecycle states.
+ */
+public abstract class ApplicationHandle {
+
+ /**
+ * The property key for the unique identifier (PID) of the application
+ * instance.
+ */
+ public static final String APPLICATION_PID = Constants.SERVICE_PID;
+
+ /**
+ * The property key for the pid of the corresponding application descriptor.
+ */
+ public final static String APPLICATION_DESCRIPTOR = "application.descriptor";
+
+ /**
+ * The property key for the state of this appliction instance.
+ */
+ public final static String APPLICATION_STATE = "application.state";
+
+ /**
+ * The application instance is running. This is the initial state of a newly
+ * created application instance.
+ */
+ public final static String RUNNING = "RUNNING";
+
+ /**
+ * The application instance is being stopped. This is the state of the
+ * application instance during the execution of the <code>destroy()</code>
+ * method.
+ */
+ public final static String STOPPING = "STOPPING";
+
+ private final String instanceId;
+
+ /**
+ * Application instance identifier is specified by the container when the
+ * instance is created. The instance identifier must remain static for the
+ * lifetime of the instance, it must remain the same even across framework
+ * restarts for the same application instance. This value must be the same
+ * as the <code>service.pid</code> service property of this application
+ * handle.
+ * <p>
+ * The instance identifier should follow the following scheme:
+ * &lt;<i>application descriptor PID</i>&gt;.&lt;<i>index</i>&gt;
+ * where &lt;<i>application descriptor PID</i>&gt; is the PID of the
+ * corresponding <code>ApplicationDescriptor</code> and &lt;<i>index</i>&gt;
+ * is a unique integer index assigned by the application container.
+ * Even after destroying the application index the same index value should not
+ * be reused in a reasonably long timeframe.
+ *
+ * @param instanceId the instance identifier of the represented application
+ * instance. It must not be null.
+ *
+ * @param descriptor the <code>ApplicationDescriptor</code> of the represented
+ * application instance. It must not be null.
+ *
+ * @throws NullPointerException if any of the arguments is null.
+ */
+ protected ApplicationHandle(String instanceId, ApplicationDescriptor descriptor ) {
+ if( (null == instanceId) || (null == descriptor) ) {
+ throw new NullPointerException("Parameters must not be null!");
+ }
+
+ this.instanceId = instanceId;
+ this.descriptor = descriptor;
+ }
+
+ /**
+ * Retrieves the <code>ApplicationDescriptor</code> to which this
+ * <code>ApplicationHandle</code> belongs.
+ *
+ * @return The corresponding <code>ApplicationDescriptor</code>
+ */
+ public final ApplicationDescriptor getApplicationDescriptor() {
+ return descriptor;
+ }
+
+ /**
+ * Get the state of the application instance.
+ *
+ * @return the state of the application.
+ *
+ * @throws IllegalStateException
+ * if the application handle is unregistered
+ */
+ public abstract String getState();
+
+ /**
+ * Returns the unique identifier of this instance. This value is also
+ * available as a service property of this application handle's service.pid.
+ *
+ * @return the unique identifier of the instance
+ */
+ public final String getInstanceId() {
+ return instanceId;
+ }
+
+ /**
+ * The application instance's lifecycle state can be influenced by this
+ * method. It lets the application instance perform operations to stop
+ * the application safely, e.g. saving its state to a permanent storage.
+ * <p>
+ * The method must check if the lifecycle transition is valid; a STOPPING
+ * application cannot be stopped. If it is invalid then the method must
+ * exit. Otherwise the lifecycle state of the application instance must be
+ * set to STOPPING. Then the destroySpecific() method must be called to
+ * perform any application model specific steps for safe stopping of the
+ * represented application instance.
+ * <p>
+ * At the end the <code>ApplicationHandle</code> must be unregistered.
+ * This method should free all the resources related to this
+ * <code>ApplicationHandle</code>.
+ * <p>
+ * When this method is completed the application instance has already made
+ * its operations for safe stopping, the ApplicationHandle has been
+ * unregistered and its related resources has been freed. Further calls on
+ * this application should not be made because they may have unexpected
+ * results.
+ *
+ * @throws SecurityException
+ * if the caller doesn't have "lifecycle"
+ * <code>ApplicationAdminPermission</code> for the corresponding application.
+ *
+ * @throws Exception
+ * is thrown if an exception or an error occurred during the
+ * method execution.
+ * @throws IllegalStateException
+ * if the application handle is unregistered
+ */
+ public final void destroy() throws Exception {
+ if (STOPPING.equals(getState()))
+ return;
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null)
+ sm.checkPermission(new ApplicationAdminPermission(getApplicationDescriptor(), ApplicationAdminPermission.LIFECYCLE_ACTION));
+ destroySpecific();
+ }
+
+ /**
+ * Called by the destroy() method to perform application model specific
+ * steps to stop and destroy an application instance safely.
+ *
+ * @throws IllegalStateException
+ * if the application handle is unregistered
+ * @throws Exception
+ * is thrown if an exception or an error occurred during the
+ * method execution.
+ */
+ protected abstract void destroySpecific() throws Exception;
+
+ ApplicationDescriptor descriptor;
+
+ /**
+ * @skip
+ *
+ */
+ public interface Delegate {
+ void setApplicationHandle(ApplicationHandle d, ApplicationDescriptor.Delegate descriptor );
+ void destroy() throws Exception;
+ }
+}
diff --git a/bundles/org.eclipse.equinox.app/src/org/osgi/service/application/ScheduledApplication.java b/bundles/org.eclipse.equinox.app/src/org/osgi/service/application/ScheduledApplication.java
new file mode 100755
index 000000000..d08d47a1d
--- /dev/null
+++ b/bundles/org.eclipse.equinox.app/src/org/osgi/service/application/ScheduledApplication.java
@@ -0,0 +1,89 @@
+/*
+ * $Header: /cvshome/build/org.osgi.service.application/src/org/osgi/service/application/ScheduledApplication.java,v 1.13 2005/08/29 09:24:34 ckarai Exp $
+ *
+ * Copyright (c) OSGi Alliance (2004, 2005). 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.
+ */
+
+package org.osgi.service.application;
+
+import java.util.Map;
+
+/**
+ * It is allowed to schedule an application based on a specific event.
+ * ScheduledApplication service keeps the scheduling information. When the
+ * specified event is fired a new instance must be launched. Note that launching
+ * operation may fail because e.g. the application is locked.
+ */
+public interface ScheduledApplication {
+
+ /**
+ * Queries the topic of the triggering event. The topic may contain a
+ * trailing asterisk as wildcard.
+ *
+ * @return the topic of the triggering event
+ *
+ * @throws IllegalStateException
+ * if the scheduled application service is unregistered
+ */
+ public String getTopic();
+
+ /**
+ * Queries the event filter for the triggering event.
+ *
+ * @return the event filter for triggering event
+ *
+ * @throws IllegalStateException
+ * if the scheduled application service is unregistered
+ */
+ public String getEventFilter();
+
+ /**
+ * Queries if the scheduling is recurring.
+ *
+ * @return true if the scheduling is recurring, otherwise returns false
+ *
+ * @throws IllegalStateException
+ * if the scheduled application service is unregistered
+ */
+ public boolean isRecurring();
+
+ /**
+ * Retrieves the ApplicationDescriptor which represents the application and
+ * necessary for launching.
+ *
+ * @return the application descriptor that
+ * represents the scheduled application
+ *
+ * @throws IllegalStateException
+ * if the scheduled application service is unregistered
+ */
+ public ApplicationDescriptor getApplicationDescriptor();
+
+ /**
+ * Queries the startup arguments specified when the application was
+ * scheduled. The method returns a copy of the arguments, it is not possible
+ * to modify the arguments after scheduling.
+ *
+ * @return the startup arguments of the scheduled application. It may be
+ * null if null argument was specified.
+ *
+ * @throws IllegalStateException
+ * if the scheduled application service is unregistered
+ */
+ public Map getArguments();
+
+ /**
+ * Cancels this schedule of the application.
+ *
+ * @throws SecurityException
+ * if the caller doesn't have "schedule"
+ * ApplicationAdminPermission for the scheduled application.
+ * @throws IllegalStateException
+ * if the scheduled application service is unregistered
+ */
+ public void remove();
+} \ No newline at end of file

Back to the top