You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by ri...@apache.org on 2005/08/16 20:34:41 UTC

svn commit: r233031 [20/21] - in /incubator/oscar/trunk: ./ etc/ lib/ src/ src/org/ src/org/apache/ src/org/apache/osgi/ src/org/apache/osgi/bundle/ src/org/apache/osgi/bundle/bundlerepository/ src/org/apache/osgi/bundle/bundlerepository/kxmlsax/ src/o...

Added: incubator/oscar/trunk/src/org/osgi/service/packageadmin/PackageAdmin.java
URL: http://svn.apache.org/viewcvs/incubator/oscar/trunk/src/org/osgi/service/packageadmin/PackageAdmin.java?rev=233031&view=auto
==============================================================================
--- incubator/oscar/trunk/src/org/osgi/service/packageadmin/PackageAdmin.java (added)
+++ incubator/oscar/trunk/src/org/osgi/service/packageadmin/PackageAdmin.java Tue Aug 16 11:33:34 2005
@@ -0,0 +1,289 @@
+/*
+ * $Header: /cvshome/build/org.osgi.service.packageadmin/src/org/osgi/service/packageadmin/PackageAdmin.java,v 1.10 2005/05/13 20:32:34 hargrave Exp $
+ * 
+ * Copyright (c) OSGi Alliance (2001, 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.packageadmin;
+
+import org.osgi.framework.Bundle;
+
+/**
+ * Framework service which allows bundle programmers to inspect the packages
+ * exported in the Framework and eagerly update or uninstall bundles.
+ * 
+ * If present, there will only be a single instance of this service registered
+ * with the Framework.
+ * 
+ * <p>
+ * The term <i>exported package </i> (and the corresponding interface
+ * {@link ExportedPackage})refers to a package that has actually been exported
+ * (as opposed to one that is available for export).
+ * 
+ * <p>
+ * The information about exported packages returned by this service is valid
+ * only until the next time {@link #refreshPackages}is called. If an
+ * <code>ExportedPackage</code> object becomes stale, (that is, the package it
+ * references has been updated or removed as a result of calling
+ * <code>PackageAdmin.refreshPackages()</code>), its <code>getName()</code> and
+ * <code>getSpecificationVersion()</code> continue to return their old values,
+ * <code>isRemovalPending()</code> returns <code>true</code>, and
+ * <code>getExportingBundle()</code> and <code>getImportingBundles()</code> return
+ * <code>null</code>.
+ * 
+ * @version $Revision: 1.10 $
+ */
+public interface PackageAdmin {
+	/**
+	 * Gets the packages exported by the specified bundle.
+	 * 
+	 * @param bundle The bundle whose exported packages are to be returned, or
+	 *        <code>null</code> if all the packages currently exported in the
+	 *        Framework are to be returned. If the specified bundle is the
+	 *        system bundle (that is, the bundle with id zero), this method
+	 *        returns all the packages on the system classpath whose name does
+	 *        not start with "java.". In an environment where the exhaustive
+	 *        list of packages on the system classpath is not known in advance,
+	 *        this method will return all currently known packages on the system
+	 *        classpath, that is, all packages on the system classpath that
+	 *        contains one or more classes that have been loaded.
+	 * 
+	 * @return The array of packages exported by the specified bundle, or
+	 *         <code>null</code> if the specified bundle has not exported any
+	 *         packages.
+	 */
+	public ExportedPackage[] getExportedPackages(Bundle bundle);
+
+	/**
+	 * Gets the <code>ExportedPackage</code> object with the specified package
+	 * name. All exported packages will be checked for the specified name. The
+	 * exported package with the highest version will be returned.
+	 * <p>
+	 * In an environment where the exhaustive list of packages on the system
+	 * classpath is not known in advance, this method attempts to see if the
+	 * named package is on the system classpath. This means that this method may
+	 * discover an <code>ExportedPackage</code> object that was not present in the
+	 * list returned by a prior call to <code>getExportedPackages()</code>.
+	 * 
+	 * @param name The name of the exported package to be returned.
+	 * 
+	 * @return The exported package with the specified name, or <code>null</code>
+	 *         if no exported packages with that name exists.
+	 */
+	public ExportedPackage getExportedPackage(String name);
+
+	/**
+	 * Forces the update (replacement) or removal of packages exported by the
+	 * specified bundles.
+	 * 
+	 * <p>
+	 * If no bundles are specified, this method will update or remove any
+	 * packages exported by any bundles that were previously updated or
+	 * uninstalled since the last call to this method. The technique by which
+	 * this is accomplished may vary among different Framework implementations.
+	 * One permissible implementation is to stop and restart the Framework.
+	 * 
+	 * <p>
+	 * This method returns to the caller immediately and then performs the
+	 * following steps in its own thread:
+	 * 
+	 * <ol>
+	 * <li>Compute a graph of bundles starting with the specified bundles. If
+	 * no bundles are specified, compute a graph of bundles starting with
+	 * previously updated or uninstalled ones. Add to the graph any bundle that
+	 * imports a package that is currently exported by a bundle in the graph.
+	 * The graph is fully constructed when there is no bundle outside the graph
+	 * that imports a package from a bundle in the graph. The graph may contain
+	 * <code>UNINSTALLED</code> bundles that are currently still exporting
+	 * packages.
+	 * 
+	 * <li>Each bundle in the graph that is in the <code>ACTIVE</code> state will
+	 * be stopped as described in the <code>Bundle.stop</code> method.
+	 * 
+	 * <li>Each bundle in the graph that is in the <code>RESOLVED</code> state is
+	 * moved to the <code>INSTALLED</code> state. The effect of this step is that
+	 * bundles in the graph are no longer <code>RESOLVED</code>.
+	 * 
+	 * <li>Each bundle in the graph that is in the <code>UNINSTALLED</code> state
+	 * is removed from the graph and is now completely removed from the
+	 * Framework.
+	 * 
+	 * <li>Each bundle in the graph that was in the <code>ACTIVE</code> state
+	 * prior to Step 2 is started as described in the <code>Bundle.start</code>
+	 * method, causing all bundles required for the restart to be resolved. It
+	 * is possible that, as a result of the previous steps, packages that were
+	 * previously exported no longer are. Therefore, some bundles may be
+	 * unresolvable until another bundle offering a compatible package for
+	 * export has been installed in the Framework.
+	 * <li>A framework event of type <code>FrameworkEvent.PACKAGES_REFRESHED</code>
+	 * is broadcast.
+	 * </ol>
+	 * 
+	 * <p>
+	 * For any exceptions that are thrown during any of these steps, a
+	 * <code>FrameworkEvent</code> of type <code>ERROR</code> is broadcast,
+	 * containing the exception. The source bundle for these events should be
+	 * the specific bundle to which the exception is related. If no specific
+	 * bundle can be associated with the exception then the System Bundle must
+	 * be used as the source bundle for the event.
+	 * 
+	 * @param bundles the bundles whose exported packages are to be updated or
+	 *        removed, or <code>null</code> for all previously updated or
+	 *        uninstalled bundles.
+	 * 
+	 * @exception SecurityException if the caller does not have the
+	 *            <code>AdminPermission</code> and the Java runtime environment
+	 *            supports permissions.
+	 */
+	public void refreshPackages(Bundle[] bundles);
+
+	/**
+	 * Get the <code>ExportedPackage</code> objects with the specified
+	 * package name. All exported packages will be checked for the specified
+	 * name.
+	 * <p>
+	 * In an environment where the exhaustive list of packages on the system
+	 * classpath is not known in advance, this method attempts to see if the
+	 * named package is on the system classpath. This means that this method may
+	 * discover an <code>ExportedPackage</code> object that was not present in the
+	 * list returned by a prior call to <code>getExportedPackages()</code>.
+	 * 
+	 * @param name The name of the exported packages to be returned.
+	 * 
+	 * @return An array of the exported packages with the specified name, or
+	 *         <code>null</code> if no exported packages with that name exists.
+	 * @since 1.2
+	 */
+	public ExportedPackage[] getExportedPackages(String name);
+
+	/**
+	 * Resolve the specified bundles. The Framework must attempt to resolve the
+	 * specified bundles that are unresolved. Additional bundles that are not
+	 * included in the specified bundles may be resolved as a result of calling
+	 * this method. A permissible implementation of this method is to attempt to
+	 * resolve all unresolved bundles installed in the framework.
+	 * 
+	 * <p>
+	 * If <code>null</code> is specified then the Framework will attempt to
+	 * resolve all unresolved bundles. This method must not cause any bundle to
+	 * be refreshed, stopped, or started. This method will not return until the
+	 * operation has completed.
+	 * 
+	 * @param bundles The bundles to resolve or <code>null</code> to resolve all
+	 *        unresolved bundles installed in the Framework.
+	 * @return <code>true</code> if all specified bundles are resolved;
+	 * @since 1.2
+	 */
+	public boolean resolveBundles(Bundle[] bundles);
+
+	/**
+	 * Returns an array of RequiredBundles with the specified symbolic name. If
+	 * the symbolic name argument is <code>null</code> then all RequiredBundles
+	 * are returned.
+	 * 
+	 * @param symbolicName The symbolic name of the RequiredBundle or
+	 *        <code>null</code> for all RequiredBundles in the Framework.
+	 * @return An array of RequiredBundles with the specified symbolic name or
+	 *         <code>null</code> if no RequiredBundles exist with that symbolic
+	 *         name.
+	 * @since 1.2
+	 */
+	public RequiredBundle[] getRequiredBundles(String symbolicName);
+
+	/**
+	 * Returns the bundles with the specified symbolic name within the specified
+	 * version range. If no bundles are installed that have the specified
+	 * symbolic name, then <code>null</code> is returned. If a version range is
+	 * specified, then only the bundles that have the specified symbolic name
+	 * and belong to the specified version range are returned. The returned
+	 * bundles are ordered by version in descending version order so that the
+	 * first element of the array contains the bundle with the highest version.
+	 * 
+	 * @see org.osgi.framework.Constants#BUNDLE_VERSION_ATTRIBUTE
+	 * @param symbolicName The symbolic name of the desired bundles.
+	 * @param versionRange The version range of the desired bundles, or
+	 *        <code>null</code> if all versions are desired.
+	 * @return An array of bundles with the specified name belonging to the
+	 *         specified version range ordered in descending version order, or
+	 *         <code>null</code> if no bundles are found.
+	 * @since 1.2
+	 */
+	public Bundle[] getBundles(String symbolicName, String versionRange);
+
+	/**
+	 * Returns an array of attached fragment bundles for the specified bundle.
+	 * If the specified bundle is a fragment then <code>null</code> is returned.
+	 * If no fragments are attached to the specified bundle then <code>null</code>
+	 * is returned.
+	 * <p>
+	 * This method does not attempt to resolve the specified bundle.  If the 
+	 * specified bundle is not resolved then <code>null</code> is returned. 
+	 * 
+	 * @param bundle The bundle whose attached fragment bundles are to be
+	 *        returned.
+	 * @return An array of fragment bundles or <code>null</code> if the bundle
+	 *         does not have any attached fragment bundles or the bundle is not
+	 *         resolved.
+	 * @since 1.2
+	 */
+	public Bundle[] getFragments(Bundle bundle);
+
+	/**
+	 * Returns an array of host bundles to which the specified fragment bundle
+	 * is attached or <code>null</code> if the specified bundle is not attached to
+	 * a host or is not a fragment bundle.
+	 * 
+	 * @param bundle The bundle whose host bundles are to be returned.
+	 * @return An array of host bundles or <code>null</code> if the bundle does
+	 *         not have any host bundles.
+	 * @since 1.2
+	 */
+	public Bundle[] getHosts(Bundle bundle);
+
+	/**
+	 * Returns the bundle for which the specified class is loaded from. The
+	 * classloader of the bundle returned must have been used to load the
+	 * specified class. If the class was not loaded by a bundle classloader then
+	 * <code>null</code> is returned.
+	 * 
+	 * @param clazz the class object to get a bundle for
+	 * @return the bundle from which the specified class is loaded or
+	 *         <code>null</code> if the class was not loaded by a bundle
+	 *         classloader
+	 * @since 1.2
+	 */
+	public Bundle getBundle(Class clazz);
+
+	/**
+	 * The bundle is a fragment bundle.
+	 * 
+	 * <p>
+	 * The value of <code>BUNDLE_TYPE_FRAGMENT</code> is 0x00000001.
+	 * 
+	 * @since 1.2
+	 */
+	public static final int	BUNDLE_TYPE_FRAGMENT	= 0x00000001;
+
+	/**
+	 * Returns the special type of the specified bundle. The bundle type values
+	 * are:
+	 * <ul>
+	 * <li>{@link #BUNDLE_TYPE_FRAGMENT}
+	 * </ul>
+	 * 
+	 * A bundle may be more than one type at a time. A type code is used to
+	 * identify the bundle type for future extendability.
+	 * 
+	 * <p>
+	 * If a bundle is not one or more of the defined types then 0x00000000 is
+	 * returned.
+	 * 
+	 * @return The special type of the bundle.
+	 * @since 1.2
+	 */
+	public int getBundleType(Bundle bundle);
+}
\ No newline at end of file

Added: incubator/oscar/trunk/src/org/osgi/service/packageadmin/RequiredBundle.java
URL: http://svn.apache.org/viewcvs/incubator/oscar/trunk/src/org/osgi/service/packageadmin/RequiredBundle.java?rev=233031&view=auto
==============================================================================
--- incubator/oscar/trunk/src/org/osgi/service/packageadmin/RequiredBundle.java (added)
+++ incubator/oscar/trunk/src/org/osgi/service/packageadmin/RequiredBundle.java Tue Aug 16 11:33:34 2005
@@ -0,0 +1,77 @@
+/*
+ * $Header: /cvshome/build/org.osgi.service.packageadmin/src/org/osgi/service/packageadmin/RequiredBundle.java,v 1.5 2005/05/13 20:32:34 hargrave 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.packageadmin;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.Version;
+
+/**
+ * A required bundle.
+ * 
+ * Instances implementing this interface are created by the Package Admin
+ * service.
+ * 
+ * <p>
+ * The information about a <code>RequiredBundle</code> provided by this object is
+ * valid only until the next time <code>PackageAdmin.refreshPackages()</code>
+ * called. If a <code>RequiredBundle</code> object becomes stale (that is, the
+ * bundle it references has been updated or removed as a result of calling
+ * <code>PackageAdmin.refreshPackages()</code>), its <code>getSymbolicName()</code>
+ * and <code>getVersion()</code> continue to return their old values,
+ * <code>isRemovalPending()</code> returns true, and <code>getBundle()</code> and
+ * <code>getRequiringBundles()</code> return <code>null</code>.
+ * 
+ * @since 1.2
+ */
+public interface RequiredBundle {
+	/**
+	 * Returns the bundle which defines this RequiredBundle.
+	 * 
+	 * @return The bundle, or <code>null</code> if this <code>RequiredBundle</code>
+	 *         object has become stale.
+	 */
+	public Bundle getBundle();
+
+	/**
+	 * Returns the resolved bundles that currently require this bundle. If this
+	 * <code>RequiredBundle</code> object is required and re-exported by another
+	 * bundle then all the requiring bundles of the re-exporting bundle are
+	 * included in the returned array.
+	 * 
+	 * @return An array of resolved bundles currently requiring this bundle, or
+	 *         <code>null</code> if this <code>RequiredBundle</code> object has
+	 *         become stale.
+	 */
+	public Bundle[] getRequiringBundles();
+
+	/**
+	 * Returns the symbolic name of the bundle.
+	 * 
+	 * @return The symbolic name of the bundle.
+	 */
+	public String getSymbolicName();
+
+	/**
+	 * Returns the version of the bundle.
+	 * 
+	 * @return The version of the bundle.
+	 */
+	public Version getVersion();
+
+	/**
+	 * Returns <code>true</code> if the bundle has been updated or uninstalled.
+	 * 
+	 * @return <code>true</code> if the bundle has been updated or uninstalled, or
+	 *         if the <code>RequiredBundle</code> object has become stale;
+	 *         <code>false</code> otherwise.
+	 */
+	public boolean isRemovalPending();
+}
\ No newline at end of file

Added: incubator/oscar/trunk/src/org/osgi/service/permissionadmin/PermissionAdmin.java
URL: http://svn.apache.org/viewcvs/incubator/oscar/trunk/src/org/osgi/service/permissionadmin/PermissionAdmin.java?rev=233031&view=auto
==============================================================================
--- incubator/oscar/trunk/src/org/osgi/service/permissionadmin/PermissionAdmin.java (added)
+++ incubator/oscar/trunk/src/org/osgi/service/permissionadmin/PermissionAdmin.java Tue Aug 16 11:33:34 2005
@@ -0,0 +1,113 @@
+/*
+ * $Header: /cvshome/build/org.osgi.service.permissionadmin/src/org/osgi/service/permissionadmin/PermissionAdmin.java,v 1.7 2005/05/13 20:33:46 hargrave Exp $
+ * 
+ * Copyright (c) OSGi Alliance (2001, 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.permissionadmin;
+
+/**
+ * The Permission Admin service allows management agents to manage the
+ * permissions of bundles. There is at most one Permission Admin service present
+ * in the OSGi environment.
+ * <p>
+ * Access to the Permission Admin service is protected by corresponding
+ * <code>ServicePermission</code>. In addition <code>AdminPermission</code> is
+ * required to actually set permissions.
+ * 
+ * <p>
+ * Bundle permissions are managed using a permission table. A bundle's location
+ * serves as the key into this permission table. The value of a table entry is
+ * the set of permissions (of type <code>PermissionInfo</code>) granted to the
+ * bundle named by the given location. A bundle may have an entry in the
+ * permission table prior to being installed in the Framework.
+ * 
+ * <p>
+ * The permissions specified in <code>setDefaultPermissions</code> are used as the
+ * default permissions which are granted to all bundles that do not have an
+ * entry in the permission table.
+ * 
+ * <p>
+ * Any changes to a bundle's permissions in the permission table will take
+ * effect no later than when bundle's <code>java.security.ProtectionDomain</code>
+ * is next involved in a permission check, and will be made persistent.
+ * 
+ * <p>
+ * Only permission classes on the system classpath or from an exported package
+ * are considered during a permission check. Additionally, only permission
+ * classes that are subclasses of <code>java.security.Permission</code> and define
+ * a 2-argument constructor that takes a <i>name </i> string and an <i>actions
+ * </i> string can be used.
+ * <p>
+ * Permissions implicitly granted by the Framework (for example, a bundle's
+ * permission to access its persistent storage area) cannot be changed, and are
+ * not reflected in the permissions returned by <code>getPermissions</code> and
+ * <code>getDefaultPermissions</code>.
+ * 
+ * @version $Revision: 1.7 $
+ */
+public interface PermissionAdmin {
+	/**
+	 * Gets the permissions assigned to the bundle with the specified location.
+	 * 
+	 * @param location The location of the bundle whose permissions are to be
+	 *        returned.
+	 * 
+	 * @return The permissions assigned to the bundle with the specified
+	 *         location, or <code>null</code> if that bundle has not been assigned
+	 *         any permissions.
+	 */
+	PermissionInfo[] getPermissions(String location);
+
+	/**
+	 * Assigns the specified permissions to the bundle with the specified
+	 * location.
+	 * 
+	 * @param location The location of the bundle that will be assigned the
+	 *        permissions.
+	 * @param permissions The permissions to be assigned, or <code>null</code> if
+	 *        the specified location is to be removed from the permission table.
+	 * @exception SecurityException if the caller does not have the
+	 *            <code>AdminPermission</code>.
+	 */
+	void setPermissions(String location, PermissionInfo[] permissions);
+
+	/**
+	 * Returns the bundle locations that have permissions assigned to them, that
+	 * is, bundle locations for which an entry exists in the permission table.
+	 * 
+	 * @return The locations of bundles that have been assigned any permissions,
+	 *         or <code>null</code> if the permission table is empty.
+	 */
+	String[] getLocations();
+
+	/**
+	 * Gets the default permissions.
+	 * 
+	 * <p>
+	 * These are the permissions granted to any bundle that does not have
+	 * permissions assigned to its location.
+	 * 
+	 * @return The default permissions, or <code>null</code> if no default
+	 *         permissions are set.
+	 */
+	PermissionInfo[] getDefaultPermissions();
+
+	/**
+	 * Sets the default permissions.
+	 * 
+	 * <p>
+	 * These are the permissions granted to any bundle that does not have
+	 * permissions assigned to its location.
+	 * 
+	 * @param permissions The default permissions, or <code>null</code> if the
+	 *        default permissions are to be removed from the permission table.
+	 * @exception SecurityException if the caller does not have the
+	 *            <code>AdminPermission</code>.
+	 */
+	void setDefaultPermissions(PermissionInfo[] permissions);
+}
\ No newline at end of file

Added: incubator/oscar/trunk/src/org/osgi/service/permissionadmin/PermissionInfo.java
URL: http://svn.apache.org/viewcvs/incubator/oscar/trunk/src/org/osgi/service/permissionadmin/PermissionInfo.java?rev=233031&view=auto
==============================================================================
--- incubator/oscar/trunk/src/org/osgi/service/permissionadmin/PermissionInfo.java (added)
+++ incubator/oscar/trunk/src/org/osgi/service/permissionadmin/PermissionInfo.java Tue Aug 16 11:33:34 2005
@@ -0,0 +1,360 @@
+/*
+ * $Header: /cvshome/build/org.osgi.service.permissionadmin/src/org/osgi/service/permissionadmin/PermissionInfo.java,v 1.8 2005/06/21 15:41:57 hargrave Exp $
+ * 
+ * Copyright (c) OSGi Alliance (2001, 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.permissionadmin;
+
+/**
+ * Permission representation used by the Permission Admin service.
+ * 
+ * <p>
+ * This class encapsulates three pieces of information: a Permission <i>type
+ * </i> (class name), which must be a subclass of
+ * <code>java.security.Permission</code>, and the <i>name </i> and <i>actions
+ * </i> arguments passed to its constructor.
+ * 
+ * <p>
+ * In order for a permission represented by a <code>PermissionInfo</code> to be
+ * instantiated and considered during a permission check, its Permission class
+ * must be available from the system classpath or an exported package. This
+ * means that the instantiation of a permission represented by a
+ * <code>PermissionInfo</code> may be delayed until the package containing its
+ * Permission class has been exported by a bundle.
+ * 
+ * @version $Revision: 1.8 $
+ */
+public class PermissionInfo {
+	private String	type;
+	private String	name;
+	private String	actions;
+
+	/**
+	 * Constructs a <code>PermissionInfo</code> from the given type, name, and
+	 * actions.
+	 * 
+	 * @param type The fully qualified class name of the permission represented
+	 *        by this <code>PermissionInfo</code>. The class must be a subclass
+	 *        of <code>java.security.Permission</code> and must define a
+	 *        2-argument constructor that takes a <i>name </i> string and an
+	 *        <i>actions </i> string.
+	 * 
+	 * @param name The permission name that will be passed as the first argument
+	 *        to the constructor of the <code>Permission</code> class identified
+	 *        by <code>type</code>.
+	 * 
+	 * @param actions The permission actions that will be passed as the second
+	 *        argument to the constructor of the <code>Permission</code> class
+	 *        identified by <code>type</code>.
+	 * 
+	 * @exception java.lang.NullPointerException if <code>type</code> is
+	 *            <code>null</code>.
+	 * @exception java.lang.IllegalArgumentException if <code>action</code> is not
+	 *            <code>null</code> and <code>name</code> is <code>null</code>.
+	 */
+	public PermissionInfo(String type, String name, String actions) {
+		this.type = type;
+		this.name = name;
+		this.actions = actions;
+		if (type == null) {
+			throw new NullPointerException("type is null");
+		}
+		if ((name == null) && (actions != null)) {
+			throw new IllegalArgumentException("name missing");
+		}
+	}
+
+	/**
+	 * Constructs a <code>PermissionInfo</code> object from the given encoded
+	 * <code>PermissionInfo</code> string.
+	 * 
+	 * @param encodedPermission The encoded <code>PermissionInfo</code>.
+	 * @see #getEncoded
+	 * @exception java.lang.IllegalArgumentException if
+	 *            <code>encodedPermission</code> is not properly formatted.
+	 */
+	public PermissionInfo(String encodedPermission) {
+		if (encodedPermission == null) {
+			throw new NullPointerException("missing encoded permission");
+		}
+		if (encodedPermission.length() == 0) {
+			throw new IllegalArgumentException("empty encoded permission");
+		}
+		try {
+			char[] encoded = encodedPermission.toCharArray();
+			/* the first character must be '(' */
+			if (encoded[0] != '(') {
+				throw new IllegalArgumentException(
+						"first character not open parenthesis");
+			}
+			/* type is not quoted or encoded */
+			int end = 1;
+			int begin = end;
+			while ((encoded[end] != ' ') && (encoded[end] != ')')) {
+				end++;
+			}
+			if (end == begin) {
+				throw new IllegalArgumentException("expecting type");
+			}
+			this.type = new String(encoded, begin, end - begin);
+			/* type may be followed by name which is quoted and encoded */
+			// TODO Need to support multiple spaces
+			if (encoded[end] == ' ') {
+				end++;
+				if (encoded[end] != '"') {
+					throw new IllegalArgumentException("expecting quoted name");
+				}
+				end++;
+				begin = end;
+				while (encoded[end] != '"') {
+					if (encoded[end] == '\\') {
+						end++;
+					}
+					end++;
+				}
+				this.name = decodeString(encoded, begin, end);
+				end++;
+				/* name may be followed by actions which is quoted and encoded */
+				// TODO Need to support multiple spaces
+				if (encoded[end] == ' ') {
+					end++;
+					if (encoded[end] != '"') {
+						throw new IllegalArgumentException(
+								"expecting quoted actions");
+					}
+					end++;
+					begin = end;
+					while (encoded[end] != '"') {
+						if (encoded[end] == '\\') {
+							end++;
+						}
+						end++;
+					}
+					this.actions = decodeString(encoded, begin, end);
+					end++;
+				}
+			}
+			/* the final character must be ')' */
+			if ((encoded[end] != ')') || (end + 1 != encoded.length)) {
+				throw new IllegalArgumentException("last character not "
+						+ "close parenthesis");
+			}
+		}
+		catch (ArrayIndexOutOfBoundsException e) {
+			throw new IllegalArgumentException("parsing terminated abruptly");
+		}
+	}
+
+	/**
+	 * Returns the string encoding of this <code>PermissionInfo</code> in a form
+	 * suitable for restoring this <code>PermissionInfo</code>.
+	 * 
+	 * <p>
+	 * The encoded format is:
+	 * 
+	 * <pre>
+	 * (type)
+	 * </pre>
+	 * 
+	 * or
+	 * 
+	 * <pre>
+	 * (type &quot;name&quot;)
+	 * </pre>
+	 * 
+	 * or
+	 * 
+	 * <pre>
+	 * (type &quot;name&quot; &quot;actions&quot;)
+	 * </pre>
+	 * 
+	 * where <i>name</i> and <i>actions</i> are strings that are encoded for
+	 * proper parsing. Specifically, the <code>"</code>,<code>\</code>, carriage
+	 * return, and linefeed characters are escaped using <code>\"</code>,
+	 * <code>\\</code>,<code>\r</code>, and <code>\n</code>, respectively.
+	 * 
+	 * <p>
+	 * The encoded string must contain no leading or trailing whitespace
+	 * characters. A single space character must be used between <i>type</i> and 
+	 * &quot;<i>name</i>&quot; and between &quot;<i>name</i>&quot; and &quot;<i>actions</i>&quot;.
+	 * 
+	 * @return The string encoding of this <code>PermissionInfo</code>.
+	 */
+	public final String getEncoded() {
+		StringBuffer output = new StringBuffer(
+				8
+						+ type.length()
+						+ ((((name == null) ? 0 : name.length()) + ((actions == null) ? 0
+								: actions.length())) << 1));
+		output.append('(');
+		output.append(type);
+		if (name != null) {
+			output.append(" \"");
+			encodeString(name, output);
+			if (actions != null) {
+				output.append("\" \"");
+				encodeString(actions, output);
+			}
+			output.append('\"');
+		}
+		output.append(')');
+		return (output.toString());
+	}
+
+	/**
+	 * Returns the string representation of this <code>PermissionInfo</code>. The
+	 * string is created by calling the <code>getEncoded</code> method on this
+	 * <code>PermissionInfo</code>.
+	 * 
+	 * @return The string representation of this <code>PermissionInfo</code>.
+	 */
+	public String toString() {
+		return (getEncoded());
+	}
+
+	/**
+	 * Returns the fully qualified class name of the permission represented by
+	 * this <code>PermissionInfo</code>.
+	 * 
+	 * @return The fully qualified class name of the permission represented by
+	 *         this <code>PermissionInfo</code>.
+	 */
+	public final String getType() {
+		return (type);
+	}
+
+	/**
+	 * Returns the name of the permission represented by this
+	 * <code>PermissionInfo</code>.
+	 * 
+	 * @return The name of the permission represented by this
+	 *         <code>PermissionInfo</code>, or <code>null</code> if the permission
+	 *         does not have a name.
+	 */
+	public final String getName() {
+		return (name);
+	}
+
+	/**
+	 * Returns the actions of the permission represented by this
+	 * <code>PermissionInfo</code>.
+	 * 
+	 * @return The actions of the permission represented by this
+	 *         <code>PermissionInfo</code>, or <code>null</code> if the permission
+	 *         does not have any actions associated with it.
+	 */
+	public final String getActions() {
+		return (actions);
+	}
+
+	/**
+	 * Determines the equality of two <code>PermissionInfo</code> objects.
+	 * 
+	 * This method checks that specified object has the same type, name and
+	 * actions as this <code>PermissionInfo</code> object.
+	 * 
+	 * @param obj The object to test for equality with this
+	 *        <code>PermissionInfo</code> object.
+	 * @return <code>true</code> if <code>obj</code> is a <code>PermissionInfo</code>,
+	 *         and has the same type, name and actions as this
+	 *         <code>PermissionInfo</code> object; <code>false</code> otherwise.
+	 */
+	public boolean equals(Object obj) {
+		if (obj == this) {
+			return (true);
+		}
+		if (!(obj instanceof PermissionInfo)) {
+			return (false);
+		}
+		PermissionInfo other = (PermissionInfo) obj;
+		if (!type.equals(other.type) || ((name == null) ^ (other.name == null))
+				|| ((actions == null) ^ (other.actions == null))) {
+			return (false);
+		}
+		if (name != null) {
+			if (actions != null) {
+				return (name.equals(other.name) && actions
+						.equals(other.actions));
+			}
+			else {
+				return (name.equals(other.name));
+			}
+		}
+		else {
+			return (true);
+		}
+	}
+
+	/**
+	 * Returns the hash code value for this object.
+	 * 
+	 * @return A hash code value for this object.
+	 */
+	public int hashCode() {
+		int hash = type.hashCode();
+		if (name != null) {
+			hash ^= name.hashCode();
+			if (actions != null) {
+				hash ^= actions.hashCode();
+			}
+		}
+		return (hash);
+	}
+
+	/**
+	 * This escapes the quotes, backslashes, \n, and \r in the string using a
+	 * backslash and appends the newly escaped string to a StringBuffer.
+	 */
+	private static void encodeString(String str, StringBuffer output) {
+		int len = str.length();
+		for (int i = 0; i < len; i++) {
+			char c = str.charAt(i);
+			switch (c) {
+				case '"' :
+				case '\\' :
+					output.append('\\');
+					output.append(c);
+					break;
+				case '\r' :
+					output.append("\\r");
+					break;
+				case '\n' :
+					output.append("\\n");
+					break;
+				default :
+					output.append(c);
+					break;
+			}
+		}
+	}
+
+	/**
+	 * Takes an encoded character array and decodes it into a new String.
+	 */
+	private static String decodeString(char[] str, int begin, int end) {
+		StringBuffer output = new StringBuffer(end - begin);
+		for (int i = begin; i < end; i++) {
+			char c = str[i];
+			if (c == '\\') {
+				i++;
+				if (i < end) {
+					c = str[i];
+					if (c == 'n') {
+						c = '\n';
+					}
+					else
+						if (c == 'r') {
+							c = '\r';
+						}
+				}
+			}
+			output.append(c);
+		}
+		return (output.toString());
+	}
+}
\ No newline at end of file

Added: incubator/oscar/trunk/src/org/osgi/service/startlevel/StartLevel.java
URL: http://svn.apache.org/viewcvs/incubator/oscar/trunk/src/org/osgi/service/startlevel/StartLevel.java?rev=233031&view=auto
==============================================================================
--- incubator/oscar/trunk/src/org/osgi/service/startlevel/StartLevel.java (added)
+++ incubator/oscar/trunk/src/org/osgi/service/startlevel/StartLevel.java Tue Aug 16 11:33:34 2005
@@ -0,0 +1,228 @@
+/*
+ * $Header: /cvshome/build/org.osgi.service.startlevel/src/org/osgi/service/startlevel/StartLevel.java,v 1.6 2005/05/13 20:34:03 hargrave Exp $
+ * 
+ * Copyright (c) OSGi Alliance (2002, 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.startlevel;
+
+import org.osgi.framework.Bundle;
+
+/**
+ * The StartLevel service allows management agents to manage a start level
+ * assigned to each bundle and the active start level of the Framework. There is
+ * at most one StartLevel service present in the OSGi environment.
+ * 
+ * <p>
+ * A start level is defined to be a state of execution in which the Framework
+ * exists. StartLevel values are defined as unsigned integers with 0 (zero)
+ * being the state where the Framework is not launched. Progressively higher
+ * integral values represent progressively higher start levels. e.g. 2 is a
+ * higher start level than 1.
+ * <p>
+ * Access to the StartLevel service is protected by corresponding
+ * <code>ServicePermission</code>. In addition the <code>AdminPermission</code>
+ * that is required to actually modify start level information.
+ * <p>
+ * Start Level support in the Framework includes the ability to control the
+ * beginning start level of the Framework, to modify the active start level of
+ * the Framework and to assign a specific start level to a bundle. How the
+ * beginning start level of a Framework is specified is implementation
+ * dependent. It may be a command line argument when invoking the Framework
+ * implementation.
+ * <p>
+ * When the Framework is first started it must be at start level zero. In this
+ * state, no bundles are running. This is the initial state of the Framework
+ * before it is launched.
+ * 
+ * When the Framework is launched, the Framework will enter start level one and
+ * all bundles which are assigned to start level one and are persistently marked
+ * to be started are started as described in the <code>Bundle.start</code> method.
+ * Within a start level, bundles are started in ascending order by
+ * <code>Bundle.getBundleId</code>. The Framework will continue to increase the
+ * start level, starting bundles at each start level, until the Framework has
+ * reached a beginning start level. At this point the Framework has completed
+ * starting bundles and will then broadcast a Framework event of type
+ * <code>FrameworkEvent.STARTED</code> to announce it has completed its launch.
+ * 
+ * <p>
+ * The StartLevel service can be used by management bundles to alter the active
+ * start level of the framework.
+ * 
+ * @version $Revision: 1.6 $
+ */
+public interface StartLevel {
+	/**
+	 * Return the active start level value of the Framework.
+	 * 
+	 * If the Framework is in the process of changing the start level this
+	 * method must return the active start level if this differs from the
+	 * requested start level.
+	 * 
+	 * @return The active start level value of the Framework.
+	 */
+	public abstract int getStartLevel();
+
+	/**
+	 * Modify the active start level of the Framework.
+	 * 
+	 * <p>
+	 * The Framework will move to the requested start level. This method will
+	 * return immediately to the caller and the start level change will occur
+	 * asynchronously on another thread.
+	 * 
+	 * <p>
+	 * If the specified start level is higher than the active start level, the
+	 * Framework will continue to increase the start level until the Framework
+	 * has reached the specified start level, starting bundles at each start
+	 * level which are persistently marked to be started as described in the
+	 * <code>Bundle.start</code> method.
+	 * 
+	 * At each intermediate start level value on the way to and including the
+	 * target start level, the framework must:
+	 * <ol>
+	 * <li>Change the active start level to the intermediate start level value.
+	 * <li>Start bundles at the intermediate start level in ascending order by
+	 * <code>Bundle.getBundleId</code>.
+	 * </ol>
+	 * When this process completes after the specified start level is reached,
+	 * the Framework will broadcast a Framework event of type
+	 * <code>FrameworkEvent.STARTLEVEL_CHANGED</code> to announce it has moved to
+	 * the specified start level.
+	 * 
+	 * <p>
+	 * If the specified start level is lower than the active start level, the
+	 * Framework will continue to decrease the start level until the Framework
+	 * has reached the specified start level stopping bundles at each start
+	 * level as described in the <code>Bundle.stop</code> method except that their
+	 * persistently recorded state indicates that they must be restarted in the
+	 * future.
+	 * 
+	 * At each intermediate start level value on the way to and including the
+	 * specified start level, the framework must:
+	 * <ol>
+	 * <li>Stop bundles at the intermediate start level in descending order by
+	 * <code>Bundle.getBundleId</code>.
+	 * <li>Change the active start level to the intermediate start level value.
+	 * </ol>
+	 * When this process completes after the specified start level is reached,
+	 * the Framework will broadcast a Framework event of type
+	 * <code>FrameworkEvent.STARTLEVEL_CHANGED</code> to announce it has moved to
+	 * the specified start level.
+	 * 
+	 * <p>
+	 * If the specified start level is equal to the active start level, then no
+	 * bundles are started or stopped, however, the Framework must broadcast a
+	 * Framework event of type <code>FrameworkEvent.STARTLEVEL_CHANGED</code> to
+	 * announce it has finished moving to the specified start level. This event
+	 * may arrive before the this method return.
+	 * 
+	 * @param startlevel The requested start level for the Framework.
+	 * @throws IllegalArgumentException If the specified start level is less
+	 *         than or equal to zero.
+	 * @throws SecurityException If the caller does not have the
+	 *         <code>AdminPermission</code> and the Java runtime environment
+	 *         supports permissions.
+	 */
+	public abstract void setStartLevel(int startlevel);
+
+	/**
+	 * Return the assigned start level value for the specified Bundle.
+	 * 
+	 * @param bundle The target bundle.
+	 * @return The start level value of the specified Bundle.
+	 * @exception java.lang.IllegalArgumentException If the specified bundle has
+	 *            been uninstalled.
+	 */
+	public abstract int getBundleStartLevel(Bundle bundle);
+
+	/**
+	 * Assign a start level value to the specified Bundle.
+	 * 
+	 * <p>
+	 * The specified bundle will be assigned the specified start level. The
+	 * start level value assigned to the bundle will be persistently recorded by
+	 * the Framework.
+	 * 
+	 * If the new start level for the bundle is lower than or equal to the
+	 * active start level of the Framework, the Framework will start the
+	 * specified bundle as described in the <code>Bundle.start</code> method if
+	 * the bundle is persistently marked to be started. The actual starting of
+	 * this bundle must occur asynchronously.
+	 * 
+	 * If the new start level for the bundle is higher than the active start
+	 * level of the Framework, the Framework will stop the specified bundle as
+	 * described in the <code>Bundle.stop</code> method except that the
+	 * persistently recorded state for the bundle indicates that the bundle must
+	 * be restarted in the future. The actual stopping of this bundle must occur
+	 * asynchronously.
+	 * 
+	 * @param bundle The target bundle.
+	 * @param startlevel The new start level for the specified Bundle.
+	 * @throws IllegalArgumentException If the specified bundle has been
+	 *         uninstalled or if the specified start level is less than or equal
+	 *         to zero, or the specified bundle is the system bundle.
+	 * @throws SecurityException if the caller does not have the
+	 *         <code>AdminPermission</code> and the Java runtime environment
+	 *         supports permissions.
+	 */
+	public abstract void setBundleStartLevel(Bundle bundle, int startlevel);
+
+	/**
+	 * Return the initial start level value that is assigned to a Bundle when it
+	 * is first installed.
+	 * 
+	 * @return The initial start level value for Bundles.
+	 * @see #setInitialBundleStartLevel
+	 */
+	public abstract int getInitialBundleStartLevel();
+
+	/**
+	 * Set the initial start level value that is assigned to a Bundle when it is
+	 * first installed.
+	 * 
+	 * <p>
+	 * The initial bundle start level will be set to the specified start level.
+	 * The initial bundle start level value will be persistently recorded by the
+	 * Framework.
+	 * 
+	 * <p>
+	 * When a Bundle is installed via <code>BundleContext.installBundle</code>,
+	 * it is assigned the initial bundle start level value.
+	 * 
+	 * <p>
+	 * The default initial bundle start level value is 1 unless this method has
+	 * been called to assign a different initial bundle start level value.
+	 * 
+	 * <p>
+	 * Thie method does not change the start level values of installed bundles.
+	 * 
+	 * @param startlevel The initial start level for newly installed bundles.
+	 * @throws IllegalArgumentException If the specified start level is less
+	 *         than or equal to zero.
+	 * @throws SecurityException if the caller does not have the
+	 *         <code>AdminPermission</code> and the Java runtime environment
+	 *         supports permissions.
+	 */
+	public abstract void setInitialBundleStartLevel(int startlevel);
+
+	/**
+	 * Return the persistent state of the specified bundle.
+	 * 
+	 * <p>
+	 * This method returns the persistent state of a bundle. The persistent
+	 * state of a bundle indicates whether a bundle is persistently marked to be
+	 * started when it's start level is reached.
+	 * 
+	 * @return <code>true</code> if the bundle is persistently marked to be
+	 *         started, <code>false</code> if the bundle is not persistently
+	 *         marked to be started.
+	 * @exception java.lang.IllegalArgumentException If the specified bundle has
+	 *            been uninstalled.
+	 */
+	public abstract boolean isBundlePersistentlyStarted(Bundle bundle);
+}
\ No newline at end of file

Added: incubator/oscar/trunk/src/org/osgi/service/url/AbstractURLStreamHandlerService.java
URL: http://svn.apache.org/viewcvs/incubator/oscar/trunk/src/org/osgi/service/url/AbstractURLStreamHandlerService.java?rev=233031&view=auto
==============================================================================
--- incubator/oscar/trunk/src/org/osgi/service/url/AbstractURLStreamHandlerService.java (added)
+++ incubator/oscar/trunk/src/org/osgi/service/url/AbstractURLStreamHandlerService.java Tue Aug 16 11:33:34 2005
@@ -0,0 +1,142 @@
+/*
+ * $Header: /cvshome/build/org.osgi.service.url/src/org/osgi/service/url/AbstractURLStreamHandlerService.java,v 1.6 2005/05/13 20:32:35 hargrave Exp $
+ * 
+ * Copyright (c) OSGi Alliance (2002, 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.url;
+
+import java.net.*;
+
+/**
+ * Abstract implementation of the <code>URLStreamHandlerService</code> interface.
+ * All the methods simply invoke the corresponding methods on
+ * <code>java.net.URLStreamHandler</code> except for <code>parseURL</code> and
+ * <code>setURL</code>, which use the <code>URLStreamHandlerSetter</code>
+ * parameter. Subclasses of this abstract class should not need to override the
+ * <code>setURL</code> and <code>parseURL(URLStreamHandlerSetter,...)</code>
+ * methods.
+ * 
+ * @version $Revision: 1.6 $
+ */
+public abstract class AbstractURLStreamHandlerService extends URLStreamHandler
+		implements URLStreamHandlerService {
+	/**
+	 * @see "java.net.URLStreamHandler.openConnection"
+	 */
+	public abstract URLConnection openConnection(URL u)
+			throws java.io.IOException;
+
+	/**
+	 * The <code>URLStreamHandlerSetter</code> object passed to the parseURL
+	 * method.
+	 */
+	protected URLStreamHandlerSetter	realHandler;
+
+	/**
+	 * Parse a URL using the <code>URLStreamHandlerSetter</code> object. This
+	 * method sets the <code>realHandler</code> field with the specified
+	 * <code>URLStreamHandlerSetter</code> object and then calls
+	 * <code>parseURL(URL,String,int,int)</code>.
+	 * 
+	 * @param realHandler The object on which the <code>setURL</code> method must
+	 *        be invoked for the specified URL.
+	 * @see "java.net.URLStreamHandler.parseURL"
+	 */
+	public void parseURL(URLStreamHandlerSetter realHandler, URL u,
+			String spec, int start, int limit) {
+		this.realHandler = realHandler;
+		parseURL(u, spec, start, limit);
+	}
+
+	/**
+	 * This method calls <code>super.toExternalForm</code>.
+	 * 
+	 * @see "java.net.URLStreamHandler.toExternalForm"
+	 */
+	public String toExternalForm(URL u) {
+		return super.toExternalForm(u);
+	}
+
+	/**
+	 * This method calls <code>super.equals(URL,URL)</code>.
+	 * 
+	 * @see "java.net.URLStreamHandler.equals(URL,URL)"
+	 */
+	public boolean equals(URL u1, URL u2) {
+		return super.equals(u1, u2);
+	}
+
+	/**
+	 * This method calls <code>super.getDefaultPort</code>.
+	 * 
+	 * @see "java.net.URLStreamHandler.getDefaultPort"
+	 */
+	public int getDefaultPort() {
+		return super.getDefaultPort();
+	}
+
+	/**
+	 * This method calls <code>super.getHostAddress</code>.
+	 * 
+	 * @see "java.net.URLStreamHandler.getHostAddress"
+	 */
+	public InetAddress getHostAddress(URL u) {
+		return super.getHostAddress(u);
+	}
+
+	/**
+	 * This method calls <code>super.hashCode(URL)</code>.
+	 * 
+	 * @see "java.net.URLStreamHandler.hashCode(URL)"
+	 */
+	public int hashCode(URL u) {
+		return super.hashCode(u);
+	}
+
+	/**
+	 * This method calls <code>super.hostsEqual</code>.
+	 * 
+	 * @see "java.net.URLStreamHandler.hostsEqual"
+	 */
+	public boolean hostsEqual(URL u1, URL u2) {
+		return super.hostsEqual(u1, u2);
+	}
+
+	/**
+	 * This method calls <code>super.sameFile</code>.
+	 * 
+	 * @see "java.net.URLStreamHandler.sameFile"
+	 */
+	public boolean sameFile(URL u1, URL u2) {
+		return super.sameFile(u1, u2);
+	}
+
+	/**
+	 * This method calls
+	 * <code>realHandler.setURL(URL,String,String,int,String,String)</code>.
+	 * 
+	 * @see "java.net.URLStreamHandler.setURL(URL,String,String,int,String,String)"
+	 * @deprecated This method is only for compatibility with handlers written
+	 *             for JDK 1.1.
+	 */
+	protected void setURL(URL u, String proto, String host, int port,
+			String file, String ref) {
+		realHandler.setURL(u, proto, host, port, file, ref);
+	}
+
+	/**
+	 * This method calls
+	 * <code>realHandler.setURL(URL,String,String,int,String,String,String,String)</code>.
+	 * 
+	 * @see "java.net.URLStreamHandler.setURL(URL,String,String,int,String,String,String,String)"
+	 */
+	protected void setURL(URL u, String proto, String host, int port,
+			String auth, String user, String path, String query, String ref) {
+		realHandler.setURL(u, proto, host, port, auth, user, path, query, ref);
+	}
+}
\ No newline at end of file

Added: incubator/oscar/trunk/src/org/osgi/service/url/URLConstants.java
URL: http://svn.apache.org/viewcvs/incubator/oscar/trunk/src/org/osgi/service/url/URLConstants.java?rev=233031&view=auto
==============================================================================
--- incubator/oscar/trunk/src/org/osgi/service/url/URLConstants.java (added)
+++ incubator/oscar/trunk/src/org/osgi/service/url/URLConstants.java Tue Aug 16 11:33:34 2005
@@ -0,0 +1,36 @@
+/*
+ * $Header: /cvshome/build/org.osgi.service.url/src/org/osgi/service/url/URLConstants.java,v 1.6 2005/05/13 20:32:35 hargrave Exp $
+ * 
+ * Copyright (c) OSGi Alliance (2002, 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.url;
+
+/**
+ * Defines standard names for property keys associated with
+ * {@link URLStreamHandlerService}and <code>java.net.ContentHandler</code>
+ * services.
+ * 
+ * <p>
+ * The values associated with these keys are of type <code>java.lang.String[]</code>,
+ * unless otherwise indicated.
+ * 
+ * @version $Revision: 1.6 $
+ */
+public interface URLConstants {
+	/**
+	 * Service property naming the protocols serviced by a
+	 * URLStreamHandlerService. The property's value is an array of protocol
+	 * names.
+	 */
+	public static final String	URL_HANDLER_PROTOCOL	= "url.handler.protocol";
+	/**
+	 * Service property naming the MIME types serviced by a
+	 * java.net.ContentHandler. The property's value is an array of MIME types.
+	 */
+	public static final String	URL_CONTENT_MIMETYPE	= "url.content.mimetype";
+}
\ No newline at end of file

Added: incubator/oscar/trunk/src/org/osgi/service/url/URLStreamHandlerService.java
URL: http://svn.apache.org/viewcvs/incubator/oscar/trunk/src/org/osgi/service/url/URLStreamHandlerService.java?rev=233031&view=auto
==============================================================================
--- incubator/oscar/trunk/src/org/osgi/service/url/URLStreamHandlerService.java (added)
+++ incubator/oscar/trunk/src/org/osgi/service/url/URLStreamHandlerService.java Tue Aug 16 11:33:34 2005
@@ -0,0 +1,84 @@
+/*
+ * $Header: /cvshome/build/org.osgi.service.url/src/org/osgi/service/url/URLStreamHandlerService.java,v 1.6 2005/05/13 20:32:35 hargrave Exp $
+ * 
+ * Copyright (c) OSGi Alliance (2002, 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.url;
+
+import java.net.*;
+
+/**
+ * Service interface with public versions of the protected
+ * <code>java.net.URLStreamHandler</code> methods.
+ * <p>
+ * The important differences between this interface and the
+ * <code>URLStreamHandler</code> class are that the <code>setURL</code> method is
+ * absent and the <code>parseURL</code> method takes a
+ * {@link URLStreamHandlerSetter}object as the first argument. Classes
+ * implementing this interface must call the <code>setURL</code> method on the
+ * <code>URLStreamHandlerSetter</code> object received in the <code>parseURL</code>
+ * method instead of <code>URLStreamHandler.setURL</code> to avoid a
+ * <code>SecurityException</code>.
+ * 
+ * @see AbstractURLStreamHandlerService
+ * 
+ * @version $Revision: 1.6 $
+ */
+public interface URLStreamHandlerService {
+	/**
+	 * @see "java.net.URLStreamHandler.openConnection"
+	 */
+	public URLConnection openConnection(URL u) throws java.io.IOException;
+
+	/**
+	 * Parse a URL. This method is called by the <code>URLStreamHandler</code>
+	 * proxy, instead of <code>java.net.URLStreamHandler.parseURL</code>, passing
+	 * a <code>URLStreamHandlerSetter</code> object.
+	 * 
+	 * @param realHandler The object on which <code>setURL</code> must be invoked
+	 *        for this URL.
+	 * @see "java.net.URLStreamHandler.parseURL"
+	 */
+	public void parseURL(URLStreamHandlerSetter realHandler, URL u,
+			String spec, int start, int limit);
+
+	/**
+	 * @see "java.net.URLStreamHandler.toExternalForm"
+	 */
+	public String toExternalForm(URL u);
+
+	/**
+	 * @see "java.net.URLStreamHandler.equals(URL, URL)"
+	 */
+	public boolean equals(URL u1, URL u2);
+
+	/**
+	 * @see "java.net.URLStreamHandler.getDefaultPort"
+	 */
+	public int getDefaultPort();
+
+	/**
+	 * @see "java.net.URLStreamHandler.getHostAddress"
+	 */
+	public InetAddress getHostAddress(URL u);
+
+	/**
+	 * @see "java.net.URLStreamHandler.hashCode(URL)"
+	 */
+	public int hashCode(URL u);
+
+	/**
+	 * @see "java.net.URLStreamHandler.hostsEqual"
+	 */
+	public boolean hostsEqual(URL u1, URL u2);
+
+	/**
+	 * @see "java.net.URLStreamHandler.sameFile"
+	 */
+	public boolean sameFile(URL u1, URL u2);
+}
\ No newline at end of file

Added: incubator/oscar/trunk/src/org/osgi/service/url/URLStreamHandlerSetter.java
URL: http://svn.apache.org/viewcvs/incubator/oscar/trunk/src/org/osgi/service/url/URLStreamHandlerSetter.java?rev=233031&view=auto
==============================================================================
--- incubator/oscar/trunk/src/org/osgi/service/url/URLStreamHandlerSetter.java (added)
+++ incubator/oscar/trunk/src/org/osgi/service/url/URLStreamHandlerSetter.java Tue Aug 16 11:33:34 2005
@@ -0,0 +1,44 @@
+/*
+ * $Header: /cvshome/build/org.osgi.service.url/src/org/osgi/service/url/URLStreamHandlerSetter.java,v 1.6 2005/05/13 20:32:35 hargrave Exp $
+ * 
+ * Copyright (c) OSGi Alliance (2002, 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.url;
+
+import java.net.URL;
+
+/**
+ * Interface used by <code>URLStreamHandlerService</code> objects to call the
+ * <code>setURL</code> method on the proxy <code>URLStreamHandler</code> object.
+ * 
+ * <p>
+ * Objects of this type are passed to the
+ * {@link URLStreamHandlerService#parseURL}method. Invoking the <code>setURL</code>
+ * method on the <code>URLStreamHandlerSetter</code> object will invoke the
+ * <code>setURL</code> method on the proxy <code>URLStreamHandler</code> object that
+ * is actually registered with <code>java.net.URL</code> for the protocol.
+ * 
+ * @version $Revision: 1.6 $
+ */
+public interface URLStreamHandlerSetter {
+	/**
+	 * @see "java.net.URLStreamHandler.setURL(URL,String,String,int,String,String)"
+	 * 
+	 * @deprecated This method is only for compatibility with handlers written
+	 *             for JDK 1.1.
+	 */
+	public void setURL(URL u, String protocol, String host, int port,
+			String file, String ref);
+
+	/**
+	 * @see "java.net.URLStreamHandler.setURL(URL,String,String,int,String,String,String,String)"
+	 */
+	public void setURL(URL u, String protocol, String host, int port,
+			String authority, String userInfo, String path, String query,
+			String ref);
+}
\ No newline at end of file

Added: incubator/oscar/trunk/src/org/osgi/util/tracker/ServiceTracker.java
URL: http://svn.apache.org/viewcvs/incubator/oscar/trunk/src/org/osgi/util/tracker/ServiceTracker.java?rev=233031&view=auto
==============================================================================
--- incubator/oscar/trunk/src/org/osgi/util/tracker/ServiceTracker.java (added)
+++ incubator/oscar/trunk/src/org/osgi/util/tracker/ServiceTracker.java Tue Aug 16 11:33:34 2005
@@ -0,0 +1,982 @@
+/*
+ * $Header: /cvshome/build/org.osgi.util.tracker/src/org/osgi/util/tracker/ServiceTracker.java,v 1.13 2005/05/13 20:33:35 hargrave Exp $
+ * 
+ * Copyright (c) OSGi Alliance (2000, 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.util.tracker;
+
+import java.util.*;
+
+import org.osgi.framework.*;
+
+/**
+ * The <code>ServiceTracker</code> class simplifies using services from the
+ * Framework's service registry.
+ * <p>
+ * A <code>ServiceTracker</code> object is constructed with search criteria
+ * and a <code>ServiceTrackerCustomizer</code> object. A
+ * <code>ServiceTracker</code> object can use the
+ * <code>ServiceTrackerCustomizer</code> object to customize the service
+ * objects to be tracked. The <code>ServiceTracker</code> object can then be
+ * opened to begin tracking all services in the Framework's service registry
+ * that match the specified search criteria. The <code>ServiceTracker</code>
+ * object correctly handles all of the details of listening to
+ * <code>ServiceEvent</code> objects and getting and ungetting services.
+ * <p>
+ * The <code>getServiceReferences</code> method can be called to get
+ * references to the services being tracked. The <code>getService</code> and
+ * <code>getServices</code> methods can be called to get the service objects
+ * for the tracked service.
+ * 
+ * @version $Revision: 1.13 $
+ */
+public class ServiceTracker implements ServiceTrackerCustomizer {
+	/* set this to true to compile in debug messages */
+	static final boolean					DEBUG			= false;
+	/**
+	 * Bundle context this <code>ServiceTracker</code> object is tracking
+	 * against.
+	 */
+	protected final BundleContext			context;
+	/**
+	 * Filter specifying search criteria for the services to track.
+	 * 
+	 * @since 1.1
+	 */
+	protected final Filter					filter;
+	/**
+	 * <code>ServiceTrackerCustomizer</code> object for this tracker.
+	 */
+	private final ServiceTrackerCustomizer	customizer;
+	/**
+	 * Filter string for use when adding the ServiceListener. If this field is
+	 * set, then certain optimizations can be taken since we don't have a user
+	 * supplied filter.
+	 */
+	private final String					listenerFilter;
+	/**
+	 * Class name to be tracked. If this field is set, then we are tracking by
+	 * class name.
+	 */
+	private final String					trackClass;
+	/**
+	 * Reference to be tracked. If this field is set, then we are tracking a
+	 * single ServiceReference.
+	 */
+	private final ServiceReference			trackReference;
+	/**
+	 * Tracked services: <code>ServiceReference</code> object -> customized
+	 * Object and <code>ServiceListener</code> object
+	 */
+	private Tracked							tracked;
+	/**
+	 * Modification count. This field is initialized to zero by open, set to -1
+	 * by close and incremented by modified. This field is volatile since it is
+	 * accessed by multiple threads.
+	 */
+	private volatile int					trackingCount	= -1;
+	/**
+	 * Cached ServiceReference for getServiceReference. This field is volatile
+	 * since it is accessed by multiple threads.
+	 */
+	private volatile ServiceReference		cachedReference;
+	/**
+	 * Cached service object for getService. This field is volatile since it is
+	 * accessed by multiple threads.
+	 */
+	private volatile Object					cachedService;
+
+	/**
+	 * Create a <code>ServiceTracker</code> object on the specified
+	 * <code>ServiceReference</code> object.
+	 * 
+	 * <p>
+	 * The service referenced by the specified <code>ServiceReference</code>
+	 * object will be tracked by this <code>ServiceTracker</code> object.
+	 * 
+	 * @param context <code>BundleContext</code> object against which the
+	 *        tracking is done.
+	 * @param reference <code>ServiceReference</code> object for the service
+	 *        to be tracked.
+	 * @param customizer The customizer object to call when services are added,
+	 *        modified, or removed in this <code>ServiceTracker</code> object.
+	 *        If customizer is <code>null</code>, then this
+	 *        <code>ServiceTracker</code> object will be used as the
+	 *        <code>ServiceTrackerCustomizer</code> object and the
+	 *        <code>ServiceTracker</code> object will call the
+	 *        <code>ServiceTrackerCustomizer</code> methods on itself.
+	 */
+	public ServiceTracker(BundleContext context, ServiceReference reference,
+			ServiceTrackerCustomizer customizer) {
+		this.context = context;
+		this.trackReference = reference;
+		this.trackClass = null;
+		this.customizer = (customizer == null) ? this : customizer;
+		this.listenerFilter = "(" + Constants.SERVICE_ID + "=" + reference.getProperty(Constants.SERVICE_ID).toString() + ")"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		try {
+			this.filter = context.createFilter(listenerFilter);
+		}
+		catch (InvalidSyntaxException e) { // we could only get this exception
+			// if the ServiceReference was
+			// invalid
+			throw new IllegalArgumentException(
+					"unexpected InvalidSyntaxException: " + e.getMessage()); //$NON-NLS-1$
+		}
+	}
+
+	/**
+	 * Create a <code>ServiceTracker</code> object on the specified class
+	 * name.
+	 * 
+	 * <p>
+	 * Services registered under the specified class name will be tracked by
+	 * this <code>ServiceTracker</code> object.
+	 * 
+	 * @param context <code>BundleContext</code> object against which the
+	 *        tracking is done.
+	 * @param clazz Class name of the services to be tracked.
+	 * @param customizer The customizer object to call when services are added,
+	 *        modified, or removed in this <code>ServiceTracker</code> object.
+	 *        If customizer is <code>null</code>, then this
+	 *        <code>ServiceTracker</code> object will be used as the
+	 *        <code>ServiceTrackerCustomizer</code> object and the
+	 *        <code>ServiceTracker</code> object will call the
+	 *        <code>ServiceTrackerCustomizer</code> methods on itself.
+	 */
+	public ServiceTracker(BundleContext context, String clazz,
+			ServiceTrackerCustomizer customizer) {
+		this.context = context;
+		this.trackReference = null;
+		this.trackClass = clazz;
+		this.customizer = (customizer == null) ? this : customizer;
+		this.listenerFilter = "(" + Constants.OBJECTCLASS + "=" + clazz.toString() + ")"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		try {
+			this.filter = context.createFilter(listenerFilter);
+		}
+		catch (InvalidSyntaxException e) { // we could only get this exception
+			// if the clazz argument was
+			// malformed
+			throw new IllegalArgumentException(
+					"unexpected InvalidSyntaxException: " + e.getMessage()); //$NON-NLS-1$
+		}
+	}
+
+	/**
+	 * Create a <code>ServiceTracker</code> object on the specified
+	 * <code>Filter</code> object.
+	 * 
+	 * <p>
+	 * Services which match the specified <code>Filter</code> object will be
+	 * tracked by this <code>ServiceTracker</code> object.
+	 * 
+	 * @param context <code>BundleContext</code> object against which the
+	 *        tracking is done.
+	 * @param filter <code>Filter</code> object to select the services to be
+	 *        tracked.
+	 * @param customizer The customizer object to call when services are added,
+	 *        modified, or removed in this <code>ServiceTracker</code> object.
+	 *        If customizer is null, then this <code>ServiceTracker</code>
+	 *        object will be used as the <code>ServiceTrackerCustomizer</code>
+	 *        object and the <code>ServiceTracker</code> object will call the
+	 *        <code>ServiceTrackerCustomizer</code> methods on itself.
+	 * @since 1.1
+	 */
+	public ServiceTracker(BundleContext context, Filter filter,
+			ServiceTrackerCustomizer customizer) {
+		this.context = context;
+		this.trackReference = null;
+		this.trackClass = null;
+		this.listenerFilter = null;
+		this.filter = filter;
+		this.customizer = (customizer == null) ? this : customizer;
+		if ((context == null) || (filter == null)) { // we throw a NPE here
+			// to
+			// be consistent with the
+			// other constructors
+			throw new NullPointerException();
+		}
+	}
+
+	/**
+	 * Open this <code>ServiceTracker</code> object and begin tracking
+	 * services.
+	 * 
+	 * <p>
+	 * This method calls <code>open(false)</code>.
+	 * 
+	 * @throws java.lang.IllegalStateException if the <code>BundleContext</code>
+	 *         object with which this <code>ServiceTracker</code> object was
+	 *         created is no longer valid.
+	 * @see #open(boolean)
+	 */
+	public void open() {
+		open(false);
+	}
+
+	/**
+	 * Open this <code>ServiceTracker</code> object and begin tracking
+	 * services.
+	 * 
+	 * <p>
+	 * Services which match the search criteria specified when this
+	 * <code>ServiceTracker</code> object was created are now tracked by this
+	 * <code>ServiceTracker</code> object.
+	 * 
+	 * @param trackAllServices If <code>true</code>, then this
+	 *        <code>ServiceTracker</code> will track all matching services
+	 *        regardless of class loader accessibility. If <code>false</code>,
+	 *        then this <code>ServiceTracker</code> will only track matching
+	 *        services which are class loader accessibile to the bundle whose
+	 *        <code>BundleContext</code> is used by this
+	 *        <code>ServiceTracker</code>.
+	 * @throws java.lang.IllegalStateException if the <code>BundleContext</code>
+	 *         object with which this <code>ServiceTracker</code> object was
+	 *         created is no longer valid.
+	 * @since 1.3
+	 */
+	public synchronized void open(boolean trackAllServices) {
+		if (tracked != null) {
+			return;
+		}
+		if (DEBUG) {
+			System.out.println("ServiceTracker.open: " + filter); //$NON-NLS-1$
+		}
+		tracked = trackAllServices ? new AllTracked() : new Tracked();
+		trackingCount = 0;
+		ServiceReference[] references;
+		synchronized (tracked) {
+			try {
+				context.addServiceListener(tracked, listenerFilter);
+				if (listenerFilter == null) { // user supplied filter
+					references = getInitialReferences(trackAllServices, null,
+							filter.toString());
+				}
+				else { // constructor supplied filter
+					if (trackClass == null) {
+						references = new ServiceReference[] {trackReference};
+					}
+					else {
+						references = getInitialReferences(trackAllServices,
+								trackClass, null);
+					}
+				}
+			}
+			catch (InvalidSyntaxException e) {
+				throw new RuntimeException(
+						"unexpected InvalidSyntaxException: " + e.getMessage()); //$NON-NLS-1$
+			}
+		}
+		/* Call tracked outside of synchronized region */
+		if (references != null) {
+			int length = references.length;
+			for (int i = 0; i < length; i++) {
+				ServiceReference reference = references[i];
+				/* if the service is still registered */
+				if (reference.getBundle() != null) {
+					tracked.track(reference);
+				}
+			}
+		}
+	}
+
+	/**
+	 * Returns the list of initial <code>ServiceReference</code> objects that
+	 * will be tracked by this <code>ServiceTracker</code> object.
+	 * 
+	 * @param trackAllServices If true, use getAllServiceReferences.
+	 * @param trackClass the class name with which the service was registered,
+	 *        or null for all services.
+	 * @param filterString the filter criteria or null for all services.
+	 * @return the list of initial <code>ServiceReference</code> objects.
+	 * @throws InvalidSyntaxException if the filter uses an invalid syntax.
+	 */
+	private ServiceReference[] getInitialReferences(boolean trackAllServices,
+			String trackClass, String filterString)
+			throws InvalidSyntaxException {
+		if (trackAllServices) {
+			return context.getAllServiceReferences(trackClass, filterString);
+		}
+		else {
+			return context.getServiceReferences(trackClass, filterString);
+		}
+	}
+
+	/**
+	 * Close this <code>ServiceTracker</code> object.
+	 * 
+	 * <p>
+	 * This method should be called when this <code>ServiceTracker</code>
+	 * object should end the tracking of services.
+	 */
+	public synchronized void close() {
+		if (tracked == null) {
+			return;
+		}
+		if (DEBUG) {
+			System.out.println("ServiceTracker.close: " + filter); //$NON-NLS-1$
+		}
+		tracked.close();
+		ServiceReference[] references = getServiceReferences();
+		Tracked outgoing = tracked;
+		tracked = null;
+		try {
+			context.removeServiceListener(outgoing);
+		}
+		catch (IllegalStateException e) {
+			/* In case the context was stopped. */
+		}
+		if (references != null) {
+			for (int i = 0; i < references.length; i++) {
+				outgoing.untrack(references[i]);
+			}
+		}
+		trackingCount = -1;
+		if (DEBUG) {
+			if ((cachedReference == null) && (cachedService == null)) {
+				System.out
+						.println("ServiceTracker.close[cached cleared]: " + filter); //$NON-NLS-1$
+			}
+		}
+	}
+
+	/**
+	 * Default implementation of the
+	 * <code>ServiceTrackerCustomizer.addingService</code> method.
+	 * 
+	 * <p>
+	 * This method is only called when this <code>ServiceTracker</code> object
+	 * has been constructed with a <code>null ServiceTrackerCustomizer</code>
+	 * argument.
+	 * 
+	 * The default implementation returns the result of calling
+	 * <code>getService</code>, on the <code>BundleContext</code> object
+	 * with which this <code>ServiceTracker</code> object was created, passing
+	 * the specified <code>ServiceReference</code> object.
+	 * <p>
+	 * This method can be overridden in a subclass to customize the service
+	 * object to be tracked for the service being added. In that case, take care
+	 * not to rely on the default implementation of removedService that will
+	 * unget the service.
+	 * 
+	 * @param reference Reference to service being added to this
+	 *        <code>ServiceTracker</code> object.
+	 * @return The service object to be tracked for the service added to this
+	 *         <code>ServiceTracker</code> object.
+	 * @see ServiceTrackerCustomizer
+	 */
+	public Object addingService(ServiceReference reference) {
+		return context.getService(reference);
+	}
+
+	/**
+	 * Default implementation of the
+	 * <code>ServiceTrackerCustomizer.modifiedService</code> method.
+	 * 
+	 * <p>
+	 * This method is only called when this <code>ServiceTracker</code> object
+	 * has been constructed with a <code>null ServiceTrackerCustomizer</code>
+	 * argument.
+	 * 
+	 * The default implementation does nothing.
+	 * 
+	 * @param reference Reference to modified service.
+	 * @param service The service object for the modified service.
+	 * @see ServiceTrackerCustomizer
+	 */
+	public void modifiedService(ServiceReference reference, Object service) {
+	}
+
+	/**
+	 * Default implementation of the
+	 * <code>ServiceTrackerCustomizer.removedService</code> method.
+	 * 
+	 * <p>
+	 * This method is only called when this <code>ServiceTracker</code> object
+	 * has been constructed with a <code>null ServiceTrackerCustomizer</code>
+	 * argument.
+	 * 
+	 * The default implementation calls <code>ungetService</code>, on the
+	 * <code>BundleContext</code> object with which this
+	 * <code>ServiceTracker</code> object was created, passing the specified
+	 * <code>ServiceReference</code> object.
+	 * <p>
+	 * This method can be overridden in a subclass. If the default
+	 * implementation of <code>addingService</code> method was used, this
+	 * method must unget the service.
+	 * 
+	 * @param reference Reference to removed service.
+	 * @param service The service object for the removed service.
+	 * @see ServiceTrackerCustomizer
+	 */
+	public void removedService(ServiceReference reference, Object service) {
+		context.ungetService(reference);
+	}
+
+	/**
+	 * Wait for at least one service to be tracked by this
+	 * <code>ServiceTracker</code> object.
+	 * <p>
+	 * It is strongly recommended that <code>waitForService</code> is not used
+	 * during the calling of the <code>BundleActivator</code> methods.
+	 * <code>BundleActivator</code> methods are expected to complete in a
+	 * short period of time.
+	 * 
+	 * @param timeout time interval in milliseconds to wait. If zero, the method
+	 *        will wait indefinately.
+	 * @return Returns the result of <code>getService()</code>.
+	 * @throws IllegalArgumentException If the value of timeout is negative.
+	 */
+	public Object waitForService(long timeout) throws InterruptedException {
+		if (timeout < 0) {
+			throw new IllegalArgumentException("timeout value is negative"); //$NON-NLS-1$
+		}
+		Object object = getService();
+		while (object == null) {
+			Tracked tracked = this.tracked; /*
+											 * use local var since we are not
+											 * synchronized
+											 */
+			if (tracked == null) /* if ServiceTracker is not open */
+			{
+				return null;
+			}
+			synchronized (tracked) {
+				if (tracked.size() == 0) {
+					tracked.wait(timeout);
+				}
+			}
+			object = getService();
+			if (timeout > 0) {
+				return object;
+			}
+		}
+		return object;
+	}
+
+	/**
+	 * Return an array of <code>ServiceReference</code> objects for all
+	 * services being tracked by this <code>ServiceTracker</code> object.
+	 * 
+	 * @return Array of <code>ServiceReference</code> objects or
+	 *         <code>null</code> if no service are being tracked.
+	 */
+	public ServiceReference[] getServiceReferences() {
+		Tracked tracked = this.tracked; /*
+										 * use local var since we are not
+										 * synchronized
+										 */
+		if (tracked == null) /* if ServiceTracker is not open */
+		{
+			return null;
+		}
+		synchronized (tracked) {
+			int length = tracked.size();
+			if (length == 0) {
+				return null;
+			}
+			ServiceReference[] references = new ServiceReference[length];
+			Enumeration keys = tracked.keys();
+			for (int i = 0; i < length; i++) {
+				references[i] = (ServiceReference) keys.nextElement();
+			}
+			return references;
+		}
+	}
+
+	/**
+	 * Returns a <code>ServiceReference</code> object for one of the services
+	 * being tracked by this <code>ServiceTracker</code> object.
+	 * 
+	 * <p>
+	 * If multiple services are being tracked, the service with the highest
+	 * ranking (as specified in its <code>service.ranking</code> property) is
+	 * returned.
+	 * 
+	 * <p>
+	 * If there is a tie in ranking, the service with the lowest service ID (as
+	 * specified in its <code>service.id</code> property); that is, the
+	 * service that was registered first is returned.
+	 * <p>
+	 * This is the same algorithm used by
+	 * <code>BundleContext.getServiceReference</code>.
+	 * 
+	 * @return <code>ServiceReference</code> object or <code>null</code> if
+	 *         no service is being tracked.
+	 * @since 1.1
+	 */
+	public ServiceReference getServiceReference() {
+		ServiceReference reference = cachedReference;
+		if (reference != null) {
+			if (DEBUG) {
+				System.out
+						.println("ServiceTracker.getServiceReference[cached]: " + filter); //$NON-NLS-1$
+			}
+			return reference;
+		}
+		if (DEBUG) {
+			System.out.println("ServiceTracker.getServiceReference: " + filter); //$NON-NLS-1$
+		}
+		ServiceReference[] references = getServiceReferences();
+		int length = (references == null) ? 0 : references.length;
+		if (length == 0) /* if no service is being tracked */
+		{
+			return null;
+		}
+		int index = 0;
+		if (length > 1) /* if more than one service, select highest ranking */
+		{
+			int rankings[] = new int[length];
+			int count = 0;
+			int maxRanking = Integer.MIN_VALUE;
+			for (int i = 0; i < length; i++) {
+				Object property = references[i]
+						.getProperty(Constants.SERVICE_RANKING);
+				int ranking = (property instanceof Integer) ? ((Integer) property)
+						.intValue()
+						: 0;
+				rankings[i] = ranking;
+				if (ranking > maxRanking) {
+					index = i;
+					maxRanking = ranking;
+					count = 1;
+				}
+				else {
+					if (ranking == maxRanking) {
+						count++;
+					}
+				}
+			}
+			if (count > 1) /* if still more than one service, select lowest id */
+			{
+				long minId = Long.MAX_VALUE;
+				for (int i = 0; i < length; i++) {
+					if (rankings[i] == maxRanking) {
+						long id = ((Long) (references[i]
+								.getProperty(Constants.SERVICE_ID)))
+								.longValue();
+						if (id < minId) {
+							index = i;
+							minId = id;
+						}
+					}
+				}
+			}
+		}
+		return cachedReference = references[index];
+	}
+
+	/**
+	 * Returns the service object for the specified
+	 * <code>ServiceReference</code> object if the referenced service is being
+	 * tracked by this <code>ServiceTracker</code> object.
+	 * 
+	 * @param reference Reference to the desired service.
+	 * @return Service object or <code>null</code> if the service referenced
+	 *         by the specified <code>ServiceReference</code> object is not
+	 *         being tracked.
+	 */
+	public Object getService(ServiceReference reference) {
+		Tracked tracked = this.tracked; /*
+										 * use local var since we are not
+										 * synchronized
+										 */
+		if (tracked == null) /* if ServiceTracker is not open */
+		{
+			return null;
+		}
+		synchronized (tracked) {
+			return tracked.get(reference);
+		}
+	}
+
+	/**
+	 * Return an array of service objects for all services being tracked by this
+	 * <code>ServiceTracker</code> object.
+	 * 
+	 * @return Array of service objects or <code>null</code> if no service are
+	 *         being tracked.
+	 */
+	public Object[] getServices() {
+		Tracked tracked = this.tracked; /*
+										 * use local var since we are not
+										 * synchronized
+										 */
+		if (tracked == null) /* if ServiceTracker is not open */
+		{
+			return null;
+		}
+		synchronized (tracked) {
+			ServiceReference[] references = getServiceReferences();
+			int length = (references == null) ? 0 : references.length;
+			if (length == 0) {
+				return null;
+			}
+			Object[] objects = new Object[length];
+			for (int i = 0; i < length; i++) {
+				objects[i] = getService(references[i]);
+			}
+			return objects;
+		}
+	}
+
+	/**
+	 * Returns a service object for one of the services being tracked by this
+	 * <code>ServiceTracker</code> object.
+	 * 
+	 * <p>
+	 * If any services are being tracked, this method returns the result of
+	 * calling <code>getService(getServiceReference())</code>.
+	 * 
+	 * @return Service object or <code>null</code> if no service is being
+	 *         tracked.
+	 */
+	public Object getService() {
+		Object service = cachedService;
+		if (service != null) {
+			if (DEBUG) {
+				System.out
+						.println("ServiceTracker.getService[cached]: " + filter); //$NON-NLS-1$
+			}
+			return service;
+		}
+		if (DEBUG) {
+			System.out.println("ServiceTracker.getService: " + filter); //$NON-NLS-1$
+		}
+		ServiceReference reference = getServiceReference();
+		if (reference == null) {
+			return null;
+		}
+		return cachedService = getService(reference);
+	}
+
+	/**
+	 * Remove a service from this <code>ServiceTracker</code> object.
+	 * 
+	 * The specified service will be removed from this
+	 * <code>ServiceTracker</code> object. If the specified service was being
+	 * tracked then the <code>ServiceTrackerCustomizer.removedService</code>
+	 * method will be called for that service.
+	 * 
+	 * @param reference Reference to the service to be removed.
+	 */
+	public void remove(ServiceReference reference) {
+		Tracked tracked = this.tracked; /*
+										 * use local var since we are not
+										 * synchronized
+										 */
+		if (tracked == null) /* if ServiceTracker is not open */
+		{
+			return;
+		}
+		tracked.untrack(reference);
+	}
+
+	/**
+	 * Return the number of services being tracked by this
+	 * <code>ServiceTracker</code> object.
+	 * 
+	 * @return Number of services being tracked.
+	 */
+	public int size() {
+		Tracked tracked = this.tracked; /*
+										 * use local var since we are not
+										 * synchronized
+										 */
+		if (tracked == null) /* if ServiceTracker is not open */
+		{
+			return 0;
+		}
+		return tracked.size();
+	}
+
+	/**
+	 * Returns the tracking count for this <code>ServiceTracker</code> object.
+	 * 
+	 * The tracking count is initialized to 0 when this
+	 * <code>ServiceTracker</code> object is opened. Every time a service is
+	 * added or removed from this <code>ServiceTracker</code> object the
+	 * tracking count is incremented.
+	 * 
+	 * <p>
+	 * The tracking count can be used to determine if this
+	 * <code>ServiceTracker</code> object has added or removed a service by
+	 * comparing a tracking count value previously collected with the current
+	 * tracking count value. If the value has not changed, then no service has
+	 * been added or removed from this <code>ServiceTracker</code> object
+	 * since the previous tracking count was collected.
+	 * 
+	 * @since 1.2
+	 * @return The tracking count for this <code>ServiceTracker</code> object
+	 *         or -1 if this <code>ServiceTracker</code> object is not open.
+	 */
+	public int getTrackingCount() {
+		return trackingCount;
+	}
+
+	/**
+	 * Called by the Tracked object whenever the set of tracked services is
+	 * modified. Increments the tracking count and clears the cache.
+	 */
+	/*
+	 * This method must not be synchronized since it is called by Tracked while
+	 * Tracked is synchronized. We don't want synchronization interactions
+	 * between the ServiceListener thread and the user thread.
+	 */
+	private void modified() {
+		trackingCount++; /* increment modification count */
+		cachedReference = null; /* clear cached value */
+		cachedService = null; /* clear cached value */
+		if (DEBUG) {
+			System.out.println("ServiceTracker.modified: " + filter); //$NON-NLS-1$
+		}
+	}
+
+	/**
+	 * Inner class to track services. If a <code>ServiceTracker</code> object
+	 * is reused (closed then reopened), then a new Tracked object is used. This
+	 * class is a hashtable mapping <code>ServiceReference</code> object ->
+	 * customized Object. This class is the <code>ServiceListener</code>
+	 * object for the tracker. This class is used to synchronize access to the
+	 * tracked services. This is not a public class. It is only for use by the
+	 * implementation of the <code>ServiceTracker</code> class.
+	 * 
+	 */
+	class Tracked extends Hashtable implements ServiceListener {
+		static final long			serialVersionUID	= -7420065199791006079L;
+		/**
+		 * List of ServiceReferences in the process of being added.
+		 */
+		private ArrayList			adding;
+		/**
+		 * true if the tracked object is closed. This field is volatile because
+		 * it is set by one thread and read by another.
+		 */
+		private volatile boolean	closed;
+
+		/**
+		 * Tracked constructor.
+		 */
+		protected Tracked() {
+			super();
+			closed = false;
+			adding = new ArrayList(6);
+		}
+
+		/**
+		 * Called by the owning <code>ServiceTracker</code> object when it is
+		 * closed.
+		 */
+		protected void close() {
+			closed = true;
+		}
+
+		/**
+		 * <code>ServiceListener</code> method for the
+		 * <code>ServiceTracker</code> class. This method must NOT be
+		 * synchronized to avoid deadlock potential.
+		 * 
+		 * @param event <code>ServiceEvent</code> object from the framework.
+		 */
+		public void serviceChanged(ServiceEvent event) {
+			/*
+			 * Check if we had a delayed call (which could happen when we
+			 * close).
+			 */
+			if (closed) {
+				return;
+			}
+			ServiceReference reference = event.getServiceReference();
+			switch (event.getType()) {
+				case ServiceEvent.REGISTERED :
+				case ServiceEvent.MODIFIED :
+					if (listenerFilter != null) { // constructor supplied
+						// filter
+						track(reference);
+						/*
+						 * If the customizer throws an unchecked exception, it
+						 * is safe to let it propagate
+						 */
+					}
+					else { // user supplied filter
+						if (filter.match(reference)) {
+							track(reference);
+							/*
+							 * If the customizer throws an unchecked exception,
+							 * it is safe to let it propagate
+							 */
+						}
+						else {
+							untrack(reference);
+							/*
+							 * If the customizer throws an unchecked exception,
+							 * it is safe to let it propagate
+							 */
+						}
+					}
+					break;
+				case ServiceEvent.UNREGISTERING :
+					untrack(reference);
+					/*
+					 * If the customizer throws an unchecked exception, it is
+					 * safe to let it propagate
+					 */
+					break;
+			}
+		}
+
+		/**
+		 * Begin to track the referenced service.
+		 * 
+		 * @param reference Reference to a service to be tracked.
+		 */
+		protected void track(ServiceReference reference) {
+			Object object;
+			synchronized (this) {
+				object = this.get(reference);
+			}
+			if (object != null) /* we are already tracking the service */
+			{
+				if (DEBUG) {
+					System.out
+							.println("ServiceTracker.Tracked.track[modified]: " + reference); //$NON-NLS-1$
+				}
+				/* Call customizer outside of synchronized region */
+				customizer.modifiedService(reference, object);
+				/*
+				 * If the customizer throws an unchecked exception, it is safe
+				 * to let it propagate
+				 */
+				return;
+			}
+			synchronized (this) {
+				if (adding.contains(reference)) /*
+												 * if this service is already in
+												 * the process of being added.
+												 */
+				{
+					if (DEBUG) {
+						System.out
+								.println("ServiceTracker.Tracked.track[already adding]: " + reference); //$NON-NLS-1$
+					}
+					return;
+				}
+				adding.add(reference); /* mark this service is being added */
+			}
+			if (DEBUG) {
+				System.out
+						.println("ServiceTracker.Tracked.track[adding]: " + reference); //$NON-NLS-1$
+			}
+			boolean becameUntracked = false;
+			/* Call customizer outside of synchronized region */
+			try {
+				object = customizer.addingService(reference);
+				/*
+				 * If the customizer throws an unchecked exception, it will
+				 * propagate after the finally
+				 */
+			}
+			finally {
+				synchronized (this) {
+					if (adding.remove(reference)) /*
+													 * if the service was not
+													 * untracked during the
+													 * customizer callback
+													 */
+					{
+						if (object != null) {
+							this.put(reference, object);
+							modified(); /* increment modification count */
+							notifyAll();
+						}
+					}
+					else {
+						becameUntracked = true;
+					}
+				}
+			}
+			/*
+			 * The service became untracked during the customizer callback.
+			 */
+			if (becameUntracked) {
+				if (DEBUG) {
+					System.out
+							.println("ServiceTracker.Tracked.track[removed]: " + reference); //$NON-NLS-1$
+				}
+				/* Call customizer outside of synchronized region */
+				customizer.removedService(reference, object);
+				/*
+				 * If the customizer throws an unchecked exception, it is safe
+				 * to let it propagate
+				 */
+			}
+		}
+
+		/**
+		 * Discontinue tracking the referenced service.
+		 * 
+		 * @param reference Reference to the tracked service.
+		 */
+		protected void untrack(ServiceReference reference) {
+			Object object;
+			synchronized (this) {
+				if (adding.remove(reference)) /*
+												 * if the service is in the
+												 * process of being added
+												 */
+				{
+					if (DEBUG) {
+						System.out
+								.println("ServiceTracker.Tracked.untrack[being added]: " + reference); //$NON-NLS-1$
+					}
+					return; /*
+							 * in case the service is untracked while in the
+							 * process of adding
+							 */
+				}
+				object = this.remove(reference); /*
+													 * must remove from tracker
+													 * before calling customizer
+													 * callback
+													 */
+				if (object == null) /* are we actually tracking the service */
+				{
+					return;
+				}
+				modified(); /* increment modification count */
+			}
+			if (DEBUG) {
+				System.out
+						.println("ServiceTracker.Tracked.untrack[removed]: " + reference); //$NON-NLS-1$
+			}
+			/* Call customizer outside of synchronized region */
+			customizer.removedService(reference, object);
+			/*
+			 * If the customizer throws an unchecked exception, it is safe to
+			 * let it propagate
+			 */
+		}
+	}
+
+	/**
+	 * Subclass of Tracked which implements the AllServiceListener interface.
+	 * This class is used by the ServiceTracker if isAllServiceTracker returns
+	 * true.
+	 * 
+	 * @since 1.3
+	 */
+	class AllTracked extends Tracked implements AllServiceListener {
+		static final long	serialVersionUID	= 4050764875305137716L;
+
+		/**
+		 * Tracked constructor.
+		 */
+		protected AllTracked() {
+			super();
+		}
+	}
+}
\ No newline at end of file