[felix-framework] 01/09: Imported Upstream version 4.4.0

Markus Koschany apo-guest at moszumanska.debian.org
Mon Feb 9 15:41:16 UTC 2015


This is an automated email from the git hooks/post-receive script.

apo-guest pushed a commit to branch experimental
in repository felix-framework.

commit 20ad5d0872ef6cb536b71396f247ead12f19c989
Author: Markus Koschany <apo at gambaru.de>
Date:   Mon Feb 2 00:24:30 2015 +0100

    Imported Upstream version 4.4.0
---
 DEPENDENCIES                                       |   33 +
 LICENSE                                            |  404 ++
 NOTICE                                             |   12 +
 doc/README.txt                                     |    6 +
 doc/changelog.txt                                  |  910 ++++
 pom.xml                                            |  100 +
 src/main/appended-resources/META-INF/DEPENDENCIES  |   22 +
 src/main/appended-resources/META-INF/LICENSE       |  202 +
 src/main/appended-resources/META-INF/NOTICE        |    4 +
 .../apache/felix/framework/BundleContextImpl.java  |  516 ++
 .../org/apache/felix/framework/BundleImpl.java     | 1371 ++++++
 .../felix/framework/BundleProtectionDomain.java    |  111 +
 .../framework/BundleRevisionDependencies.java      |  319 ++
 .../apache/felix/framework/BundleRevisionImpl.java |  674 +++
 .../apache/felix/framework/BundleWiringImpl.java   | 2870 +++++++++++
 .../felix/framework/EntryFilterEnumeration.java    |  245 +
 .../felix/framework/ExportedPackageImpl.java       |   96 +
 .../apache/felix/framework/ExtensionManager.java   |  903 ++++
 .../felix/framework/FakeURLStreamHandler.java      |   41 +
 .../java/org/apache/felix/framework/Felix.java     | 5202 ++++++++++++++++++++
 .../org/apache/felix/framework/FilterImpl.java     |  258 +
 .../apache/felix/framework/FrameworkFactory.java   |   30 +
 .../felix/framework/FrameworkStartLevelImpl.java   |  344 ++
 .../felix/framework/FrameworkWiringImpl.java       |  188 +
 .../java/org/apache/felix/framework/Logger.java    |  324 ++
 .../apache/felix/framework/PackageAdminImpl.java   |  265 +
 .../apache/felix/framework/RequiredBundleImpl.java |   79 +
 .../apache/felix/framework/ResolveContextImpl.java |  110 +
 .../felix/framework/ServiceRegistrationImpl.java   |  732 +++
 .../apache/felix/framework/ServiceRegistry.java    |  818 +++
 .../org/apache/felix/framework/StartLevelImpl.java |  103 +
 .../apache/felix/framework/StatefulResolver.java   | 1654 +++++++
 .../org/apache/felix/framework/URLHandlers.java    |  744 +++
 .../felix/framework/URLHandlersActivator.java      |  123 +
 .../framework/URLHandlersBundleStreamHandler.java  |  154 +
 .../framework/URLHandlersBundleURLConnection.java  |  239 +
 .../framework/URLHandlersContentHandlerProxy.java  |  198 +
 .../framework/URLHandlersStreamHandlerProxy.java   |  651 +++
 .../org/apache/felix/framework/WovenClassImpl.java |  376 ++
 .../felix/framework/cache/BundleArchive.java       | 1378 ++++++
 .../framework/cache/BundleArchiveRevision.java     |  142 +
 .../apache/felix/framework/cache/BundleCache.java  |  440 ++
 .../org/apache/felix/framework/cache/Content.java  |  124 +
 .../framework/cache/ContentDirectoryContent.java   |  158 +
 .../felix/framework/cache/DirectoryContent.java    |  395 ++
 .../felix/framework/cache/DirectoryRevision.java   |  112 +
 .../apache/felix/framework/cache/JarContent.java   |  546 ++
 .../apache/felix/framework/cache/JarRevision.java  |  314 ++
 .../framework/capabilityset/CapabilitySet.java     |  631 +++
 .../framework/capabilityset/SimpleFilter.java      |  650 +++
 .../felix/framework/ext/FelixBundleContext.java    |   30 +
 .../felix/framework/ext/SecurityProvider.java      |   33 +
 .../framework/resolver/CandidateComparator.java    |  101 +
 .../felix/framework/resolver/Candidates.java       | 1046 ++++
 .../felix/framework/resolver/HostedCapability.java |   26 +
 .../felix/framework/resolver/ResolveContext.java   |   52 +
 .../felix/framework/resolver/ResolveException.java |   49 +
 .../apache/felix/framework/resolver/Resolver.java  |   34 +
 .../felix/framework/resolver/ResolverImpl.java     | 1811 +++++++
 .../felix/framework/resolver/ResolverWire.java     |   49 +
 .../felix/framework/resolver/ResolverWireImpl.java |   68 +
 .../resolver/ResourceNotFoundException.java        |   32 +
 .../felix/framework/resolver/ShadowList.java       |  157 +
 .../framework/resolver/SimpleHostedCapability.java |   65 +
 .../framework/resolver/WrappedCapability.java      |  126 +
 .../framework/resolver/WrappedRequirement.java     |  116 +
 .../felix/framework/resolver/WrappedRevision.java  |  169 +
 .../felix/framework/util/CompoundEnumeration.java  |  109 +
 .../felix/framework/util/EventDispatcher.java      | 1126 +++++
 .../felix/framework/util/FelixConstants.java       |   68 +
 .../apache/felix/framework/util/ImmutableList.java |  146 +
 .../apache/felix/framework/util/ImmutableMap.java  |  129 +
 .../apache/felix/framework/util/ListenerInfo.java  |  141 +
 .../felix/framework/util/MapToDictionary.java      |   83 +
 .../org/apache/felix/framework/util/Mutex.java     |   44 +
 .../apache/felix/framework/util/SecureAction.java  | 1710 +++++++
 .../felix/framework/util/SecurityManagerEx.java    |   33 +
 .../felix/framework/util/ShrinkableCollection.java |  112 +
 .../apache/felix/framework/util/ShrinkableMap.java |   94 +
 .../felix/framework/util/StringComparator.java     |   48 +
 .../org/apache/felix/framework/util/StringMap.java |  217 +
 .../apache/felix/framework/util/ThreadGate.java    |   91 +
 .../java/org/apache/felix/framework/util/Util.java |  789 +++
 .../apache/felix/framework/util/VersionRange.java  |  159 +
 .../felix/framework/util/WeakZipFileFactory.java   |  746 +++
 .../util/manifestparser/ManifestParser.java        | 2056 ++++++++
 .../util/manifestparser/ParsedHeaderClause.java    |   40 +
 .../framework/util/manifestparser/R4Library.java   |  187 +
 .../util/manifestparser/R4LibraryClause.java       |  529 ++
 .../framework/wiring/BundleCapabilityImpl.java     |  206 +
 .../framework/wiring/BundleRequirementImpl.java    |  113 +
 .../felix/framework/wiring/BundleWireImpl.java     |   79 +
 .../java/org/osgi/framework/AdaptPermission.java   |  613 +++
 .../java/org/osgi/framework/AdminPermission.java   | 1004 ++++
 .../org/osgi/framework/AllServiceListener.java     |   60 +
 src/main/java/org/osgi/framework/Bundle.java       | 1243 +++++
 .../java/org/osgi/framework/BundleActivator.java   |   86 +
 .../java/org/osgi/framework/BundleContext.java     |  864 ++++
 src/main/java/org/osgi/framework/BundleEvent.java  |  235 +
 .../java/org/osgi/framework/BundleException.java   |  235 +
 .../java/org/osgi/framework/BundleListener.java    |   47 +
 .../java/org/osgi/framework/BundlePermission.java  |  584 +++
 .../java/org/osgi/framework/BundleReference.java   |   36 +
 .../org/osgi/framework/CapabilityPermission.java   |  775 +++
 src/main/java/org/osgi/framework/Configurable.java |   50 +
 src/main/java/org/osgi/framework/Constants.java    | 1703 +++++++
 src/main/java/org/osgi/framework/Filter.java       |  137 +
 .../java/org/osgi/framework/FrameworkEvent.java    |  244 +
 .../java/org/osgi/framework/FrameworkListener.java |   48 +
 .../java/org/osgi/framework/FrameworkUtil.java     | 2111 ++++++++
 .../org/osgi/framework/InvalidSyntaxException.java |  120 +
 .../java/org/osgi/framework/PackagePermission.java |  777 +++
 src/main/java/org/osgi/framework/ServiceEvent.java |  143 +
 .../java/org/osgi/framework/ServiceException.java  |  131 +
 .../java/org/osgi/framework/ServiceFactory.java    |  114 +
 .../java/org/osgi/framework/ServiceListener.java   |   62 +
 .../java/org/osgi/framework/ServicePermission.java |  910 ++++
 .../java/org/osgi/framework/ServiceReference.java  |  182 +
 .../org/osgi/framework/ServiceRegistration.java    |  109 +
 .../java/org/osgi/framework/SignerProperty.java    |  110 +
 .../osgi/framework/SynchronousBundleListener.java  |   65 +
 .../osgi/framework/UnfilteredServiceListener.java  |   73 +
 src/main/java/org/osgi/framework/Version.java      |  398 ++
 src/main/java/org/osgi/framework/VersionRange.java |  509 ++
 .../osgi/framework/hooks/bundle/CollisionHook.java |   85 +
 .../org/osgi/framework/hooks/bundle/EventHook.java |   61 +
 .../org/osgi/framework/hooks/bundle/FindHook.java  |   63 +
 .../framework/hooks/resolver/ResolverHook.java     |  207 +
 .../hooks/resolver/ResolverHookFactory.java        |  104 +
 .../osgi/framework/hooks/service/EventHook.java    |   53 +
 .../framework/hooks/service/EventListenerHook.java |   59 +
 .../org/osgi/framework/hooks/service/FindHook.java |   60 +
 .../osgi/framework/hooks/service/ListenerHook.java |  129 +
 .../framework/hooks/weaving/WeavingException.java  |   51 +
 .../osgi/framework/hooks/weaving/WeavingHook.java  |   63 +
 .../osgi/framework/hooks/weaving/WovenClass.java   |  157 +
 .../java/org/osgi/framework/launch/Framework.java  |  360 ++
 .../osgi/framework/launch/FrameworkFactory.java    |   72 +
 .../namespace/AbstractWiringNamespace.java         |   56 +
 .../osgi/framework/namespace/BundleNamespace.java  |  153 +
 .../namespace/ExecutionEnvironmentNamespace.java   |   55 +
 .../osgi/framework/namespace/HostNamespace.java    |  174 +
 .../framework/namespace/IdentityNamespace.java     |  162 +
 .../osgi/framework/namespace/PackageNamespace.java |  112 +
 .../framework/startlevel/BundleStartLevel.java     |  106 +
 .../framework/startlevel/FrameworkStartLevel.java  |  161 +
 .../osgi/framework/wiring/BundleCapability.java    |   80 +
 .../osgi/framework/wiring/BundleRequirement.java   |   96 +
 .../org/osgi/framework/wiring/BundleRevision.java  |  304 ++
 .../org/osgi/framework/wiring/BundleRevisions.java |   66 +
 .../java/org/osgi/framework/wiring/BundleWire.java |  108 +
 .../org/osgi/framework/wiring/BundleWiring.java    |  502 ++
 .../org/osgi/framework/wiring/FrameworkWiring.java |  174 +
 src/main/java/org/osgi/resource/Capability.java    |   86 +
 src/main/java/org/osgi/resource/Namespace.java     |  154 +
 src/main/java/org/osgi/resource/Requirement.java   |   90 +
 src/main/java/org/osgi/resource/Resource.java      |   82 +
 src/main/java/org/osgi/resource/Wire.java          |   87 +
 src/main/java/org/osgi/resource/Wiring.java        |  144 +
 .../osgi/service/packageadmin/ExportedPackage.java |  113 +
 .../osgi/service/packageadmin/PackageAdmin.java    |  298 ++
 .../osgi/service/packageadmin/RequiredBundle.java  |  100 +
 .../org/osgi/service/startlevel/StartLevel.java    |  276 ++
 .../url/AbstractURLStreamHandlerService.java       |  143 +
 .../java/org/osgi/service/url/URLConstants.java    |   43 +
 .../osgi/service/url/URLStreamHandlerService.java  |   89 +
 .../osgi/service/url/URLStreamHandlerSetter.java   |   49 +
 .../org/osgi/util/tracker/AbstractTracked.java     |  466 ++
 .../java/org/osgi/util/tracker/BundleTracker.java  |  494 ++
 .../osgi/util/tracker/BundleTrackerCustomizer.java |  100 +
 .../java/org/osgi/util/tracker/ServiceTracker.java |  975 ++++
 .../util/tracker/ServiceTrackerCustomizer.java     |   93 +
 .../org.osgi.framework.launch.FrameworkFactory     |    1 +
 src/main/resources/default.properties              |  794 +++
 .../org/apache/felix/framework/Felix.properties    |    1 +
 .../org/osgi/framework/hooks/bundle/packageinfo    |    1 +
 .../org/osgi/framework/hooks/resolver/packageinfo  |    1 +
 .../org/osgi/framework/hooks/service/packageinfo   |    1 +
 .../org/osgi/framework/hooks/weaving/packageinfo   |    1 +
 .../org/osgi/framework/launch/packageinfo          |    1 +
 .../org/osgi/framework/namespace/packageinfo       |    1 +
 src/main/resources/org/osgi/framework/packageinfo  |    1 +
 .../org/osgi/framework/startlevel/packageinfo      |    1 +
 .../org/osgi/framework/wiring/packageinfo          |    1 +
 src/main/resources/org/osgi/resource/packageinfo   |    1 +
 .../org/osgi/service/packageadmin/packageinfo      |    1 +
 .../org/osgi/service/startlevel/packageinfo        |    1 +
 .../resources/org/osgi/service/url/packageinfo     |    1 +
 .../resources/org/osgi/util/tracker/packageinfo    |    1 +
 .../org/apache/felix/framework/BootLoaderTest.java |  119 +
 .../apache/felix/framework/CollisionHookTest.java  |  297 ++
 .../org/apache/felix/framework/FilterTest.java     |   46 +
 .../framework/RequirementsCapabilitiesTest.java    |  267 +
 .../felix/framework/ServiceRegistryTest.java       |  334 ++
 .../felix/framework/StartStopBundleTest.java       |  171 +
 .../apache/felix/framework/URLHandlersTest.java    |  385 ++
 .../framework/capabilityset/SimpleFilterTest.java  |   86 +
 .../felix/framework/util/EventDispatcherTest.java  |  171 +
 .../org/apache/felix/framework/util/UtilTest.java  |  112 +
 .../felix/framework/util/WeakZipFileTest.java      |   97 +
 .../util/manifestparser/ManifestParserTest.java    |   88 +
 201 files changed, 63816 insertions(+)

diff --git a/DEPENDENCIES b/DEPENDENCIES
new file mode 100644
index 0000000..03546b3
--- /dev/null
+++ b/DEPENDENCIES
@@ -0,0 +1,33 @@
+// ------------------------------------------------------------------
+// Transitive dependencies of this project determined from the
+// maven pom organized by organization.
+// ------------------------------------------------------------------
+
+Apache Felix Framework
+
+
+
+
+
+Copyright 2013 The Apache Software Foundation
+
+This software was developed at the Apache Software Foundation
+(http://www.apache.org) and may have dependencies on other
+Apache software licensed under Apache License 2.0.
+
+I. Included Third-Party Software
+
+This product includes software developed at
+The OSGi Alliance (http://www.osgi.org/).
+Copyright (c) OSGi Alliance (2000, 2012).
+Licensed under the Apache License 2.0.
+
+II. Used Third-Party Software
+
+This product uses software developed at
+The OSGi Alliance (http://www.osgi.org/).
+Copyright (c) OSGi Alliance (2000, 2012).
+Licensed under the Apache License 2.0.
+
+III. License Summary
+- Apache License 2.0
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..f335cc5
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,404 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/NOTICE b/NOTICE
new file mode 100644
index 0000000..61635c3
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1,12 @@
+
+Apache Felix Framework
+Copyright 2006-2014 The Apache Software Foundation
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+
+
+This product includes software developed at
+The OSGi Alliance (http://www.osgi.org/).
+Copyright (c) OSGi Alliance (2000, 2012).
+Licensed under the Apache License 2.0.
diff --git a/doc/README.txt b/doc/README.txt
new file mode 100644
index 0000000..5072242
--- /dev/null
+++ b/doc/README.txt
@@ -0,0 +1,6 @@
+For documentation on how to use, launch, and/or embed the Felix framework,
+please refer to the documentation accompanying the full Felix installation
+or the "main" Felix subproject. Documentation is also available from our
+web site at:
+
+    http://cwiki.apache.org/FELIX/documentation.html
diff --git a/doc/changelog.txt b/doc/changelog.txt
new file mode 100644
index 0000000..72f9425
--- /dev/null
+++ b/doc/changelog.txt
@@ -0,0 +1,910 @@
+Changes from 4.2.1 to 4.4.0
+---------------------------
+** Bug
+    * [FELIX-1131] - ServiceReference.isAssignableTo fails when using a factory that can not see the exported class and the bundle exporting the service does not have a direct wire to this class
+    * [FELIX-3992] - Classloader access outside of a privileged block
+    * [FELIX-4190] - The framework should not hold any lock while calling ServiceFactory#unget
+    * [FELIX-4283] - actually throw exception if method not found
+    * [FELIX-4331] - [Core R5] Support BundleContext and Framework adaptations
+    * [FELIX-4353] - [Core R5] BundleWiringTests OSGi CT test failures
+    * [FELIX-4354] - [Core R5] ResolverHookTests OSGi CT test failures
+    * [FELIX-4355] - [Core R5] org.osgi.test.cases.framework.launch OSGi CT test failure
+    * [FELIX-4365] - Input stream not properly closed on org.apache.felix.framework.Felix.getFrameworkVersion
+    * [FELIX-4388] - Initial start level not considered when bundle.start() is called
+    * [FELIX-4462] - Classloader deadlock in Java6 between two bundles
+
+** Improvement
+    * [FELIX-4128] - [Core R5] Provide an OSGi R5 compliant framework implementation
+    * [FELIX-4284] - remove dead allocation
+
+** Task
+    * [FELIX-4080] - [Core R5] Add support for org.osgi.framework.UnfilteredServiceListener
+    * [FELIX-4082] - [Core R5] Update org.osgi.framework.bsnversion framework property
+    * [FELIX-4083] - [Core R5] Support for valueOf when evaluating a Filter
+    * [FELIX-4084] - [Core R5] Enhance Bundle.adapt() to provider AccessControlContext.
+    * [FELIX-4085] - [Core R5] Implement updates to the Bundle Hook Specification
+    * [FELIX-4324] - [Core R5] Implement osgi.identity namespace for fragments
+
+** Wish
+    * [FELIX-3868] - Adding osgi.identity namespace to bundles (resources)
+
+Changes from 4.2.0 to 4.2.1
+---------------------------
+** Bug
+    * [FELIX-3411] - The implementation of org.osgi.service.startlevel.StartLevel#setStartLevel(int) does not follow the spec
+    * [FELIX-3893] - Bundle in cache doesn't pass security check anymore.
+    * [FELIX-3907] - NullPointerException in BundleWiringImpl when m_disposed is true.
+    * [FELIX-3939] - IllegalArgumentException from ClassLoader.definePackage
+    * [FELIX-3950] - Permission check being done even when security manager is absent
+    * [FELIX-3957] - Error events can be thrown when refreshing fragment bundles
+
+** Improvement
+    * [FELIX-3961] - Add a fallback to the default java security policy if no security provider is present
+
+Changes from 4.0.3 to 4.2.0
+---------------------------
+** Bug
+    * [FELIX-2780] - Extension bundle implementation relies on urlhandlers  service to start extension bundles
+    * [FELIX-3160] - NPE in BundleRevisionImpl.close() when uninstalling a bundle
+    * [FELIX-3242] - Concurrent modification problem in StatefulResolver$ResolverStateImpl.getCandidates
+    * [FELIX-3273] - Possible exception when accessing headers
+    * [FELIX-3306] - Lazy activation of bundles is not always working as expected
+    * [FELIX-3343] - Installing an fragment bundle without Bundle-ManifestVersion: 2 causes NPE in resolver
+    * [FELIX-3348] - StartLevel thread may terminate on uncaught exception
+    * [FELIX-3353] - The implementation of org.osgi.service.packageadmin.PackageAdmin#getExportedPackages(Bundle), does not follow the spec.
+    * [FELIX-3367] - getClassloader permission
+    * [FELIX-3397] - NPE when trying to resolve invalid fragments
+    * [FELIX-3411] - The implementation of org.osgi.service.startlevel.StartLevel#setStartLevel(int) does not follow the spec
+    * [FELIX-3413] - NPE and thread blocked in org.osgi.service.packageadmin.PackageAdmin#refreshPackages(Bundle[])
+    * [FELIX-3455] - Framework JARs for JDK 7
+    * [FELIX-3465] - Multi root resolve operations may cause duplicate blame chains to be created
+    * [FELIX-3618] - [Framework] Should not allow bundles to use generic cap/req headers for osgi.wiring.* namespaces
+    * [FELIX-3626] - Issue of felix on android 4.1
+    * [FELIX-3632] - [Framework] Parsing of delimited strings in manifest parser collapses all consecutive escapes
+    * [FELIX-3670] - PackageAdmin.isBundleType throws NPE for uninstalled bundle
+    * [FELIX-3713] - Bundle.start() returns without starting the bundle
+    * [FELIX-3743] - Potential endless loop setting the active framework startlevel
+    * [FELIX-3753] - Felix crashes when embedded within Felix
+    * [FELIX-3761] - When a bundle registers a service, the bundle lock is obtained without any real purpose
+    * [FELIX-3766] - Slightly invalid logic for pre-checking dynamic imports which cause the framework the grab the lock with no real need
+    * [FELIX-3803] - Bundle#getResource always try to resolve the bundle
+    * [FELIX-3824] - Possible InvalidStateException thrown while unregistering bundle services
+    * [FELIX-3840] - problem with URLHandlers when running 2 frameworks in one jvm in separate class loaders
+    * [FELIX-3844] - Native bundles cannot be installed on Windows 8 and Windows Server 2012
+    * [FELIX-3852] - InstallBundle throws ClassCastException: java.util.jar.Attributes$Name cannot be cast to java.lang.String
+    * [FELIX-3887] - ClassCastException during resolution of Require-Bundle: system.bundle
+
+** Improvement
+    * [FELIX-3344] - [Framework] Filter parsing treats ** as invalid syntax
+    * [FELIX-3372] - Add the ability to handle a blank on the property "org.osgi.framework.system.packages.extra"
+    * [FELIX-3394] - [Framework] Refactor internal resolver APIs to better align with upcoming OSGi resolver spec
+    * [FELIX-3447] - Optimize read only collections by using a specific class and not a wrapper which is slower
+    * [FELIX-3553] - Use of parallel class loading capability of JDK7
+    * [FELIX-3609] - Small optimizations
+    * [FELIX-3611] - Bundle certificates are not added to the CodeSource when building the BundleProtectionDomain
+    * [FELIX-3807] - Refreshing bundles should first grab all the bundle locks to avoid concurrent modifications of those bundles
+
+** New Feature
+    * [FELIX-3504] - [Framework] Move to OSGi R5 packages
+
+** Task
+    * [FELIX-3786] - Create system package definintions for Java 8
+
+
+Changes from 4.0.2 to 4.0.3
+---------------------------
+
+** Bug
+    * [FELIX-3003] - NPE in ResolverImpl.permutateIfNeeded
+    * [FELIX-3296] - URLHandlers caches null as values for common protocols
+    * [FELIX-3302] - Adapt the URLHandlers for the 4.0 refactoring
+    * [FELIX-3363] - Native bundles cannot be installed on Windows Server 2008 r2 with the tag win32
+    * [FELIX-3393] - Possible deadlock with reentrant calls
+    * [FELIX-3572] - [Framework] Resolver is not checking package space consistency for dynamic imports
+
+** Improvement
+    * [FELIX-3262] - Startup delay due to URLHandlersBundleStreamHandler
+
+Changes from 4.0.1 to 4.0.2
+---------------------------
+
+** Bug
+    * [FELIX-3178] - NPE in ResolverImpl
+    * [FELIX-3194] - [Framework] Manifest parser is not correctly handling escapes
+    * [FELIX-3205] - Error resolving system.bundle dependencies
+    * [FELIX-3207] - Improper handling of nulls and substring matching at CapabilitySet/SimpleFilter
+    * [FELIX-3211] - org.apache.felix.framework.cache.BundleCache.deleteDirectoryTreeRecursive throws NPE
+    * [FELIX-3220] - Multiple ClassCastException(s) when invoking OSGi Service-Hooks (EventHook, FindHook)
+
+** Improvement
+    * [FELIX-3166] - Make Felix compile within Eclipse
+
+** New Feature
+    * [FELIX-3156] - Implement Bundle::getDataFile and Bundle::compareTo
+
+Changes from 4.0.0 to 4.0.1
+---------------------------
+
+** Bug
+    * [FELIX-3137] - [Framework] Capabilities from resolved singleton bundles are not indexed correctly
+    * [FELIX-3150] - Filter parameter is ignored at getServiceReferences
+    * [FELIX-3153] - [Framework] refreshPackages() should stop bundles in one pass and refresh them in a second pass
+
+** Improvement
+    * [FELIX-3138] - [Framework] Refactor some unnecessary code in the resolver
+    * [FELIX-3141] - [Framework] Framework should filter removal pending fragments not the resolver
+
+Changes from 3.2.2 to 4.0.0
+---------------------------
+
+** Bug
+    * [FELIX-2762] - Substring parser incorrectly disallowing use of parentheses characters
+    * [FELIX-2990] - [Framework] Bug introduced into handling of native libraries during a fresh
+    * [FELIX-3015] - [Framework] Provide org.osgi.util.tracker version 1.5
+    * [FELIX-3033] - [Framework] Service registry hooks are not correctly filtering bundle contexts for event hook
+    * [FELIX-3038] - Framework does not offer J2SE-1.2 Execution Environment
+    * [FELIX-3043] - [Framework] Resolver is not correctly resolving fragments in all cases
+    * [FELIX-3059] - Reexported packages are not calculate correctly leading to resolver and class loading bugs
+    * [FELIX-3062] - [Framework] Resolver gets into infinite loop in the face of a require-bundle cycle
+    * [FELIX-3082] - [Framework] Bundles not allowed to add listeners while in STOPPING state
+    * [FELIX-3085] - Importing/exporting "." should not be allowed
+    * [FELIX-3096] - Could not add FrameworkListener from ServiceListener
+
+** Improvement
+    * [FELIX-2467] - The framework extensions should be returned when invoking the method PackageAdmin#getFragments with the system bundle
+    * [FELIX-2572] - JRE system packages should include "uses" constraints
+    * [FELIX-2950] - [Framework] Adopt OSGi R4.3 API as framework internal API
+    * [FELIX-2998] - [Framework] OSGi R4.3 changed filter handling of exceptions when calling equals()/compare() to return false
+    * [FELIX-3000] - Move sending service registered event out of bundle lock
+    * [FELIX-3071] - [Framework] It should be possible to limit number of open JAR files
+    * [FELIX-3125] - [Framework] Use single file per bundle in bundle cache by default
+
+** New Feature
+    * [FELIX-2959] - [Framework] Implement OSGi R4.3 class loader byte-code weaving hook
+    * [FELIX-2969] - [Framework] Implement OSGi R4.3 framework wiring object
+    * [FELIX-2973] - [Framework] Implement OSGi R4.3 generic capabilities and requirements
+    * [FELIX-2975] - [Framework] Implement OSGi R4.3 framework start level object
+    * [FELIX-2986] - [Framework] Implement OSGi R4.3 resolver hooks
+    * [FELIX-2999] - [Framework] OSGi R4.3 now specifies that number types should be trimmed when evaluating filters
+    * [FELIX-3032] - [Framework] Implement OSGi R4.3 bundle hooks
+    * [FELIX-3052] - [Framework] Implement OSGi R4.3 system bundle generic capabilities
+    * [FELIX-3056] - [Framework] Implement OSGi R4.3 event listener service registry hook
+    * [FELIX-3122] - [Framework] Implement OSGi R4.3 framework UUID
+    * [FELIX-3124] - [Framework] Implement OSGi R4.3 property to allow installing bundles with the same BSN and version
+
+** Task
+    * [FELIX-3110] - [Framework] Implement 4.3 security checks
+
+
+Changes from 3.2.1 to 3.2.2
+---------------------------
+
+** Bug
+    * [FELIX-2935] - Bundle.getEntryPaths and findEntries are returning META-INF/ multiple times
+    * [FELIX-2941] - Felix Framework 3.2.0 and Framework Security 1.4.2 fails OSGI Conditional Permission Admin CT when running on IBM JVM 5 and 6.
+    * [FELIX-2942] - Bundles with higher start level get activated even before framework reaches that start level
+
+
+Changes from 3.2.0 to 3.2.1
+---------------------------
+
+* Bug
+    * [FELIX-2901] - [Framework] NPE from a host bundle during framework shutdown
+    * [FELIX-2917] - Constructor of SecurityExceptino() which is incompatible to 1.4 is used  
+    * [FELIX-2924] - bundle stop hangs for http-2.2.0 in felix-3.2.0
+
+** Improvement
+    * [FELIX-2909] - [Framework] Resolver could be more efficient if it detected if fragments were present
+
+
+Changes from 3.0.9 to 3.2.0
+---------------------------
+
+* Bug
+    * [FELIX-1816] - deadlock on SystemBundle.stop()
+    * [FELIX-2741] - NPE in ResolverImpl.calculatePackageSpaces
+    * [FELIX-2748] - Possible synchronization issue with BundleContext.getBundle()
+    * [FELIX-2877] - java6 update 24 breaks felix when used inside webstart
+
+
+** Improvement
+    * [FELIX-2858] - [Framework] Modify resolver to be self-contained with respect to fragment handling
+    * [FELIX-2859] - [Framework] Modify resolver to be self-contained with respect to singleton handling
+    * [FELIX-2864] - [Framework] Add package profile for JDK7 to default properties
+
+Changes from 3.0.8 to 3.0.9
+---------------------------
+
+** Bug
+    * [FELIX-1581] - Bundle#update, Bundle#uninstall and Bundle#stop should wait for a (configurable) timeout before throwing an exception if the bundle is starting or stopping
+    * [FELIX-2784] - When the framework try to grab the bundle lock, it ignores thread interruption
+    * [FELIX-2822] - [Framework] System bundle module's state not reset when framework restarted leading to NPE
+    * [FELIX-2832] - [Framework] It should not be possible to open an URLConnection to "/" for a bundle URL
+    * [FELIX-2840] - [Framework] Uninstalling an uninstalled bundle throws NoSuchElementException
+    * [FELIX-2849] - PackageAdmin is inconsistent between getFragments and getHosts
+    * [FELIX-2850] - PackageAdmin return fragments / hosts even if the host isn't resolved
+    * [FELIX-2851] - Resolution problems after a fragment can't be resolved
+
+** Improvement
+    * [FELIX-2841] - [Framework] Improve resolve exception messages
+
+Changes from 3.0.7 to 3.0.8
+---------------------------
+
+** Bug
+    * [FELIX-2749] - Boolean.parseBoolean() newly defined in Java5 should not be used.
+    * [FELIX-2789] - Native library matching is not correctly checking additional extensions
+    * [FELIX-2800] - Logging will throw an NPE if no bundle is specified
+    * [FELIX-2802] - A failed update doesn't rollback properly
+    * [FELIX-2805] - Cache PackageAdmin.getBundle() result for system bundle classes
+
+Changes from 3.0.6 to 3.0.7
+---------------------------
+
+** Bug
+    * [FELIX-2456] - Framework no longer fires UNRESOLVED event when a bundle is explicitly refreshed
+    * [FELIX-2693] - [Framework] Service registry should throw a ServiceException for factories resulting in a cycle
+    * [FELIX-2700] - Framework uses java.nio package which is not supported by CDC VM
+    * [FELIX-2710] - [Framework] For exploded bundles Bundle.getEntry("a/b.jar/")==Bundle.getEntry("a/b.jar")
+    * [FELIX-2717] - [Framework] Resolver treats multiple exports of same package as conflicting for fragment imports
+    * [FELIX-2725] - [Framework] Resolver is not correctly calculating exported packages for already resolved modules
+    * [FELIX-2728] - JarRevision does not close URLConnections which are instances of HttpURLConnection
+    * [FELIX-2736] - [Framework] Resolver is not correctly verifying package space consistency for dynamic imports
+    * [FELIX-2738] - [Framework] DirectoryContent does not handle security correctly
+
+** Improvement
+    * [FELIX-2703] - [Framework] Include OSGi/Minimum EEs in default properties
+    * [FELIX-2721] - [Framework] Implement custom manifest parser and avoid JarFile
+    * [FELIX-2737] - [Framework] Optimize resolver algorithm by not re-calculating uses constraints for resolved modules
+
+Changes from 3.0.5 to 3.0.6
+---------------------------
+
+** Bug
+    * [FELIX-2670] - [Framework] Implicit boot delegation doesn't delegate for external code in all cases
+    * [FELIX-2683] - [Framework] Bundle last modified time is not persisted on deployment
+
+** Improvement
+    * [FELIX-2560] - Bundle URLs do not survive refreshes
+
+** New Feature
+    * [FELIX-2646] - [Framework] Locking could be used to prevent concurrent access to a single bundle cache
+
+Changes from 3.0.4 to 3.0.5
+---------------------------
+
+** Bug
+    * [FELIX-2653] - LinkageError caused by duplicate class definition during implicit boot delegation
+
+** Improvement
+    * [FELIX-2645] - Add a (hidden) way to retrieve a local URL for a given bundle URL
+    * [FELIX-2654] - [Framework] Modify bundle cache to use a single file per bundle state
+
+Changes from 3.0.3 to 3.0.4
+---------------------------
+
+** Bug
+    * [FELIX-2584] - No FrameworkEvent.ERROR on unchecked exception in event listener
+    * [FELIX-2629] - [Framework] Module class loader should return an empty enumeration for getResources()
+
+** Improvement
+    * [FELIX-2619] - [Framework] Bundle cache is rechecking nonexistent files again and again
+    * [FELIX-2626] - [Framework] Bundle cache is rewriting some files when restarting bundles
+
+Changes from 3.0.2 to 3.0.3
+---------------------------
+
+** Bug
+    * [FELIX-2548] - Resolver should use case sensitive indexing for capabilities
+    * [FELIX-2569] - Felix bundle classloader always delegates to parent loader in getResources()
+    * [FELIX-2589] - SecurityException "SecurityManager already installed" is thrown when calling Framework.init() multiple times with FRAMEWORK_SECURITY set
+    * [FELIX-2598] - Hang in Felix: thread owing a bundle lock waits for ever to lock it again
+    * [FELIX-2599] - When specifying the packages exported by the system bundle, attributes and directives on packages do not show up in the system bundle headers
+
+** Improvement
+    * [FELIX-1022] - Classloader Exceptions should be more informative
+    * [FELIX-2549] - Fix some synchronization issues in content handling
+    * [FELIX-2555] - Log messages should contain the bundle id, when available
+    * [FELIX-2597] - Deadlock during delivery of resolved event
+
+Changes from 3.0.1 to 3.0.2
+---------------------------
+
+** Bug
+    * [FELIX-2421] - Implicit bootdelegation doesn't ignore classloaders if they are inner classes
+    * [FELIX-2437] - Deadlock on refrsh Import and refresh
+    * [FELIX-2451] - Felix uses System.out to print debug messages related to resolver...
+    * [FELIX-2459] - Wrong error message on a missing package
+    * [FELIX-2466] - Unknown attributes are not stripped from Fragment-Host header
+    * [FELIX-2473] - Subtring matching is incorreclty matching against an empty string
+    * [FELIX-2479] - Required bundles not correctly re-exporting their substitutable exports
+
+** Improvement
+    * [FELIX-2528] - Potential performance issue in resolver when uses constraint conflict is detected
+    * [FELIX-2529] - In some scenarios the resolver will not backtrack on imported package decisions
+
+Changes from 3.0.0 to 3.0.1
+---------------------------
+
+** Bug
+    * [FELIX-2401] - NPE in org.apache.felix.framework.FilterImpl.DictionaryCapability.getAttribute(String)
+
+Changes from 2.0.5 to 3.0.0
+---------------------------
+** Sub-task
+    * [FELIX-2036] - Improve resolver's generic capability/requirement model
+    * [FELIX-2037] - Improve resolver performance by making solution space searching smarter
+
+** Bug
+    * [FELIX-995] - JRE packages are exported with wrong version
+    * [FELIX-1967] - Freeze finding consistent class space
+    * [FELIX-2080] - Updating bundles when debugging switched on might result in a deadlock with 100% CPU usage
+    * [FELIX-2150] - URLStreamHandlerProxy.setURL may not set query component correctly
+    * [FELIX-2172] - Extremely long resolve stage when running CXF-DOSGi system test
+    * [FELIX-2177] - Fragment bundles not loaded after second start when using autodeploy
+    * [FELIX-2271] - CLONE -NPE "name can't be null" when trying to install a bundle in Felix 2.0.3
+    * [FELIX-2273] - getClassLoader-permission required accessing classes from dynamically loaded class
+    * [FELIX-2281] - Bundle id order affects fragment resolution success
+    * [FELIX-2317] - Possible NPE for jars with null Manifest
+    * [FELIX-2321] - BundleException type should be properly set when installing a duplicate bundle
+    * [FELIX-2332] - Lots of contention on ExtensionManager.openConnection(URL)
+    * [FELIX-2335] - Bundle.loadClass() for system bundle doesn't obey boot delegation
+    * [FELIX-2356] - extension bundle cannot load class from embed jar
+    * [FELIX-2383] - Bundles are restarted during start level change
+    * [FELIX-2392] - Felix framework uses a Java5 method
+
+** Improvement
+    * [FELIX-1210] - Allow jars with missing intermediate entries to be handled as if they were present in Bundle.getEntryPaths
+    * [FELIX-1797] - Customizable Framework startup message
+    * [FELIX-2035] - Reimplement framework resolver
+    * [FELIX-2039] - Reimplement standard OSGi LDAP filter to use new filter solution for resolver
+    * [FELIX-2040] - Modify framework service registry to use resolver's new capability/requirement model
+    * [FELIX-2041] - Look into using generics in framework code
+    * [FELIX-2042] - Use Gogo as the default shell for the framework distribution
+    * [FELIX-2324] - Support execution environment so that OBR works properly
+    * [FELIX-2336] - Variable substitution in configuration files should ignore mismatched delimiters
+
+Changes from 2.0.4 to 2.0.5
+---------------------------
+** Bug
+    * [FELIX-1753] - The start level should check that the bundle still exists before starting it to avoid an ugly exception
+    * [FELIX-2087] - NPE "name can't be null" when trying to install a bundle in Felix 2.0.3
+    * [FELIX-2107] - Bundle.findEntries() matches '*<filePattern>' instead of '<filePattern>'
+    * [FELIX-2195] - Using URLDecoder.decode on locations is wrong
+    * [FELIX-2222] - Failure to reinstall a cached bundle will corrupt the bundle cache
+
+Changes from 2.0.3 to 2.0.4
+---------------------------
+
+** Bug
+    * [FELIX-2056] - URLHandlersStreamHandler not getting handlers from frameworks that are inside a different classloader
+    * [FELIX-2067] - Fragment bundle ignored silently when the host Bundle-SymbolicName equals import package name
+    * [FELIX-2073] - The System Bundle is not providing WS Addressing
+
+** Improvement
+    * [FELIX-2071] - Missing checks inside ModuleImpl (in Framework) which causes exceptions
+
+Changes from 2.0.2 to 2.0.3
+---------------------------
+
+** Bug
+    * [FELIX-1838] - PackageAdmin.getExportedPackages() duplicates output for packages with different version
+    * [FELIX-1867] - ModuleImpl diagnoseClassLoadError throw NullPointerException for empty package name in debug mode
+    * [FELIX-1917] - A few minor bugs in the framework found while embedding Felix
+    * [FELIX-1919] - Fragment bundle cannot be linked to its host
+    * [FELIX-1920] - RequiredBundle.getRequiringBundles() incorrectly calculates result
+    * [FELIX-1929] - getStartLevel() always reports requested start level, not active start level
+    * [FELIX-1982] - Documented but uninterpreted felix.cache.* properties
+    * [FELIX-1998] - Use UTF-8 when decoding reference location URLs
+    * [FELIX-2002] - Uninstalled fragments are not properly detached
+
+** Improvement
+    * [FELIX-37] - Implement security for bundle resource URLs
+    * [FELIX-325] - Factor out security checks from the framework/system bundle code
+    * [FELIX-1973] - Implement all required security checks
+
+** New Feature
+    * [FELIX-30] - Implement extension bundles
+    * [FELIX-1991] - Allow boot delegation class loader to be configurable per bundle
+
+
+Changes from 2.0.1 to 2.0.2
+---------------------------
+
+** Bug
+    * [FELIX-1754] - Usage of BundleContext.getServiceReferences results in failure to activate components
+    * [FELIX-1782] - Errors during start-up on gnu/classpath based VMs (jamvm, kaffe, cacao) and mika
+    * [FELIX-1795] - Bundle version ignored in Fragment-Host header
+    * [FELIX-1834] - java.io.IOException: No framework context found when embedding felix frameworks as bundles
+
+** Improvement
+    * [FELIX-1534] - Improve fragment merging
+    * [FELIX-1781] - Try to reduce object allocations/usage in resolver algorithm
+    * [FELIX-1783] - Eliminate contention on ServiceRegistry.getServiceReferences(String, Filter)
+
+Changes from 2.0.0 to 2.0.1
+---------------------------
+
+** Bug
+    * [FELIX-1565] - Deadlock UrlHandlers
+    * [FELIX-1573] - Occasional NPE in URLHandlersBundleStreamHandler
+    * [FELIX-1580] - Regression with native library handling
+    * [FELIX-1586] - Framework reports org.osgi.framework.version as 1.3
+    * [FELIX-1600] - ServiceReference.isAssignableTo() always returns true if requesting bundle has no wire
+    * [FELIX-1631] - Implicit bootdelegation causes hang on android
+    * [FELIX-1710] - Resolver still does not discard all partial results when a cyclical dependency fails
+    * [FELIX-1721] - Framework boot delegation has a bug due to extraneous code
+    * [FELIX-1731] - Native library extraction could be improved to help cases where there are dependencies among libraries
+
+** Improvement
+    * [FELIX-1619] - Change the default auto-deploy actions to be install and start only
+    * [FELIX-1625] - Refactor bundle cache to simplify management
+    * [FELIX-1679] - VersionRange class should use finals to be thread safe.
+    * [FELIX-1724] - Various module metadata should be cached
+
+** Task
+    * [FELIX-1617] - Modify framework, main, shell, shell.tui, and obr to depend on official OSGi JAR files
+
+Changes from 1.8.1 to 2.0.0
+---------------------------
+
+** Bug
+    * [FELIX-893] - Felix fails to start using J9 JVM
+    * [FELIX-905] - Felix needs an RFC 126 FindHook
+    * [FELIX-906] - Felix needs an RFC 126 EventHook
+    * [FELIX-1122] - Extension bundles are not being removed from the bundle list when uninstalled
+    * [FELIX-1123] - System bundle does still not correctly handle (export) package attributes
+    * [FELIX-1124] - ResourceNotFoundException too verbose
+    * [FELIX-1130] - Bundle.getHeaders() returns a Dictionary<java.util.jar.Attribute.Name,String>
+    * [FELIX-1138] - URL Handlers performance regression due to service lookups
+    * [FELIX-1170] - MemoryLeak when stopping and restarting Felix
+    * [FELIX-1187] - BundleContext.ungetService() should return false only if the usage count is zero when the method is invoked
+    * [FELIX-1197] - Bundle Fragments not resolved correctly
+    * [FELIX-1198] - config.properties still refers to old org.osgi.framework.startlevel property
+    * [FELIX-1247] - BundleEvent.UNRESOLVED should be fired during update/uninstall not refresh
+    * [FELIX-1249] - Bundle.findEntries() should search fragments as well as the bundle itself
+    * [FELIX-1254] - Bundle#findEntries does not return resources from fragments
+    * [FELIX-1271] - Improve manifest localization to handle special cases
+    * [FELIX-1272] - Need to special case getResource()/getResources()/loadClass() for fragment bundles
+    * [FELIX-1273] - Bundle.getResources() should return null for a non-existent resource
+    * [FELIX-1277] - Fix Service Hooks Tests failures in RFC 126 TCK
+    * [FELIX-1279] - Framework.waitForStop() does not obey timeout
+    * [FELIX-1280] - Package Admin - getExportedPackages must return null instead of an empty array
+    * [FELIX-1285] - SecureAction captures the calling context incorrectly
+    * [FELIX-1286] - Module class loader should use secure action instead of a privileged block
+    * [FELIX-1287] - System bundle operations from RFC-132 (e.g., init(), start(), stop(), waitForStop()) are using wrong lock object
+    * [FELIX-1288] - System bundle context should be null after stopping the framework
+    * [FELIX-1292] - PackageAdmin.getBundle(Class) should return null if the bundle associated with the passed in class is from another framework
+    * [FELIX-1293] - StringMap used for case insensitive properties does not respect ordering if case sensitivity is changed
+    * [FELIX-1295] - ServiceRegistry increments/decrements service use count after/before getService/ungetService() is called on ServiceFactory
+    * [FELIX-1371] - Automatic parent class loader delegation does not correctly filter calls to Bundle.loadClass()
+    * [FELIX-1397] - Required execution environment verification should happen at resolve time, not install time
+    * [FELIX-1400] - bootdelegation and dynamic-import-packages are accepting invalid patterns
+    * [FELIX-1401] - Manifest localization with fragments not handled correctly
+    * [FELIX-1422] - Resolver does not always discard partial results when a cyclically dependency fails
+    * [FELIX-1435] - Resolver does not always resolve a dynamic import to a fragment export
+    * [FELIX-1527] - R4.2 spec errata now specifies uninstalling a bundle must transition through INSTALLED on its way to UNINSTALLED
+    * [FELIX-1551] - Start level service must synchronously persist bundle start level changes
+    * [FELIX-1556] - Bundle.getResource/s is not able to find resources if the package is not alraedy wired when I use DynamicImport-Package: *
+
+** Improvement
+    * [FELIX-712] - Ability to disable automatic parent classloader delegation
+    * [FELIX-883] - JarContent logs and then swallows exceptions when reading from JAR file, should probably throw an exception
+    * [FELIX-1120] - Enable BundleCache customization
+    * [FELIX-1134] - Add support for native libraries in fragments
+    * [FELIX-1189] - Improve error message in main when there is an error processing auto-install/start bundles
+    * [FELIX-1246] - PackageAdmin.getBundle() is not implemented efficiently
+    * [FELIX-1260] - Make Bundle.findEntries() and Bundle.getEntryPaths() more thread safe
+    * [FELIX-1291] - Implement support for proper return type from Framework.waitForStop()
+    * [FELIX-1300] - Remove legacy bundle cache support when extracting embedded JAR files
+    * [FELIX-1360] - Improve native library matching algorithm (part 2)
+    * [FELIX-1404] - Use provided classes from OSGi R4.2 companion code
+    * [FELIX-1432] - Manifest parser doesn't return import package declarations in order of manifest
+    * [FELIX-1462] - Felix framework launcher should only use standard launching API
+
+** New Feature
+    * [FELIX-33] - Implement system bundle update
+    * [FELIX-749] - Add support for lazy activation of bundles
+    * [FELIX-1193] - Implement org.osgi.framework.bundle.parent from RFC 132
+    * [FELIX-1205] - Update to the latest OSGi R4.2 API
+    * [FELIX-1244] - Add support for ServiceEvent.MODIFIED_ENDMATCH
+    * [FELIX-1250] - Support service exceptions for service factories
+    * [FELIX-1289] - Support for FrameworkUtil.getBundle()
+    * [FELIX-1297] - Implement support for new org.osgi.framework.command.execpermission configuration property
+    * [FELIX-1298] - Implement support for new org.osgi.framework.library.extensions configuration property
+    * [FELIX-1446] - Framework launcher should automatically deploy bundles in bundle directory
+    * [FELIX-1478] - Add shutdown hook to launcher to cleanly shutdown the framework if the process is killed
+
+
+
+
+** Task
+    * [FELIX-1144] - The NOTICE file for Main subproject is not correctly copied into the source JAR
+
+
+** Test
+    * [FELIX-1208] - Need migrate the EventDispatcherTest to newer version of EasyMock
+ 
+Changes form 1.6.1 to 1.8.0
+---------------------------
+** Bug
+    * [FELIX-1034] - bootdelegation property seems to be matching more packages than desired
+    * [FELIX-1059] - DynamicImport-Package matches more packages than desired
+
+** Improvement
+    * [FELIX-1060] - URLHandlers doesn't support URLStreamHandler.openConnection(proxy,url) method
+
+** New Feature
+    * [FELIX-29] - Implement bundle fragments
+
+Changes from 1.6.0 to 1.6.1
+---------------------------
+** Bug
+    * [FELIX-1027] - deadlock with felix 1.6.0 ?
+    * [FELIX-1028] - NPE in configuration view when running webconsole with Equinox
+    * [FELIX-1033] - Exceptions when Felix is started with security manager
+    * [FELIX-1035] - deadlock observed while using fileinstall to monitor multiple directories
+    * [FELIX-1045] - Felix 1.6.0 fails with ClassCircularityError
+
+Changes from 1.4.1 to 1.6.0
+---------------------------
+
+** Bug
+    * [FELIX-737] - Resolver does not correctly discard export when module imports the same package (part 2)
+    * [FELIX-852] - Fragment support is currently limited to directly resolved hosts
+    * [FELIX-869] - SCR throws exception on invalidating a component with a reference to a service that was already unregistered
+    * [FELIX-891] - Bundle lock acquisition should not record lock until it verifies the bundle is lockable
+    * [FELIX-892] - ServiceReferenceImpl improper implementation of equals and hashcode
+    * [FELIX-893] - Felix fails to start using J9 JVM
+    * [FELIX-897] - Empty system package is exported when a semicolon is present in "extra" configuration
+    * [FELIX-902] - Empty bundle.state file produces NPE
+    * [FELIX-910] - Framework may ignore framework startlevel on startup
+    * [FELIX-911] - Potential deadlock between Bundle.stop() and BundleContext.registerService()
+    * [FELIX-915] - PackageAdmin returns null on getBundle(...) with system classes
+    * [FELIX-934] - Bootdelegation bug
+    * [FELIX-947] - Behaviour of startlevel comman doesn't adhere to spec
+    * [FELIX-948] - ServiceReferenceImpl.compareTo should handle other types than integer for service ranking
+    * [FELIX-951] - Deadlock in iPojo when stopping Felix
+    * [FELIX-952] - Exception thrown when unregistering services because the bundle is stopped
+    * [FELIX-953] - Bundle#loadClass sometimes return null instead of throwing a CNFE
+    * [FELIX-961] - 100% CPU looping inside uses calculation
+    * [FELIX-962] - Erroneous class loading delegation to the application launcher classloader in some cases
+    * [FELIX-969] - system bundle does not correctly handle (export) package attributes
+    * [FELIX-971] - Exception thrown in ServiceTracker at shutdown 
+    * [FELIX-978] - Resolver does not clean up properly on a failed recursive attempt to resolve
+    * [FELIX-1004] - Extensionmanager openConnection(URL) method should be public
+    * [FELIX-1005] - Strange list of imported packages returned by the package admin
+
+** Improvement
+    * [FELIX-681] - Modify daemon threads to catch all exceptions and log them to avoid premature thread death
+    * [FELIX-851] - Refactor the module abstraction layer to align more closely to OSGi concepts
+    * [FELIX-872] - JarContent swallows exception when opening manifest, it should log it
+    * [FELIX-883] - JarContent logs and then swallows exceptions when reading from JAR file, should probably throw an exception
+    * [FELIX-894] - Loosen locking when starting/stopping/uninstalling a bundle by firing event without holding a lock
+    * [FELIX-908] - Unsynchronize access to bundle state inside BundleInfo by making the variable volatile
+
+Changes from 1.4.0 to 1.4.1
+---------------------------
+
+* [2008-12-19] Refactor the URLHandlers implementation to fix a possible 
+  Linkage-Error when running embedded inside tomcat. Furthermore, make it possible
+  to dynamically set a SecurityManager, change the url we add to the framework
+  classloader to fix issues with rmi and make sure we restore the previous URLStreamHandlerFactory
+  after stopping. (FELIX-842, FELIX-837, FELIX-844, FELIX-827)
+* [2008-12-19] Add missing javax.security.cert in JRE 1.4 and 1.5. (FELIX-854)
+* [2008-12-19] Improve framework internal locking protocol.
+* [2008-12-18] Fixed an issue where bundles with a non-existing native library
+  would not be removed correctly. (FELIX-835)
+* [2008-11-21] Throw an exception when there is an attempt to start or stop a 
+  fragment, as per the spec. (FELIX-820)
+* [2008-11-20] Fixed a bug during shutdown where uninstalled fragments are not
+  properly closed. (FELIX-819)
+* [2008-11-14] Added partial support for new service registry hooks as proposed 
+  for OSGi R4.2; currently, only listener hooks are supported. (FELIX-804)
+* [2008-11-08] Fixed Felix' delegation hack so that it correctly delegates to 
+  the parent class loader for resources as appropriate; previously it was always 
+  delegating for class loading, which was incorrect. (FELIX-808)
+
+Changes from 1.2.2 to 1.4.0
+---------------------------
+
+* [2008-11-05] URLStreamHandlerService and ContentHandlerService override
+  built-in handlers and make it possible to use URLHandlers when extending the 
+  Felix class. (FELIX-756, FELIX-800)
+* [2008-11-04] Implement ServiceReference.compareTo() method. (FELIX-799)
+* [2008-10-31] Fix some visibility issues in the LDAP operators which could
+  result in incorrect toString values. (FELIX-765)
+* [2008-10-31] Fixed spec compliance issues around getting a service reference 
+  from an invalid registration and throwing an exception when there are
+  duplicate property keys. (FELIX-798)
+* [2008-10-21] Fixed an issue with extension bundles which would be installed
+  as fragments and fail to install extension bundles if they have incomplete 
+  metadata. (FELIX-770)
+* [2008-10-21] Fix a possible deadlock in URLHandlers. (FELIX-748)
+* [2008-10-16] Modified framework to have default values for the system
+  packages property, which also required modifying main to no longer expect to
+  set it. Now it is possible to start Felix with no configuration properties.
+  (FELIX-753)
+* [2008-10-16] Update felix to work with the 1.0.0-rc1 version of the android
+  sdk.
+* [2008-10-15] Implemented remaining PackageAdmin methods from R4. (FELIX-35)
+* [2008-10-15] Modified resolver to correctly mark fragment modules as resolved.
+  (FELIX-777)
+* [2008-10-15] Modified the resolver to longer consider uninstalled fragments
+  and hosts when resolving dependencies; this required a new "stale" flag on 
+  modules that gets set when their associated bundle is uninstalled. (FELIX-776)
+* [2008-10-15] Modified the resolver to sort candidates when resolving 
+  Require-Bundle dependencies. (FELIX-775)
+* [2008-10-14] Modified ExportedPackage.getImportingBundles() to include
+  bundles requiring the exporting bundle, in addition to those bundles
+  importing the package. Also tried to simplify dependency management code
+  by separating it out. (FELIX-764) 
+* [2008-10-14] Fix a NullPointerException in SecureAction when a
+  SecurityManager is used by keeping pointers on the local stack. (FELIX-766)
+* [2008-10-13] Use System.gc to allow to update and uninstall bundles with 
+  native libs on Windows. (FELIX-733)
+* [2008-10-10] Set the activator to null on Bundle.stop() to allow for earlier 
+  garbage collection. (FELIX-762)
+* [2008-10-10] Implements support for flushing the cache on framework 
+  initialization. (FELIX-755)
+* [2008-10-10] Improve exception messages. (FELIX-750)
+* [2008-10-09] Modified the bundle cache to no longer have profiles. (FELIX-754)
+* [2008-10-08] Modified the Felix API to aligned with the proposed standard 
+  OSGi framework API. The framework instance can now be stopped and restarted. 
+  (FELIX-753)
+* [2008-09-29] Support transiently starting/stopping bundles. (FELIX-713) 
+* [2008-09-25] Correctly discard exported packages when a bundle is chosen to
+  import a package it exports. (FELIX-736)
+* [2008-09-24] Clearly distinguish between "resolved" and "unresolved" 
+  capabilities, which is necessary since "resolved" capabilities have higher 
+  priority than "unresolved" ones. (FELIX-730)
+* [2008-09-24] Do not bootdelegate in the case where Bundle.loadClass() has
+  been used. (FELIX-712)
+* [2008-09-23] Improve decoding of reference URLs. (FELIX-731)
+* [2008-09-23] For completeness, added symbolic names to framework and main.
+* [2008-09-17] Improve gc by fixing an issue with StartLevel and PackageAdmin.
+  (FELIX-728)
+* [2008-09-15] Fix an issue with Require-Bundle not aggregating packages 
+  correctly. (FELIX-722)
+* [2008-09-15] Fixed a bug where class loader delegation for dynamic imports 
+  was happening when it shouldn't. (FELIX-724)
+* [2008-09-12] Added a configuration property to determine whether installing a 
+  fragment that uses unimplemented features throws an exception or logs a 
+  warning. (FELIX-725)
+* [2008-09-12] Removed some experimental code for "implicit requirements",
+  since it was no longer needed.
+* [2008-09-12] Fix some visibility issues within the LDAP filter. (FELIX-721)
+
+Changes from 1.2.1 to 1.2.2
+---------------------------
+
+* [2008-10-14] Fixed a NullPointer exception that could happen when 
+  running with a SecurityManager as a backport of FELIX-766.
+
+Changes from 1.2.0 to 1.2.1
+---------------------------
+
+* [2008-09-05] Fixed a performance regression that was caused by uncached 
+  access to the bundle manifest headers. (FELIX-711)
+* [2008-09-03] Fixed a bug in calculating the system bundle's exported 
+  packages which added a null export package.
+
+Changes from 1.0.4 to 1.2.0
+---------------------------
+
+* [2008-08-29] Support JAR-wide manifest package metadata. (FELIX-682)
+* [2008-08-29] Throw an exception when installing a fragment that uses 
+  features that we do not support.
+* [2008-08-20] Improve error reporting when loading bundle classes using 
+  Bundle.loadClass. 
+* [2008-08-16] Fix attribute checking when resolving dynamic imports. 
+  (FELIX-676)
+* [2008-08-08] Implement singleton bundle support. (FELIX-102)
+* [2008-08-06] Default to current security policy if no security extension is 
+  installed in order to make Subject.doAs work. (FELIX-654)
+* [2008-08-04] Return Bundle.getLastModified() for bundle URLs 
+  lastModified(). (FELIX-582)
+* [2008-08-04] Improve handling of improper resource URLs. (FELIX-577)
+* [2008-08-01] Add necessary stub methods to implement new R4.1 methods. 
+  (FELIX-595)
+* [2008-08-01] Applied patch to properly return symbolic name when it has
+  parameters. (FELIX-650)
+* [2008-07-25] Fix some rollback issues after a bundle update throws an 
+  exception. (FELIX-647)
+* [2008-07-23] Modified manifest parser to be more lenient. (FELIX-641)
+* [2008-07-07] Add support for the standard symbolic name for the system 
+  bundle. (FELIX-602)
+* [2008-06-01] Add initial fragment support for extending the host bundle 
+  class path. (FELIX-29, FELIX-656)
+
+Changes from 1.0.3 to 1.0.4
+___________________________
+
+* [2008-04-18] Check that the returned object from a service factory is an 
+  instance of all classes named when the service was registered. (FELIX-540)
+* [2008-04-13] Fix an issue when logging messages while holding framework 
+  internal locks that could lead to a deadlock. For now we just disable 
+  logging to log services inside the framework. (FELIX-536)
+* [2008-04-13] Implemented various small performance improvments e.g.,
+  we now cache filters and create an index for objectclass to improve 
+  service lookup performance and don't use the BundleInfo to get the 
+  bundle id but the BundleImpl directly. Furthermore, we added an index to 
+  get bundles by id. 
+* [2008-04-04] Fix an NoClassDefFoundError when trying to query the 
+  framework classloader for whether a class is available or not. 
+* [2008-03-19] Don't try to resolve extension bundles and fix a 
+  classcastexception that could happen if more then one framework is around 
+  and both have extensions installed. Furthermore, make extension bundles 
+  use the system bundle context.
+* [2008-03-07] Bundle.getResource does now check for AdminPermission. 
+  (FELIX-150)
+* [2008-03-06] Make urlhandlers work when a handler factory is already set. 
+  Furthermore, it is now possible to have more then one framework running 
+  in more then one classloader. (FELIX-38).
+* [2008-03-05] Make LDAP filter reentrant.
+* [2008-02-29] Refactored the IContent abstraction from the module loader 
+  layer to provide more generic access to the content of the module. This 
+  makes it possible that a bundle's class path could span multiple module's, 
+  which will be necessary for fragments. (FELIX-29)
+* [2008-02-27] Applied a patch to disable the class loading diagnostic message
+  when the logger is not at the DEBUG level. (FELIX-500)
+* [2008-02-15] Modified the service registry to use more fine-grained locking 
+  to avoid callbacks to service factories while holding locks. (FELIX-489)
+
+Changes from 1.0.1 to 1.0.3
+---------------------------
+
+* [2008-01-27] Moved framework docs to main, since the launcher examples now
+  depend on main for the auto-property handling.
+* [2008-01-25] Applied patches  to improve how Felix finds resources when
+  getResources() is called. (FELIX-466, FELIX-467)
+* [2008-01-25] Make FilterImpl.toString() add escape characters where needed.
+  (FELIX-471)
+* [2008-01-16] Fix a NPE on framework restart when extension bundles are
+  installed.
+* [2008-01-15] Try to fix a potential visibility issue on loading a class
+  form a bundle where it seems to be possible that we create two classloaders
+  instead of one.
+* [2008-01-15] Modified boot delegation to not terminate when delegating to
+  parent class loader for non-java.* packages. (FELIX-463)
+* [2008-01-04] Fix some issues related to directories on the bundle class
+  path. Specifically, leading slashes created an issue and are now stripped
+  and entries were not being properly filtered when enumerating the contents
+  of a class path directory. (FELIX-426)
+* [2007-12-20] Modified logger to have all of its public methods be final to
+  avoid it from being extended in undesired ways via the constructor supplied
+  logger. (FELIX-428)
+* [2007-12-20] The TCK has changed to verify that bundles do not depend on
+  themselves, so we filter that case now.
+* [2007-12-20] Applied patch from Guillaume Nodet to properly fire a framework
+  error event only when a bundle cannot be resolved. (FELIX-441)
+* [2007-12-19] Modified framework to accept a Logger instance so that host
+  applications can do custom logging until the log service arrives. (FELIX-428)
+* [2007-12-19] Add support for loading bundles on Google Android (FELIX-440).
+* [2007-12-18] Improve native code loading - bundle installation will now
+  fail in case a native library is not in the jar and we only use the first
+  library of a given name. Furthermore, we now support .dylib extensions on
+  the mac as well as others; should make it possible to use .netmodules as
+  well. (FELIX-439)
+* [2007-12-17] Make the win32 alias match any version of windows for native
+  clauses. (FELIX-438)
+* [2007-12-14] Removed auto-property processing out of the framework and
+  into the default launcher, i.e, main. (FELIX-393)
+* [2007-12-13] Fix a StackOverflowError in URLHandlers.createStreamHandler() 
+  when creating URL on jamvm and Mika. This patch resolves an unfortunate 
+  interaction between our ExtensionManager and the URLHandlers by making the 
+  URLHandlers aware of the extension protocol. Pretty much like we do already 
+  for the bundle protocol. (FELIX-435)
+* [2007-11-26] Fix a small oversight in the extension manager that could lead
+  to a null pointer exception and save some memory by creating less objects.
+* [2007-11-26] Degrate to version 0.0.0 if we can not find the Felix.properties 
+  for some reason and avoid a null pointer in this case.
+* [2007-11-08] Reorganized usage count methods to better handle null
+  reference checking in response to Karl Pauls seeing an NPE when trying to
+  get a service that was already unregistered while shutting down the
+  framework.
+* [2007-11-06] Added a simple check to detect and work around a bug in J9.
+  (FELIX-416)
+* [2007-10-30] Change how the extension manager url stream handler handles 
+  request to the root path in order to make some tomcat issue go away.
+  (FELIX-414)
+* [2007-10-26] Added support for "/" bundle resources as requested. (FELIX-383)
+* [2007-10-24] Use system bundle when firing a framework error event when an 
+  install error occurs.
+* [2007-10-22] Fix a NPE when getEntryPaths is called on the system bundle. 
+  (FELIX-394)
+* [2007-10-16] Modifies bundle resource URL handling such that if a resource 
+  URL points to a resource that does not exist, a bundle class path search 
+  for the resource will be instigated and if any matching resource is found, 
+  that one will be used instead. (FELIX-383)
+* [2007-10-10] Auto-property handling now installs bundles into the default 
+  bundle start level if a start level is not specified. (FELIX-359)
+* [2007-10-08] Overwrite the URLStreamHandler.getHostAddress(URL) in the 
+  ExtensionManager to immediately return null to prevent DNS lookup. 
+  (FELIX-388)
+* [2007-09-30] Resolved a concurrency issue that could result in the same 
+  bundle being resolved more than once; also tried to simplify locking in 
+  the core search policy implementation. (FELIX-381)
+
+Changes from 1.0.0 to 1.0.1
+---------------------------
+
+* [2007-07-23] Fixed a bug in the framework shutdown process which was 
+  causing threads calling Felix.stopAndWait() to not get notified that 
+  the framework had shutdown. (FELIX-329)
+* [2007-08-15] Fixed a bug in the LDAP filter which was not thread safe 
+  on execution. (FELIX-338)
+* [2007-08-17] Added support for persistent last used bundle ID to avoid
+  re-use of bundle identifiers. (FELIX-339)
+* [2007-08-23] Modified BundleImpl.getHeaders(Locale) to accept a null 
+  locale. (FELIX-346)
+* [2007-09-11] Added support to handle external termination of Felix 
+  which was not handled or detected and prevented a restart of future 
+  Felix instances in the same VM. (FELIX-363)
+* [2007-09-12] Add support for Collection values in the LDAP filter to 
+  match the 4.1 spec. 
+* [2007-09-12] Fixed a bug in the LDAP filter where attributes where 
+  limited to [a-zA-Z ] so didn't allow for e.g., numbers. (FELIX-361)
+* [2007-09-13] Enable support for exporting the same package more than 
+  once (FELIX-101).
+* [2007-09-13] Returns the system bundle from a call to 
+  PackageAdmin.getBundle(Class) in case the class comes from the 
+  classloader (or one of its parents) that loaded Felix and the system 
+  bundle exports the package.
+* [2007-09-16] Fixed a bug in class space filtering in the service registry 
+  that could result in class cast exceptions for service clients.
+* [2007-09-16] Fixed a bug that prevented extension bundle exports to be 
+  usable.
+
+Changes from 0.8.0-incubator to 1.0.0
+-------------------------------------
+
+* [2007-01-18] Added support for bundle header localization. (FELIX-27)
+* [2007-01-22] Modified framework resolver to support a generic
+  capability/requirement model.
+* [2007-01-22] Reorganized and centralized manifest parsing code and added
+  support for resolver's generic capability/requirement model. (FELIX-98)
+* [2007-01-22] Improved native library naming normalization. (FELIX-26)
+* [2007-01-23] No longer eagerly resolving classes loaded from modules
+  since this was causing verification errors with IBM J9.
+* [2007-01-25] Added some support for execution environment checking.
+  (FELIX-23)
+* [2007-01-29] Added support for getAllServiceReferences(). (FELIX-32)
+* [2007-01-31] Added Require-Bundle support to resolve using the generic
+  capability/requirement model of the resolver. (FELIX-28)
+* [2007-02-05] Fixed a bug in processor type normalization for x86-64
+  processors.
+* [2007-02-09] The resolver previously ignored packages that were pending
+  removal when resolving new bundles, now it does not.
+* [2007-02-09] Bundles are automatically refreshed when updated/uninstalled
+  if none of their exported packages are in use.
+* [2007-02-13] Added support for extension bundles. (FELIX-30)
+* [2007-03-02] Added a Bundle.getBundleContext() method until actual
+  support for OSGi R4.1.
+* [2007-04-26] Modified Bundle.findEntries() to return URLs to directory
+  entries as well as file entries.
+* [2007-05-06] Modified LDAP evaluator to special case the fact that
+  BigDecimal is not available in Foundation profile.
+* [2007-05-21] Made some performance improvements in LDAP evaluation.
+* [2007-05-22] Modified JAR file to include Service Tracker package.
+* [2007-05-22] Improved concurrency handling around checking for already
+  loaded classes and defining classes.
+* [2007-06-05] Modified resource URLs to use port number rather than
+  prepend information to the resource path.
+* [2007-06-13] Improved dynamic imports to cycle through all available
+  candidates when checking for class space consistency.
+* [2007-06-18] Improved service registry filtering based on class versions
+  to allow a bundle to register a service for a different version of class
+  that it can access.
+* [2007-06-21] Modified our "last ditch effort" to guess when to delegate
+  to the system bundle to make it a little more robust.
+* [2007-06-29] Fixed a bug in EventDispatcher that was causing asynchronous
+  events to not be fired after stopping the framework instance and creating
+  a new instance. (FELIX-314)
+* [2007-07-03] Fixed a bug in EventDispatcher that would not correctly
+  update a listener when it implemented multiple listener interfaces.
+* [2007-07-04] Modified Felix framework class to implement the Bundle
+  interface to improve the startup/shutdown sequence and to provide a
+  simplified API for creating framework instances.
+* [2007-07-11] Removed the PropertyResolver-related classes and now only
+  use Maps for configuration properties. (FELIX-324)
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..1432244
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,100 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements.  See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership.  The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied.  See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <parent>
+    <groupId>org.apache.felix</groupId>
+    <artifactId>felix-parent</artifactId>
+    <version>2.1</version>
+    <relativePath>../pom/pom.xml</relativePath>
+  </parent>
+  <modelVersion>4.0.0</modelVersion>
+  <packaging>bundle</packaging>
+  <name>Apache Felix Framework</name>
+  <artifactId>org.apache.felix.framework</artifactId>
+  <version>4.4.0</version>
+  <dependencies>
+  </dependencies>
+  <properties>
+    <dollar>$</dollar>
+  </properties>
+    <scm>
+      <connection>scm:svn:http://svn.apache.org/repos/asf/felix/releases/org.apache.felix.framework-4.4.0</connection>
+      <developerConnection>scm:svn:https://svn.apache.org/repos/asf/felix/releases/org.apache.felix.framework-4.4.0</developerConnection>
+      <url>scm:svn:https://svn.apache.org/repos/asf/felix/releases/org.apache.felix.framework-4.4.0</url>
+    </scm>
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-compiler-plugin</artifactId>
+        <configuration>
+          <target>1.5</target>
+          <source>1.5</source>
+        </configuration>
+      </plugin>
+
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <version>2.3.5</version>
+        <extensions>true</extensions>
+        <configuration>
+          <instructions>
+            <Bundle-SymbolicName>org.apache.felix.framework</Bundle-SymbolicName>
+            <Bundle-Name>Apache Felix Framework</Bundle-Name>
+            <Bundle-Description>OSGi R4 framework implementation.</Bundle-Description>
+            <Bundle-Vendor>The Apache Software Foundation</Bundle-Vendor>
+            <Export-Package>org.osgi.framework.*;-split-package:=merge-first,org.osgi.resource,org.osgi.service.*,org.osgi.util.tracker</Export-Package>
+            <Private-Package>org.apache.felix.framework.*</Private-Package>
+            <Import-Package>!*</Import-Package>
+          </instructions>
+        </configuration>
+      </plugin>
+      <plugin>
+          <groupId>org.apache.rat</groupId>
+          <artifactId>apache-rat-plugin</artifactId>
+          <executions>
+              <execution>
+                  <phase>verify</phase>
+                  <goals>
+                      <goal>check</goal>
+                  </goals>
+              </execution>
+          </executions>
+          <configuration>
+              <includes>
+                  <include>src/**</include>
+              </includes>
+              <excludes>
+                  <exclude>src/main/appended-resources/**</exclude>
+                  <exclude>src/**/packageinfo</exclude>
+                  <exclude>src/main/resources/META-INF/services/org.osgi.framework.launch.FrameworkFactory</exclude>
+                  <exclude>src/main/resources/org/apache/felix/framework/Felix.properties</exclude>
+              </excludes>
+            </configuration>
+      </plugin>
+    </plugins>
+    <resources>
+      <resource>
+        <directory>src/main/resources</directory>
+        <filtering>true</filtering>
+      </resource>
+    </resources>
+  </build>
+</project>
diff --git a/src/main/appended-resources/META-INF/DEPENDENCIES b/src/main/appended-resources/META-INF/DEPENDENCIES
new file mode 100644
index 0000000..310180d
--- /dev/null
+++ b/src/main/appended-resources/META-INF/DEPENDENCIES
@@ -0,0 +1,22 @@
+Copyright 2013 The Apache Software Foundation
+
+This software was developed at the Apache Software Foundation
+(http://www.apache.org) and may have dependencies on other
+Apache software licensed under Apache License 2.0.
+
+I. Included Third-Party Software
+
+This product includes software developed at
+The OSGi Alliance (http://www.osgi.org/).
+Copyright (c) OSGi Alliance (2000, 2012).
+Licensed under the Apache License 2.0.
+
+II. Used Third-Party Software
+
+This product uses software developed at
+The OSGi Alliance (http://www.osgi.org/).
+Copyright (c) OSGi Alliance (2000, 2012).
+Licensed under the Apache License 2.0.
+
+III. License Summary
+- Apache License 2.0
diff --git a/src/main/appended-resources/META-INF/LICENSE b/src/main/appended-resources/META-INF/LICENSE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/src/main/appended-resources/META-INF/LICENSE
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/src/main/appended-resources/META-INF/NOTICE b/src/main/appended-resources/META-INF/NOTICE
new file mode 100644
index 0000000..8b4b009
--- /dev/null
+++ b/src/main/appended-resources/META-INF/NOTICE
@@ -0,0 +1,4 @@
+This product includes software developed at
+The OSGi Alliance (http://www.osgi.org/).
+Copyright (c) OSGi Alliance (2000, 2012).
+Licensed under the Apache License 2.0.
diff --git a/src/main/java/org/apache/felix/framework/BundleContextImpl.java b/src/main/java/org/apache/felix/framework/BundleContextImpl.java
new file mode 100644
index 0000000..bbdd980
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/BundleContextImpl.java
@@ -0,0 +1,516 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework;
+
+import java.io.File;
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Dictionary;
+
+import org.apache.felix.framework.ext.FelixBundleContext;
+import org.osgi.framework.AdminPermission;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.BundleListener;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Filter;
+import org.osgi.framework.FrameworkListener;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServicePermission;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.framework.SynchronousBundleListener;
+
+class BundleContextImpl implements FelixBundleContext
+{
+    private Logger m_logger = null;
+    private Felix m_felix = null;
+    private BundleImpl m_bundle = null;
+    private boolean m_valid = true;
+
+    protected BundleContextImpl(Logger logger, Felix felix, BundleImpl bundle)
+    {
+        m_logger = logger;
+        m_felix = felix;
+        m_bundle = bundle;
+    }
+
+    protected void invalidate()
+    {
+        m_valid = false;
+    }
+
+    public void addRequirement(String s) throws BundleException
+    {
+        throw new BundleException("Not implemented yet.");
+    }
+
+    public void removeRequirement() throws BundleException
+    {
+        throw new BundleException("Not implemented yet.");
+    }
+
+    public void addCapability() throws BundleException
+    {
+        throw new BundleException("Not implemented yet.");
+    }
+
+    public void removeCapability() throws BundleException
+    {
+        throw new BundleException("Not implemented yet.");
+    }
+
+    public String getProperty(String name)
+    {
+        checkValidity();
+
+        // CONCURRENCY NOTE: This is a check-then-act situation,
+        // but we ignore it since the time window is small and
+        // the result is the same as if the calling thread had
+        // won the race condition.
+
+        Object sm = System.getSecurityManager();
+
+        if (sm != null)
+        {
+            if (!(Constants.FRAMEWORK_VERSION.equals(name) ||
+                Constants.FRAMEWORK_VENDOR.equals(name) ||
+                Constants.FRAMEWORK_LANGUAGE.equals(name)||
+                Constants.FRAMEWORK_OS_NAME.equals(name) ||
+                Constants.FRAMEWORK_OS_VERSION.equals(name) ||
+                Constants.FRAMEWORK_PROCESSOR.equals(name)))
+            {
+                ((SecurityManager) sm).checkPermission(
+                    new java.util.PropertyPermission(name, "read"));
+            }
+        }
+
+        return m_felix.getProperty(name);
+    }
+
+    public Bundle getBundle()
+    {
+        checkValidity();
+
+        // CONCURRENCY NOTE: This is a check-then-act situation,
+        // but we ignore it since the time window is small and
+        // the result is the same as if the calling thread had
+        // won the race condition.
+
+        return m_bundle;
+    }
+
+    public Filter createFilter(String expr)
+        throws InvalidSyntaxException
+    {
+        checkValidity();
+
+        // CONCURRENCY NOTE: This is a check-then-act situation,
+        // but we ignore it since the time window is small and
+        // the result is the same as if the calling thread had
+        // won the race condition.
+
+        return new FilterImpl(expr);
+    }
+
+    public Bundle installBundle(String location)
+        throws BundleException
+    {
+        return installBundle(location, null);
+    }
+
+    public Bundle installBundle(String location, InputStream is)
+        throws BundleException
+    {
+        checkValidity();
+
+        // CONCURRENCY NOTE: This is a check-then-act situation,
+        // but we ignore it since the time window is small and
+        // the result is the same as if the calling thread had
+        // won the race condition.
+
+        Bundle result = null;
+
+        Object sm = System.getSecurityManager();
+
+        if (sm != null)
+        {
+            result = m_felix.installBundle(m_bundle, location, is);
+            // Do check the bundle again in case that is was installed
+            // already.
+            ((SecurityManager) sm).checkPermission(
+                new AdminPermission(result, AdminPermission.LIFECYCLE));
+        }
+        else
+        {
+            result = m_felix.installBundle(m_bundle, location, is);
+        }
+
+        return result;
+    }
+
+    public Bundle getBundle(long id)
+    {
+        checkValidity();
+
+        // CONCURRENCY NOTE: This is a check-then-act situation,
+        // but we ignore it since the time window is small and
+        // the result is the same as if the calling thread had
+        // won the race condition.
+
+        return m_felix.getBundle(this, id);
+    }
+
+    public Bundle getBundle(String location)
+    {
+        checkValidity();
+
+        // CONCURRENCY NOTE: This is a check-then-act situation,
+        // but we ignore it since the time window is small and
+        // the result is the same as if the calling thread had
+        // won the race condition.
+
+        return m_felix.getBundle(location);
+    }
+
+    public Bundle[] getBundles()
+    {
+        checkValidity();
+
+        // CONCURRENCY NOTE: This is a check-then-act situation,
+        // but we ignore it since the time window is small and
+        // the result is the same as if the calling thread had
+        // won the race condition.
+
+        return m_felix.getBundles(this);
+    }
+
+    public void addBundleListener(BundleListener l)
+    {
+        checkValidity();
+
+        // CONCURRENCY NOTE: This is a check-then-act situation, but
+        // internally the event dispatcher double checks whether or not
+        // the bundle context is valid before adding the service listener
+        // while holding the event queue lock, so it will either succeed
+        // or fail.
+
+        Object sm = System.getSecurityManager();
+
+        if (sm != null)
+        {
+            if (l instanceof SynchronousBundleListener)
+            {
+                ((SecurityManager) sm).checkPermission(new AdminPermission(m_bundle,
+                    AdminPermission.LISTENER));
+            }
+        }
+
+        m_felix.addBundleListener(m_bundle, l);
+    }
+
+    public void removeBundleListener(BundleListener l)
+    {
+        checkValidity();
+
+        // CONCURRENCY NOTE: This is a check-then-act situation,
+        // but we ignore it since the time window is small and
+        // the result is the same as if the calling thread had
+        // won the race condition.
+
+        Object sm = System.getSecurityManager();
+
+        if (sm != null)
+        {
+            if (l instanceof SynchronousBundleListener)
+            {
+                ((SecurityManager) sm).checkPermission(new AdminPermission(m_bundle,
+                    AdminPermission.LISTENER));
+            }
+        }
+
+        m_felix.removeBundleListener(m_bundle, l);
+    }
+
+    public void addServiceListener(ServiceListener l)
+    {
+        try
+        {
+            addServiceListener(l, null);
+        }
+        catch (InvalidSyntaxException ex)
+        {
+            // This will not happen since the filter is null.
+        }
+    }
+
+    public void addServiceListener(ServiceListener l, String s)
+        throws InvalidSyntaxException
+    {
+        checkValidity();
+
+        // CONCURRENCY NOTE: This is a check-then-act situation, but
+        // internally the event dispatcher double checks whether or not
+        // the bundle context is valid before adding the service listener
+        // while holding the event queue lock, so it will either succeed
+        // or fail.
+
+        m_felix.addServiceListener(m_bundle, l, s);
+    }
+
+    public void removeServiceListener(ServiceListener l)
+    {
+        checkValidity();
+
+        // CONCURRENCY NOTE: This is a check-then-act situation,
+        // but we ignore it since the time window is small and
+        // the result is the same as if the calling thread had
+        // won the race condition.
+
+        m_felix.removeServiceListener(m_bundle, l);
+    }
+
+    public void addFrameworkListener(FrameworkListener l)
+    {
+        checkValidity();
+
+        // CONCURRENCY NOTE: This is a check-then-act situation, but
+        // internally the event dispatcher double checks whether or not
+        // the bundle context is valid before adding the service listener
+        // while holding the event queue lock, so it will either succeed
+        // or fail.
+
+        m_felix.addFrameworkListener(m_bundle, l);
+    }
+
+    public void removeFrameworkListener(FrameworkListener l)
+    {
+        checkValidity();
+
+        // CONCURRENCY NOTE: This is a check-then-act situation,
+        // but we ignore it since the time window is small and
+        // the result is the same as if the calling thread had
+        // won the race condition.
+
+        m_felix.removeFrameworkListener(m_bundle, l);
+    }
+
+    public ServiceRegistration<?> registerService(
+        String clazz, Object svcObj, Dictionary<String, ? > dict)
+    {
+        return registerService(new String[] { clazz }, svcObj, dict);
+    }
+
+    public ServiceRegistration<?> registerService(
+        String[] clazzes, Object svcObj, Dictionary<String, ? > dict)
+    {
+        checkValidity();
+
+        // CONCURRENCY NOTE: This is a NOT a check-then-act situation,
+        // because internally the framework acquires the bundle state
+        // lock to ensure state consistency.
+
+        Object sm = System.getSecurityManager();
+
+        if (sm != null)
+        {
+            if (clazzes != null)
+            {
+                for (int i = 0;i < clazzes.length;i++)
+                {
+                    ((SecurityManager) sm).checkPermission(
+                        new ServicePermission(clazzes[i], ServicePermission.REGISTER));
+                }
+            }
+        }
+
+        return m_felix.registerService(this, clazzes, svcObj, dict);
+    }
+
+    public <S> ServiceRegistration<S> registerService(
+        Class<S> clazz, S svcObj, Dictionary<String, ? > dict)
+    {
+        return (ServiceRegistration<S>)
+            registerService(new String[] { clazz.getName() }, svcObj, dict);
+    }
+
+    public ServiceReference<?> getServiceReference(String clazz)
+    {
+        checkValidity();
+
+        // CONCURRENCY NOTE: This is a check-then-act situation,
+        // but we ignore it since the time window is small and
+        // the result is the same as if the calling thread had
+        // won the race condition.
+
+        try
+        {
+            ServiceReference[] refs = getServiceReferences(clazz, null);
+            return getBestServiceReference(refs);
+        }
+        catch (InvalidSyntaxException ex)
+        {
+            m_logger.log(m_bundle, Logger.LOG_ERROR, "BundleContextImpl: " + ex);
+        }
+        return null;
+    }
+
+    public <S> ServiceReference<S> getServiceReference(Class<S> clazz)
+    {
+        return (ServiceReference<S>) getServiceReference(clazz.getName());
+    }
+
+    private ServiceReference getBestServiceReference(ServiceReference[] refs)
+    {
+        if (refs == null)
+        {
+            return null;
+        }
+
+        if (refs.length == 1)
+        {
+            return refs[0];
+        }
+
+        // Loop through all service references and return
+        // the "best" one according to its rank and ID.
+        ServiceReference bestRef = refs[0];
+        for (int i = 1; i < refs.length; i++)
+        {
+            if (bestRef.compareTo(refs[i]) < 0)
+            {
+                bestRef = refs[i];
+            }
+        }
+
+        return bestRef;
+    }
+
+    public ServiceReference<?>[] getAllServiceReferences(String clazz, String filter)
+        throws InvalidSyntaxException
+    {
+        checkValidity();
+
+        // CONCURRENCY NOTE: This is a check-then-act situation,
+        // but we ignore it since the time window is small and
+        // the result is the same as if the calling thread had
+        // won the race condition.
+
+        return m_felix.getAllowedServiceReferences(m_bundle, clazz, filter, false);
+
+    }
+
+    public ServiceReference<?>[] getServiceReferences(String clazz, String filter)
+        throws InvalidSyntaxException
+    {
+        checkValidity();
+
+        // CONCURRENCY NOTE: This is a check-then-act situation,
+        // but we ignore it since the time window is small and
+        // the result is the same as if the calling thread had
+        // won the race condition.
+
+        return m_felix.getAllowedServiceReferences(m_bundle, clazz, filter, true);
+
+    }
+
+    public <S> Collection<ServiceReference<S>> getServiceReferences(
+        Class<S> clazz, String filter)
+        throws InvalidSyntaxException
+    {
+        ServiceReference<S>[] refs =
+            (ServiceReference<S>[]) getServiceReferences(clazz.getName(), filter);
+        return (refs == null)
+            ? Collections.EMPTY_LIST
+            : (Collection<ServiceReference<S>>) Arrays.asList(refs);
+    }
+
+    public <S> S getService(ServiceReference<S> ref)
+    {
+        checkValidity();
+
+        // CONCURRENCY NOTE: This is a check-then-act situation,
+        // but we ignore it since the time window is small and
+        // the result is the same as if the calling thread had
+        // won the race condition.
+
+        if (ref == null)
+        {
+            throw new NullPointerException("Specified service reference cannot be null.");
+        }
+
+        Object sm = System.getSecurityManager();
+
+        if (sm != null)
+        {
+           ((SecurityManager) sm).checkPermission(new ServicePermission(ref, ServicePermission.GET));
+        }
+
+        return m_felix.getService(m_bundle, ref);
+    }
+
+    public boolean ungetService(ServiceReference<?> ref)
+    {
+        checkValidity();
+
+        // CONCURRENCY NOTE: This is a check-then-act situation,
+        // but we ignore it since the time window is small and
+        // the result is the same as if the calling thread had
+        // won the race condition.
+
+        if (ref == null)
+        {
+            throw new NullPointerException("Specified service reference cannot be null.");
+        }
+
+        // Unget the specified service.
+        return m_felix.ungetService(m_bundle, ref);
+    }
+
+    public File getDataFile(String s)
+    {
+        checkValidity();
+
+        // CONCURRENCY NOTE: This is a check-then-act situation,
+        // but we ignore it since the time window is small and
+        // the result is the same as if the calling thread had
+        // won the race condition.
+
+        return m_felix.getDataFile(m_bundle, s);
+    }
+
+    private void checkValidity()
+    {
+        if (m_valid)
+        {
+            switch (m_bundle.getState())
+            {
+                case Bundle.ACTIVE:
+                case Bundle.STARTING:
+                case Bundle.STOPPING:
+                    return;
+            }
+        }
+
+        throw new IllegalStateException("Invalid BundleContext.");
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/felix/framework/BundleImpl.java b/src/main/java/org/apache/felix/framework/BundleImpl.java
new file mode 100644
index 0000000..f04bac7
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/BundleImpl.java
@@ -0,0 +1,1371 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.security.AccessControlContext;
+import java.security.ProtectionDomain;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.StringTokenizer;
+
+import org.apache.felix.framework.cache.BundleArchive;
+import org.apache.felix.framework.util.SecurityManagerEx;
+import org.apache.felix.framework.util.ShrinkableCollection;
+import org.apache.felix.framework.util.StringMap;
+import org.apache.felix.framework.util.Util;
+import org.osgi.framework.AdaptPermission;
+import org.osgi.framework.AdminPermission;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServicePermission;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.Version;
+import org.osgi.framework.hooks.bundle.CollisionHook;
+import org.osgi.framework.startlevel.BundleStartLevel;
+import org.osgi.framework.wiring.BundleRevision;
+import org.osgi.framework.wiring.BundleRevisions;
+import org.osgi.framework.wiring.BundleWire;
+import org.osgi.framework.wiring.BundleWiring;
+
+class BundleImpl implements Bundle, BundleRevisions
+{
+    // No one should use this field directly, use getFramework() instead.
+    private final Felix __m_felix;
+
+    private final BundleArchive m_archive;
+    private final List<BundleRevision> m_revisions = new ArrayList<BundleRevision>(0);
+    private volatile int m_state;
+    private boolean m_useDeclaredActivationPolicy;
+    private BundleActivator m_activator = null;
+    private volatile BundleContext m_context = null;
+    private final Map m_cachedHeaders = new HashMap();
+    private Map m_uninstalledHeaders = null;
+    private long m_cachedHeadersTimestamp;
+    private final Bundle m_installingBundle;
+
+    // Indicates whether the bundle is stale, meaning that it has
+    // been refreshed and completely removed from the framework.
+    private boolean m_stale = false;
+    // Used for bundle locking.
+    private int m_lockCount = 0;
+    private Thread m_lockThread = null;
+
+    /**
+     * This constructor is used by the system bundle (i.e., the framework),
+     * since it needs a constructor that does not throw an exception.
+    **/
+    BundleImpl()
+    {
+        __m_felix = null;
+        m_archive = null;
+        m_state = Bundle.INSTALLED;
+        m_useDeclaredActivationPolicy = false;
+        m_stale = false;
+        m_activator = null;
+        m_context = null;
+        m_installingBundle = null;
+    }
+
+    BundleImpl(Felix felix, Bundle installingBundle, BundleArchive archive) throws Exception
+    {
+        __m_felix = felix;
+        m_archive = archive;
+        m_state = Bundle.INSTALLED;
+        m_useDeclaredActivationPolicy = false;
+        m_stale = false;
+        m_activator = null;
+        m_context = null;
+        m_installingBundle = installingBundle;
+
+        BundleRevision revision = createRevision(false);
+        addRevision(revision);
+    }
+
+    // This method exists because the system bundle extends BundleImpl
+    // and cannot pass itself into the BundleImpl constructor. All methods
+    // in BundleImpl should use this method to get the framework and should
+    // not access the field directly.
+    Felix getFramework()
+    {
+        return __m_felix;
+    }
+
+    BundleArchive getArchive()
+    {
+        return m_archive;
+    }
+
+// Only called when the framework is stopping. Don't need to clean up dependencies.
+    synchronized void close()
+    {
+        closeRevisions();
+        try
+        {
+            m_archive.close();
+        }
+        catch (Exception ex)
+        {
+            getFramework().getLogger().log(
+                this,
+                Logger.LOG_ERROR,
+                "Unable to close archive revisions.", ex);
+        }
+    }
+
+// Called when install fails, when stopping framework with uninstalled bundles,
+// and when refreshing an uninstalled bundle. Only need to clear up dependencies
+// for last case.
+    synchronized void closeAndDelete() throws Exception
+    {
+        if (!m_stale)
+        {
+            // Mark the bundle as stale, since it is being deleted.
+            m_stale = true;
+            // Close all revisions.
+            closeRevisions();
+            // Delete bundle archive, which will close revisions.
+            m_archive.closeAndDelete();
+        }
+    }
+
+// Called from BundleImpl.close(), BundleImpl.closeAndDelete(), and BundleImpl.refresh()
+    private void closeRevisions()
+    {
+        // Remove the bundle's associated revisions from the resolver state
+        // and close them.
+        for (BundleRevision br : m_revisions)
+        {
+            // Remove the revision from the resolver state.
+            getFramework().getResolver().removeRevision(br);
+
+            // Close the revision's content.
+            ((BundleRevisionImpl) br).close();
+        }
+    }
+
+// Called when refreshing a bundle. Must clean up dependencies beforehand.
+    synchronized void refresh() throws Exception
+    {
+        if (isExtension() && (getFramework().getState() != Bundle.STOPPING))
+        {
+            getFramework().getLogger().log(this, Logger.LOG_WARNING,
+                "Framework restart on extension bundle refresh not implemented.");
+        }
+        else
+        {
+            // Get current revision, since we can reuse it.
+            BundleRevisionImpl current = adapt(BundleRevisionImpl.class);
+            // Close all existing revisions.
+            closeRevisions();
+            // Clear all revisions.
+            m_revisions.clear();
+
+            // Purge all old archive revisions, only keeping the newest one.
+            m_archive.purge();
+
+            // Reset the content of the current bundle revision.
+            current.resetContent(m_archive.getCurrentRevision().getContent());
+            // Re-add the revision to the bundle.
+            addRevision(current);
+
+            // Reset the bundle state.
+            m_state = Bundle.INSTALLED;
+            m_stale = false;
+
+            synchronized (m_cachedHeaders)
+            {
+                m_cachedHeaders.clear();
+                m_cachedHeadersTimestamp = 0;
+            }
+        }
+    }
+
+    synchronized boolean isDeclaredActivationPolicyUsed()
+    {
+        return m_useDeclaredActivationPolicy;
+    }
+
+    synchronized void setDeclaredActivationPolicyUsed(boolean b)
+    {
+        m_useDeclaredActivationPolicy = b;
+    }
+
+    synchronized BundleActivator getActivator()
+    {
+        return m_activator;
+    }
+
+    synchronized void setActivator(BundleActivator activator)
+    {
+        m_activator = activator;
+    }
+
+    public BundleContext getBundleContext()
+    {
+        Object sm = System.getSecurityManager();
+
+        if (sm != null)
+        {
+           ((SecurityManager) sm).checkPermission(
+               new AdminPermission(this, AdminPermission.CONTEXT));
+        }
+
+        return m_context;
+    }
+
+    void setBundleContext(BundleContext context)
+    {
+        m_context = context;
+    }
+
+    public long getBundleId()
+    {
+        try
+        {
+            return m_archive.getId();
+        }
+        catch (Exception ex)
+        {
+            getFramework().getLogger().log(
+                this,
+                Logger.LOG_ERROR,
+                "Error getting the identifier from bundle archive.",
+                ex);
+            return -1;
+        }
+    }
+
+    public URL getEntry(String name)
+    {
+        Object sm = System.getSecurityManager();
+
+        if (sm != null)
+        {
+            try
+            {
+                ((SecurityManager) sm).checkPermission(new AdminPermission(this,
+                    AdminPermission.RESOURCE));
+            }
+            catch (Exception e)
+            {
+                return null; // No permission
+            }
+        }
+
+        return getFramework().getBundleEntry(this, name);
+    }
+
+    public Enumeration getEntryPaths(String path)
+    {
+        Object sm = System.getSecurityManager();
+
+        if (sm != null)
+        {
+            try
+            {
+                ((SecurityManager) sm).checkPermission(new AdminPermission(this,
+                    AdminPermission.RESOURCE));
+            }
+            catch (Exception e)
+            {
+                return null; // No permission
+            }
+        }
+
+        return getFramework().getBundleEntryPaths(this, path);
+    }
+
+    public Enumeration findEntries(String path, String filePattern, boolean recurse)
+    {
+        Object sm = System.getSecurityManager();
+
+        if (sm != null)
+        {
+            try
+            {
+                ((SecurityManager) sm).checkPermission(new AdminPermission(this,
+                    AdminPermission.RESOURCE));
+            }
+            catch (Exception e)
+            {
+                return null; // No permission
+            }
+        }
+
+        return getFramework().findBundleEntries(
+                this, path, filePattern, recurse);
+    }
+
+    public Dictionary getHeaders()
+    {
+        return getHeaders(Locale.getDefault().toString());
+    }
+
+    public Dictionary getHeaders(String locale)
+    {
+        Object sm = System.getSecurityManager();
+
+        if (sm != null)
+        {
+            ((SecurityManager) sm).checkPermission(new AdminPermission(this,
+                AdminPermission.METADATA));
+        }
+
+        if (locale == null)
+        {
+            locale = Locale.getDefault().toString();
+        }
+
+        return getFramework().getBundleHeaders(this, locale);
+    }
+
+    Map getCurrentLocalizedHeader(String locale)
+    {
+        Map result = null;
+
+        // Spec says empty local returns raw headers.
+        if (locale.length() == 0)
+        {
+            result = new StringMap(adapt(BundleRevisionImpl.class).getHeaders());
+        }
+
+        // If we have no result, try to get it from the cached headers.
+        if (result == null)
+        {
+            synchronized (m_cachedHeaders)
+            {
+                // If the bundle is uninstalled, then we should always return
+                // the uninstalled headers, which are the default locale as per
+                // the spec.
+                if (m_uninstalledHeaders != null)
+                {
+                    result = m_uninstalledHeaders;
+                }
+                // If the bundle has been updated, clear the cached headers.
+                else if (getLastModified() > m_cachedHeadersTimestamp)
+                {
+                    m_cachedHeaders.clear();
+                }
+                // Otherwise, returned the cached headers if they exist.
+                else
+                {
+                    // Check if headers for this locale have already been resolved
+                    if (m_cachedHeaders.containsKey(locale))
+                    {
+                        result = (Map) m_cachedHeaders.get(locale);
+                    }
+                }
+            }
+        }
+
+        // If the requested locale is not cached, then try to create it.
+        if (result == null)
+        {
+            // Get a modifiable copy of the raw headers.
+            Map headers = new StringMap(adapt(BundleRevisionImpl.class).getHeaders());
+            // Assume for now that this will be the result.
+            result = headers;
+
+            // Check to see if we actually need to localize anything
+            boolean localize = false;
+            for (Iterator it = headers.values().iterator(); !localize && it.hasNext(); )
+            {
+                if (((String) it.next()).startsWith("%"))
+                {
+                    localize = true;
+                }
+            }
+
+            if (!localize)
+            {
+                // If localization is not needed, just cache the headers and return
+                // them as-is. Not sure if this is useful
+                updateHeaderCache(locale, headers);
+            }
+            else
+            {
+                // Do localization here and return the localized headers
+                String basename = (String) headers.get(Constants.BUNDLE_LOCALIZATION);
+                if (basename == null)
+                {
+                    basename = Constants.BUNDLE_LOCALIZATION_DEFAULT_BASENAME;
+                }
+
+                // Create ordered list of revisions to search for localization
+                // property resources.
+                List<BundleRevision> revisionList = createLocalizationRevisionList(
+                    adapt(BundleRevisionImpl.class));
+
+                // Create ordered list of files to load properties from
+                List<String> resourceList = createLocalizationResourceList(basename, locale);
+
+                // Create a merged props file with all available props for this locale
+                boolean found = false;
+                Properties mergedProperties = new Properties();
+                for (BundleRevision br : revisionList)
+                {
+                    for (String res : resourceList)
+                    {
+                        URL temp = ((BundleRevisionImpl) br).getEntry(res + ".properties");
+                        if (temp != null)
+                        {
+                            found = true;
+                            try
+                            {
+                                mergedProperties.load(
+                                    temp.openConnection().getInputStream());
+                            }
+                            catch (IOException ex)
+                            {
+                                // File doesn't exist, just continue loop
+                            }
+                        }
+                    }
+                }
+
+                // If the specified locale was not found, then the spec says we should
+                // return the default localization.
+                if (!found && !locale.equals(Locale.getDefault().toString()))
+                {
+                    result = getCurrentLocalizedHeader(Locale.getDefault().toString());
+                }
+                // Otherwise, perform the localization based on the discovered
+                // properties and cache the result.
+                else
+                {
+                    // Resolve all localized header entries
+                    for (Iterator it = headers.entrySet().iterator(); it.hasNext(); )
+                    {
+                        Map.Entry entry = (Map.Entry) it.next();
+                        String value = (String) entry.getValue();
+                        if (value.startsWith("%"))
+                        {
+                            String newvalue;
+                            String key = value.substring(value.indexOf("%") + 1);
+                            newvalue = mergedProperties.getProperty(key);
+                            if (newvalue==null)
+                            {
+                                newvalue = key;
+                            }
+                            entry.setValue(newvalue);
+                        }
+                    }
+
+                    updateHeaderCache(locale, headers);
+                }
+            }
+        }
+
+        return result;
+    }
+
+    private void updateHeaderCache(String locale, Map localizedHeaders)
+    {
+        synchronized (m_cachedHeaders)
+        {
+            if (m_uninstalledHeaders == null)
+            {
+                m_cachedHeaders.put(locale, localizedHeaders);
+                m_cachedHeadersTimestamp = System.currentTimeMillis();
+            }
+        }
+    }
+
+    private static List<BundleRevision> createLocalizationRevisionList(BundleRevision br)
+    {
+        // If the revision is a fragment, then we actually need
+        // to search its host and associated fragments for its
+        // localization information. So, check to see if there
+        // are any hosts and then use the one with the highest
+        // version instead of the fragment itself. If there are
+        // no hosts, but the revision is a fragment, then just
+        // search the revision itself.
+        if (Util.isFragment(br))
+        {
+            if (br.getWiring() != null)
+            {
+                List<BundleWire> hostWires = br.getWiring().getRequiredWires(null);
+                if ((hostWires != null) && (hostWires.size() > 0))
+                {
+                    br = hostWires.get(0).getProviderWiring().getRevision();
+                    for (int hostIdx = 1; hostIdx < hostWires.size(); hostIdx++)
+                    {
+                        if (br.getVersion().compareTo(
+                            hostWires.get(hostIdx).getProviderWiring().getRevision().getVersion()) < 0)
+                        {
+                            br = hostWires.get(hostIdx).getProviderWiring().getRevision();
+                        }
+                    }
+                }
+            }
+        }
+
+        // Create a list of the revision and any attached fragment revisions.
+        List<BundleRevision> result = new ArrayList<BundleRevision>();
+        result.add(br);
+        BundleWiring wiring = br.getWiring();
+        if (wiring != null)
+        {
+            List<BundleRevision> fragments = Util.getFragments(wiring);
+            if (fragments != null)
+            {
+                result.addAll(fragments);
+            }
+        }
+        return result;
+    }
+
+    private static List<String> createLocalizationResourceList(String basename, String locale)
+    {
+        List<String> result = new ArrayList(4);
+
+        StringTokenizer tokens;
+        StringBuffer tempLocale = new StringBuffer(basename);
+
+        result.add(tempLocale.toString());
+
+        if (locale.length() > 0)
+        {
+            tokens = new StringTokenizer(locale, "_");
+            while (tokens.hasMoreTokens())
+            {
+                tempLocale.append("_").append(tokens.nextToken());
+                result.add(tempLocale.toString());
+            }
+        }
+        return result;
+    }
+
+    public long getLastModified()
+    {
+        try
+        {
+            return m_archive.getLastModified();
+        }
+        catch (Exception ex)
+        {
+            getFramework().getLogger().log(
+                this,
+                Logger.LOG_ERROR,
+                "Error reading last modification time from bundle archive.",
+                ex);
+            return 0;
+        }
+    }
+
+    void setLastModified(long l)
+    {
+        try
+        {
+            m_archive.setLastModified(l);
+        }
+        catch (Exception ex)
+        {
+            getFramework().getLogger().log(
+                this,
+                Logger.LOG_ERROR,
+                "Error writing last modification time to bundle archive.",
+                ex);
+        }
+    }
+
+    public String getLocation()
+    {
+        Object sm = System.getSecurityManager();
+
+        if (sm != null)
+        {
+            ((SecurityManager) sm).checkPermission(new AdminPermission(this,
+                AdminPermission.METADATA));
+        }
+        return _getLocation();
+    }
+
+    String _getLocation()
+    {
+        try
+        {
+            return m_archive.getLocation();
+        }
+        catch (Exception ex)
+        {
+            getFramework().getLogger().log(
+                this,
+                Logger.LOG_ERROR,
+                "Error getting location from bundle archive.",
+                ex);
+            return null;
+        }
+    }
+
+    /**
+     * Returns a URL to a named resource in the bundle.
+     *
+     * @return a URL to named resource, or null if not found.
+    **/
+    public URL getResource(String name)
+    {
+        Object sm = System.getSecurityManager();
+
+        if (sm != null)
+        {
+            try
+            {
+                ((SecurityManager) sm).checkPermission(
+                    new AdminPermission(this, AdminPermission.RESOURCE));
+            }
+            catch (Exception e)
+            {
+                return null; // No permission
+            }
+        }
+
+        return getFramework().getBundleResource(this, name);
+    }
+
+    public Enumeration getResources(String name) throws IOException
+    {
+        Object sm = System.getSecurityManager();
+
+        if (sm != null)
+        {
+            try
+            {
+                ((SecurityManager) sm).checkPermission(
+                    new AdminPermission(this, AdminPermission.RESOURCE));
+            }
+            catch (Exception e)
+            {
+                return null; // No permission
+            }
+        }
+
+        // Spec says we should return null when resources not found,
+        // even though ClassLoader.getResources() returns empty enumeration.
+        Enumeration e = getFramework().getBundleResources(this, name);
+        return ((e == null) || !e.hasMoreElements()) ? null : e;
+    }
+
+    /**
+     * Returns an array of service references corresponding to
+     * the bundle's registered services.
+     *
+     * @return an array of service references or null.
+    **/
+    public ServiceReference[] getRegisteredServices()
+    {
+        Object sm = System.getSecurityManager();
+
+        if (sm != null)
+        {
+            ServiceReference[] refs = getFramework().getBundleRegisteredServices(this);
+
+            if (refs == null)
+            {
+                return refs;
+            }
+
+            List result = new ArrayList();
+
+            for (int i = 0; i < refs.length; i++)
+            {
+                try
+                {
+                    ((SecurityManager) sm).checkPermission(new ServicePermission(
+                        refs[i], ServicePermission.GET));
+
+                    result.add(refs[i]);
+                }
+                catch (Exception ex)
+                {
+                    // Silently ignore.
+                }
+            }
+
+            if (result.isEmpty())
+            {
+                return null;
+            }
+
+            return (ServiceReference[]) result.toArray(new ServiceReference[result.size()]);
+        }
+        else
+        {
+            return getFramework().getBundleRegisteredServices(this);
+        }
+    }
+
+    public ServiceReference[] getServicesInUse()
+    {
+        Object sm = System.getSecurityManager();
+
+        if (sm != null)
+        {
+            ServiceReference[] refs = getFramework().getBundleServicesInUse(this);
+
+            if (refs == null)
+            {
+                return refs;
+            }
+
+            List result = new ArrayList();
+
+            for (int i = 0; i < refs.length; i++)
+            {
+                try
+                {
+                    ((SecurityManager) sm).checkPermission(
+                        new ServicePermission(refs[i], ServicePermission.GET));
+
+                    result.add(refs[i]);
+                }
+                catch (Exception ex)
+                {
+                    // Silently ignore.
+                }
+            }
+
+            if (result.isEmpty())
+            {
+                return null;
+            }
+
+            return (ServiceReference[]) result.toArray(new ServiceReference[result.size()]);
+        }
+
+        return getFramework().getBundleServicesInUse(this);
+    }
+
+    public int getState()
+    {
+        return m_state;
+    }
+
+    // This method should not be called directly.
+    void __setState(int i)
+    {
+        m_state = i;
+    }
+
+    int getPersistentState()
+    {
+        try
+        {
+            return m_archive.getPersistentState();
+        }
+        catch (Exception ex)
+        {
+            getFramework().getLogger().log(
+                this,
+                Logger.LOG_ERROR,
+                "Error reading persistent state from bundle archive.",
+                ex);
+            return Bundle.INSTALLED;
+        }
+    }
+
+    void setPersistentStateInactive()
+    {
+        try
+        {
+            m_archive.setPersistentState(Bundle.INSTALLED);
+        }
+        catch (Exception ex)
+        {
+            getFramework().getLogger().log(this, Logger.LOG_ERROR,
+                "Error writing persistent state to bundle archive.",
+                ex);
+        }
+    }
+
+    void setPersistentStateActive()
+    {
+        try
+        {
+            m_archive.setPersistentState(Bundle.ACTIVE);
+        }
+        catch (Exception ex)
+        {
+            getFramework().getLogger().log(
+                this,
+                Logger.LOG_ERROR,
+                "Error writing persistent state to bundle archive.",
+                ex);
+        }
+    }
+
+    void setPersistentStateStarting()
+    {
+        try
+        {
+            m_archive.setPersistentState(Bundle.STARTING);
+        }
+        catch (Exception ex)
+        {
+            getFramework().getLogger().log(
+                this,
+                Logger.LOG_ERROR,
+                "Error writing persistent state to bundle archive.",
+                ex);
+        }
+    }
+    void setPersistentStateUninstalled()
+    {
+        try
+        {
+            m_archive.setPersistentState(Bundle.UNINSTALLED);
+        }
+        catch (Exception ex)
+        {
+            getFramework().getLogger().log(
+                this,
+                Logger.LOG_ERROR,
+                "Error writing persistent state to bundle archive.",
+                ex);
+        }
+    }
+
+    int getStartLevel(int defaultLevel)
+    {
+        try
+        {
+            int level = m_archive.getStartLevel();
+            if ( level == -1 )
+            {
+                level = defaultLevel;
+            }
+            return level;
+        }
+        catch (Exception ex)
+        {
+            getFramework().getLogger().log(
+                this,
+                Logger.LOG_ERROR,
+                "Error reading start level from bundle archive.",
+                ex);
+            return defaultLevel;
+        }
+    }
+
+    void setStartLevel(int i)
+    {
+        try
+        {
+            m_archive.setStartLevel(i);
+        }
+        catch (Exception ex)
+        {
+            getFramework().getLogger().log(
+                this,
+                Logger.LOG_ERROR,
+                "Error writing start level to bundle archive.",
+                ex);
+        }
+    }
+
+    synchronized boolean isStale()
+    {
+        return m_stale;
+    }
+
+    synchronized boolean isExtension()
+    {
+        for (BundleRevision revision : m_revisions)
+        {
+            if (((BundleRevisionImpl) revision).isExtension())
+            {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public String getSymbolicName()
+    {
+        return adapt(BundleRevisionImpl.class).getSymbolicName();
+    }
+
+    public Version getVersion()
+    {
+        return adapt(BundleRevisionImpl.class).getVersion();
+    }
+
+    public boolean hasPermission(Object obj)
+    {
+        return getFramework().bundleHasPermission(this, obj);
+    }
+
+    public Map getSignerCertificates(int signersType)
+    {
+        // TODO: SECURITY - This needs to be adapted to our security mechanisms.
+        return (Map) getFramework().getSignerMatcher(this, signersType);
+    }
+
+    public Class loadClass(String name) throws ClassNotFoundException
+    {
+        if (isExtension())
+        {
+            throw new ClassNotFoundException("Extension bundles cannot load classes.");
+        }
+
+        Object sm = System.getSecurityManager();
+
+        if (sm != null)
+        {
+            try
+            {
+                ((SecurityManager) sm).checkPermission(new AdminPermission(this,
+                    AdminPermission.CLASS));
+            }
+            catch (Exception ex)
+            {
+                throw new ClassNotFoundException("No permission.", ex);
+            }
+        }
+
+        return getFramework().loadBundleClass(this, name);
+    }
+
+    public void start() throws BundleException
+    {
+        start(0);
+    }
+
+    public void start(int options) throws BundleException
+    {
+        Object sm = System.getSecurityManager();
+
+        if (sm != null)
+        {
+            ((SecurityManager) sm).checkPermission(new AdminPermission(this,
+                AdminPermission.EXECUTE));
+        }
+
+        getFramework().startBundle(this, options);
+    }
+
+    public void update() throws BundleException
+    {
+        update(null);
+    }
+
+    public void update(InputStream is) throws BundleException
+    {
+        Object sm = System.getSecurityManager();
+
+        if (sm != null)
+        {
+            ((SecurityManager) sm).checkPermission(new AdminPermission(this,
+                AdminPermission.LIFECYCLE));
+        }
+
+        getFramework().updateBundle(this, is);
+    }
+
+    public void stop() throws BundleException
+    {
+        stop(0);
+    }
+
+    public void stop(int options) throws BundleException
+    {
+        Object sm = System.getSecurityManager();
+
+        if (sm != null)
+        {
+            ((SecurityManager) sm).checkPermission(new AdminPermission(this,
+                AdminPermission.EXECUTE));
+        }
+
+        getFramework().stopBundle(this, ((options & Bundle.STOP_TRANSIENT) == 0));
+    }
+
+    public void uninstall() throws BundleException
+    {
+        Object sm = System.getSecurityManager();
+
+        if (sm != null)
+        {
+            ((SecurityManager) sm).checkPermission(new AdminPermission(this,
+                AdminPermission.LIFECYCLE));
+        }
+
+        // After a bundle is uninstalled, the spec says getHeaders() should
+        // return the localized headers for the default locale at the time of
+        // of uninstall. So, let's clear the existing header cache to throw
+        // away any other locales and populate it with the headers for the
+        // default locale. We only do this if there are multiple cached locales
+        // and if the default locale is not cached. If this method is called
+        // more than once, then subsequent calls will do nothing here since
+        // only the default locale will be left in the header cache.
+        synchronized (m_cachedHeaders)
+        {
+            if (m_uninstalledHeaders == null)
+            {
+                m_uninstalledHeaders = getCurrentLocalizedHeader(Locale.getDefault().toString());
+                m_cachedHeaders.clear();
+            }
+        }
+
+        // Uninstall the bundle.
+        getFramework().uninstallBundle(this);
+    }
+
+    private static final SecurityManagerEx m_smEx = new SecurityManagerEx();
+    private static final ClassLoader m_classloader = Felix.class.getClassLoader();
+
+    <A> void checkAdapt(Class<A> type)
+    {
+        Object sm = System.getSecurityManager();
+        if ((sm != null) && (getFramework().getSecurityProvider() != null))
+        {
+            Class caller = m_smEx.getClassContext()[3];
+            if (((Felix.m_secureAction.getClassLoader(caller) != m_classloader) ||
+                !caller.getName().startsWith("org.apache.felix.framework.")))
+            {
+                ((SecurityManager) sm).checkPermission(
+                    new AdaptPermission(type.getName(), this, AdaptPermission.ADAPT));
+            }
+        }
+    }
+
+    public synchronized <A> A adapt(Class<A> type)
+    {
+        checkAdapt(type);
+        if (type == BundleContext.class)
+        {
+            return (A) m_context;
+        }
+        else if (type == BundleStartLevel.class)
+        {
+            return (A) getFramework().adapt(FrameworkStartLevelImpl.class)
+                .createBundleStartLevel(this);
+        }
+        else if (type == BundleRevision.class)
+        {
+            if (m_state == Bundle.UNINSTALLED)
+            {
+                return null;
+            }
+            return (A) m_revisions.get(0);
+        }
+        // We need some way to get the current revision even if
+        // the associated bundle is uninstalled, so we use the
+        // impl revision class for this purpose.
+        else if (type == BundleRevisionImpl.class)
+        {
+            return (A) m_revisions.get(0);
+        }
+        else if (type == BundleRevisions.class)
+        {
+            return (A) this;
+        }
+        else if (type == BundleWiring.class)
+        {
+            if (m_state == Bundle.UNINSTALLED)
+            {
+                return null;
+            }
+            return (A) m_revisions.get(0).getWiring();
+        }
+        else if ( type == AccessControlContext.class)
+        {
+            if (m_state == Bundle.UNINSTALLED)
+            {
+                return null;
+            }
+            final ProtectionDomain pd = this.getProtectionDomain();
+            if (pd == null)
+            {
+                return null;
+            }
+            return (A) new AccessControlContext(new ProtectionDomain[] {pd});
+
+        }
+        return null;
+    }
+
+    public File getDataFile(String filename)
+    {
+        return getFramework().getDataFile(this, filename);
+    }
+
+    public int compareTo(Bundle t)
+    {
+        long thisBundleId = this.getBundleId();
+        long thatBundleId = t.getBundleId();
+        return (thisBundleId < thatBundleId ? -1 : (thisBundleId == thatBundleId ? 0 : 1));
+    }
+
+    @Override
+    public String toString()
+    {
+        String sym = getSymbolicName();
+        if (sym != null)
+        {
+            return sym + " [" + getBundleId() +"]";
+        }
+        return "[" + getBundleId() +"]";
+    }
+
+    synchronized boolean isRemovalPending()
+    {
+        return (m_state == Bundle.UNINSTALLED) || (m_revisions.size() > 1)  || m_stale;
+    }
+
+    //
+    // Revision management.
+    //
+
+    public Bundle getBundle()
+    {
+        return this;
+    }
+
+    public synchronized List<BundleRevision> getRevisions()
+    {
+        return new ArrayList<BundleRevision>(m_revisions);
+    }
+
+    /**
+     * Determines if the specified module is associated with this bundle.
+     * @param revision the module to determine if it is associate with this bundle.
+     * @return <tt>true</tt> if the specified module is in the array of modules
+     *         associated with this bundle, <tt>false</tt> otherwise.
+    **/
+    synchronized boolean hasRevision(BundleRevision revision)
+    {
+        return m_revisions.contains(revision);
+    }
+
+    synchronized void revise(String location, InputStream is)
+        throws Exception
+    {
+        // This operation will increase the revision count for the bundle.
+        m_archive.revise(location, is);
+        try
+        {
+            BundleRevision revision = createRevision(true);
+            addRevision(revision);
+        }
+        catch (Exception ex)
+        {
+            m_archive.rollbackRevise();
+            throw ex;
+        }
+    }
+
+    synchronized boolean rollbackRevise() throws Exception
+    {
+        boolean isExtension = isExtension();
+        BundleRevision br = m_revisions.remove(0);
+        if (!isExtension)
+        {
+            // Since revising a bundle adds a revision to the global
+            // state, we must remove it from the global state on rollback.
+            getFramework().getResolver().removeRevision(br);
+        }
+        return m_archive.rollbackRevise();
+    }
+
+    // This method should be private, but is visible because the
+    // system bundle needs to add its revision directly to the bundle,
+    // since it doesn't have an archive from which it will be created,
+    // which is the normal case.
+    synchronized void addRevision(BundleRevision revision) throws Exception
+    {
+        m_revisions.add(0, revision);
+
+        try
+        {
+            getFramework().setBundleProtectionDomain(this, (BundleRevisionImpl) revision);
+        }
+        catch (Exception ex)
+        {
+            m_revisions.remove(0);
+            throw ex;
+        }
+
+        // TODO: REFACTOR - consider nulling capabilities for extension bundles
+        // so we don't need this check anymore.
+        if (!isExtension())
+        {
+            // Now that the revision is added to the bundle, we can update
+            // the resolver's state to be aware of any new capabilities.
+            getFramework().getResolver().addRevision(revision);
+        }
+    }
+
+    private BundleRevision createRevision(boolean isUpdate) throws Exception
+    {
+        // Get and parse the manifest from the most recent revision and
+        // create an associated revision object for it.
+        Map headerMap = m_archive.getCurrentRevision().getManifestHeader();
+
+        // Create the bundle revision instance.
+        BundleRevisionImpl revision = new BundleRevisionImpl(
+            this,
+            Long.toString(getBundleId())
+                + "." + m_archive.getCurrentRevisionNumber().toString(),
+            headerMap,
+            m_archive.getCurrentRevision().getContent());
+
+        // For R4 bundles, verify that the bundle symbolic name + version
+        // is unique unless this check has been disabled.
+        String allowMultiple =
+            (String) getFramework().getConfig().get(Constants.FRAMEWORK_BSNVERSION);
+        allowMultiple = (allowMultiple == null)
+            ? Constants.FRAMEWORK_BSNVERSION_MANAGED
+            : allowMultiple;
+        if (revision.getManifestVersion().equals("2")
+            && !allowMultiple.equals(Constants.FRAMEWORK_BSNVERSION_MULTIPLE))
+        {
+            Version bundleVersion = revision.getVersion();
+            bundleVersion = (bundleVersion == null) ? Version.emptyVersion : bundleVersion;
+            String symName = revision.getSymbolicName();
+
+            List<Bundle> collisionCanditates = new ArrayList<Bundle>();
+            Bundle[] bundles = getFramework().getBundles();
+            for (int i = 0; (bundles != null) && (i < bundles.length); i++)
+            {
+                long id = ((BundleImpl) bundles[i]).getBundleId();
+                if (id != getBundleId())
+                {
+                    if (symName.equals(bundles[i].getSymbolicName())
+                        && bundleVersion.equals(bundles[i].getVersion()))
+                    {
+                        collisionCanditates.add(bundles[i]);
+                    }
+                }
+            }
+            if (!collisionCanditates.isEmpty() && allowMultiple.equals(Constants.FRAMEWORK_BSNVERSION_MANAGED))
+            {
+                Set<ServiceReference<CollisionHook>> hooks = getFramework().getHooks(CollisionHook.class);
+                if (!hooks.isEmpty())
+                {
+                    Collection<Bundle> shrinkableCollisionCandidates = new ShrinkableCollection<Bundle>(collisionCanditates);
+                    for (ServiceReference<CollisionHook> hook : hooks)
+                    {
+                        CollisionHook ch = getFramework().getService(getFramework(), hook);
+                        if (ch != null)
+                        {
+                            int operationType;
+                            Bundle target;
+                            if (isUpdate)
+                            {
+                                operationType = CollisionHook.UPDATING;
+                                target = this;
+                            }
+                            else
+                            {
+                                operationType = CollisionHook.INSTALLING;
+                                target = m_installingBundle == null ? this : m_installingBundle;
+                            }
+
+                            Felix.m_secureAction.invokeBundleCollisionHook(ch, operationType, target,
+                                    shrinkableCollisionCandidates);
+                        }
+                    }
+                }
+            }
+            if (!collisionCanditates.isEmpty())
+            {
+                throw new BundleException(
+                    "Bundle symbolic name and version are not unique: "
+                    + symName + ':' + bundleVersion, BundleException.DUPLICATE_BUNDLE_ERROR);
+            }
+        }
+
+        return revision;
+    }
+
+    synchronized ProtectionDomain getProtectionDomain()
+    {
+        ProtectionDomain pd = null;
+
+        for (int i = m_revisions.size() - 1; (i >= 0) && (pd == null); i--)
+        {
+            pd = ((BundleRevisionImpl) m_revisions.get(i)).getProtectionDomain();
+        }
+
+        return pd;
+    }
+
+    //
+    // Locking related methods.
+    //
+
+    synchronized boolean isLockable()
+    {
+        return (m_lockCount == 0) || (m_lockThread == Thread.currentThread());
+    }
+
+    synchronized Thread getLockingThread()
+    {
+        return m_lockThread;
+    }
+
+    synchronized void lock()
+    {
+        if ((m_lockCount > 0) && (m_lockThread != Thread.currentThread()))
+        {
+            throw new IllegalStateException("Bundle is locked by another thread.");
+        }
+        m_lockCount++;
+        m_lockThread = Thread.currentThread();
+    }
+
+    synchronized void unlock()
+    {
+        if (m_lockCount == 0)
+        {
+            throw new IllegalStateException("Bundle is not locked.");
+        }
+        if ((m_lockCount > 0) && (m_lockThread != Thread.currentThread()))
+        {
+            throw new IllegalStateException("Bundle is locked by another thread.");
+        }
+        m_lockCount--;
+        if (m_lockCount == 0)
+        {
+            m_lockThread = null;
+        }
+    }
+
+    BundleContext _getBundleContext()
+    {
+        return m_context;
+    }
+}
diff --git a/src/main/java/org/apache/felix/framework/BundleProtectionDomain.java b/src/main/java/org/apache/felix/framework/BundleProtectionDomain.java
new file mode 100644
index 0000000..061105c
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/BundleProtectionDomain.java
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework;
+
+import java.lang.ref.WeakReference;
+import java.net.MalformedURLException;
+import java.security.CodeSource;
+import java.security.Permission;
+import java.security.ProtectionDomain;
+import java.security.cert.Certificate;
+
+import org.osgi.framework.wiring.BundleRevision;
+
+public class BundleProtectionDomain extends ProtectionDomain
+{
+    private final WeakReference m_felix;
+    private final WeakReference m_bundle;
+    private final int m_hashCode;
+    private final String m_toString;
+    private final WeakReference m_revision;
+
+    // TODO: SECURITY - This should probably take a revision, not a bundle.
+    BundleProtectionDomain(Felix felix, BundleImpl bundle, Object certificates)
+        throws MalformedURLException
+    {
+        super(
+            new CodeSource(
+                Felix.m_secureAction.createURL(
+                    Felix.m_secureAction.createURL(null, "location:", new FakeURLStreamHandler()),
+                    bundle._getLocation().startsWith("reference:") ? 
+                        bundle._getLocation().substring("reference:".length()) : 
+                        bundle._getLocation(),
+                    new FakeURLStreamHandler()
+                    ),
+                (Certificate[]) certificates),
+            null, null, null);
+        m_felix = new WeakReference(felix);
+        m_bundle = new WeakReference(bundle);
+        m_revision = new WeakReference(bundle.adapt(BundleRevisionImpl.class));
+        m_hashCode = bundle.hashCode();
+        m_toString = "[" + bundle + "]";
+    }
+
+    BundleRevision getRevision()
+    {
+        return (BundleRevision) m_revision.get();
+    }
+
+    public boolean implies(Permission permission)
+    {
+        Felix felix = (Felix) m_felix.get();
+        return (felix != null) ?
+            felix.impliesBundlePermission(this, permission, false) : false;
+    }
+
+    boolean superImplies(Permission permission)
+    {
+        return super.implies(permission);
+    }
+
+    public boolean impliesDirect(Permission permission)
+    {
+        Felix felix = (Felix) m_felix.get();
+        return (felix != null) ?
+            felix.impliesBundlePermission(this, permission, true) : false;
+    }
+
+    BundleImpl getBundle()
+    {
+        return (BundleImpl) m_bundle.get();
+    }
+
+    public int hashCode()
+    {
+        return m_hashCode;
+    }
+
+    public boolean equals(Object other)
+    {
+        if ((other == null) || (other.getClass() != BundleProtectionDomain.class))
+        {
+            return false;
+        }
+        if (m_hashCode != other.hashCode())
+        {
+            return false;
+        }
+        return m_bundle.get() == ((BundleProtectionDomain) other).m_bundle.get();
+    }
+
+    public String toString()
+    {
+        return m_toString;
+    }
+}
diff --git a/src/main/java/org/apache/felix/framework/BundleRevisionDependencies.java b/src/main/java/org/apache/felix/framework/BundleRevisionDependencies.java
new file mode 100644
index 0000000..9cdb9d8
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/BundleRevisionDependencies.java
@@ -0,0 +1,319 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import org.apache.felix.framework.util.Util;
+import org.apache.felix.framework.wiring.BundleWireImpl;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.wiring.BundleCapability;
+import org.osgi.framework.wiring.BundleRevision;
+import org.osgi.framework.wiring.BundleRevisions;
+import org.osgi.framework.wiring.BundleWire;
+import org.osgi.framework.wiring.BundleWiring;
+
+class BundleRevisionDependencies
+{
+    private final Map<BundleRevision, Map<BundleCapability, Set<BundleWire>>>
+        m_dependentsMap = new HashMap<BundleRevision, Map<BundleCapability, Set<BundleWire>>>();
+
+    public synchronized void addDependent(BundleWire bw)
+    {
+        BundleRevision provider = bw.getProvider();
+        Map<BundleCapability, Set<BundleWire>> caps =
+            m_dependentsMap.get(provider);
+        if (caps == null)
+        {
+            caps = new HashMap<BundleCapability, Set<BundleWire>>();
+            m_dependentsMap.put(provider, caps);
+        }
+        Set<BundleWire> dependents = caps.get(bw.getCapability());
+        if (dependents == null)
+        {
+            dependents = new HashSet<BundleWire>();
+            caps.put(bw.getCapability(), dependents);
+        }
+        dependents.add(bw);
+    }
+
+/*
+    public synchronized void removeDependent(
+        BundleRevision provider, BundleCapability cap, BundleRevision requirer)
+    {
+        Map<BundleCapability, Set<BundleRevision>> caps = m_dependentsMap.get(provider);
+        if (caps != null)
+        {
+            Set<BundleRevision> dependents = caps.get(cap);
+            if (dependents == null)
+            {
+                dependents.remove(requirer);
+                if (dependents.isEmpty())
+                {
+                    caps.remove(cap);
+                    if (caps.isEmpty())
+                    {
+                        m_dependentsMap.remove(provider);
+                    }
+                }
+            }
+        }
+    }
+*/
+    public synchronized void removeDependents(BundleRevision provider)
+    {
+        m_dependentsMap.remove(provider);
+    }
+
+    public synchronized Map<BundleCapability, Set<BundleWire>>
+        getDependents(BundleRevision provider)
+    {
+        return m_dependentsMap.get(provider);
+    }
+
+    public synchronized boolean hasDependents(BundleRevision revision)
+    {
+        // We have to special case fragments, since their dependencies
+        // are actually reversed (i.e., they require a host, but then
+        // the host ends up dependent on them at run time).
+        if (Util.isFragment(revision)
+            && (revision.getWiring() != null)
+            && !revision.getWiring().getRequiredWires(null).isEmpty())
+        {
+            return true;
+        }
+        else if (m_dependentsMap.containsKey(revision))
+        {
+            return true;
+        }
+        return false;
+    }
+
+    public synchronized boolean hasDependents(Bundle bundle)
+    {
+        List<BundleRevision> revisions = bundle.adapt(BundleRevisions.class).getRevisions();
+        for (BundleRevision revision : revisions)
+        {
+            if (hasDependents(revision))
+            {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public synchronized List<BundleWire> getProvidedWires(
+        BundleRevision revision, String namespace)
+    {
+        List<BundleWire> providedWires = new ArrayList<BundleWire>();
+
+        Map<BundleCapability, Set<BundleWire>> providedCaps =
+            m_dependentsMap.get(revision);
+        if (providedCaps != null)
+        {
+            // The wires are supposed to be in declared capability order, so
+            // get the capability list from the revision's wiring, which is
+            // in declared order (including fragments), and use it to create
+            // the provided wire list in declared order.
+            BundleWiring wiring = revision.getWiring();
+            if (wiring != null)
+            {
+                List<BundleCapability> resolvedCaps = wiring.getCapabilities(namespace);
+                for (BundleCapability resolvedCap : resolvedCaps)
+                {
+                    Set<BundleWire> dependentWires = providedCaps.get(resolvedCap);
+                    if (dependentWires != null)
+                    {
+                        providedWires.addAll(dependentWires);
+                    }
+                }
+            }
+        }
+
+        return providedWires;
+    }
+
+    public synchronized Set<Bundle> getDependentBundles(Bundle bundle)
+    {
+        Set<Bundle> result = new HashSet<Bundle>();
+
+        List<BundleRevision> revisions = bundle.adapt(BundleRevisions.class).getRevisions();
+        for (BundleRevision revision : revisions)
+        {
+            // We need to special case fragments,
+            // since their dependents are their hosts.
+            if (Util.isFragment(revision))
+            {
+                BundleWiring wiring = revision.getWiring();
+                if (wiring != null)
+                {
+                    for (BundleWire bw : wiring.getRequiredWires(null))
+                    {
+                        result.add(((BundleWireImpl) bw).getProvider().getBundle());
+                    }
+                }
+            }
+            else
+            {
+                Map<BundleCapability, Set<BundleWire>> caps =
+                    m_dependentsMap.get(revision);
+                if (caps != null)
+                {
+                    for (Entry<BundleCapability, Set<BundleWire>> entry : caps.entrySet())
+                    {
+                        for (BundleWire dependentWire : entry.getValue())
+                        {
+                            result.add(dependentWire.getRequirer().getBundle());
+                        }
+                    }
+                }
+            }
+        }
+
+        return result;
+    }
+
+    public synchronized Set<Bundle> getImportingBundles(
+        Bundle exporter, BundleCapability exportCap)
+    {
+        // Create set for storing importing bundles.
+        Set<Bundle> result = new HashSet<Bundle>();
+
+        // Get exported package name.
+        String pkgName = (String)
+            exportCap.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE);
+
+        // Get all importers and requirers for all revisions of the bundle.
+        // The spec says that require-bundle should be returned with importers.
+        for (BundleRevision revision : exporter.adapt(BundleRevisions.class).getRevisions())
+        {
+            Map<BundleCapability, Set<BundleWire>>
+                caps = m_dependentsMap.get(revision);
+            if (caps != null)
+            {
+                for (Entry<BundleCapability, Set<BundleWire>> entry : caps.entrySet())
+                {
+                    BundleCapability cap = entry.getKey();
+                    if ((cap.getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE)
+                        && cap.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE)
+                            .equals(pkgName))
+                        || cap.getNamespace().equals(BundleRevision.BUNDLE_NAMESPACE))
+                    {
+                        for (BundleWire dependentWire : entry.getValue())
+                        {
+                            result.add(dependentWire.getRequirer().getBundle());
+                        }
+                    }
+                }
+            }
+        }
+
+        // Return the results.
+        return result;
+    }
+
+    public synchronized Set<Bundle> getRequiringBundles(Bundle bundle)
+    {
+        // Create set for storing requiring bundles.
+        Set<Bundle> result = new HashSet<Bundle>();
+
+        // Get all requirers for all revisions of the bundle.
+        for (BundleRevision revision : bundle.adapt(BundleRevisions.class).getRevisions())
+        {
+            Map<BundleCapability, Set<BundleWire>>
+                caps = m_dependentsMap.get(revision);
+            if (caps != null)
+            {
+                for (Entry<BundleCapability, Set<BundleWire>> entry : caps.entrySet())
+                {
+                    if (entry.getKey().getNamespace()
+                        .equals(BundleRevision.BUNDLE_NAMESPACE))
+                    {
+                        for (BundleWire dependentWire : entry.getValue())
+                        {
+                            result.add(dependentWire.getRequirer().getBundle());
+                        }
+                    }
+                }
+            }
+        }
+
+        // Return the results.
+        return result;
+    }
+
+    public synchronized void removeDependencies(Bundle bundle)
+    {
+        List<BundleRevision> revs = bundle.adapt(BundleRevisions.class).getRevisions();
+        for (BundleRevision rev : revs)
+        {
+            BundleWiring wiring = rev.getWiring();
+            if (wiring != null)
+            {
+                for (BundleWire bw : wiring.getRequiredWires(null))
+                {
+                    Map<BundleCapability, Set<BundleWire>> caps =
+                        m_dependentsMap.get(bw.getProvider());
+                    if (caps != null)
+                    {
+                        List<BundleCapability> gc = new ArrayList<BundleCapability>();
+                        for (Entry<BundleCapability, Set<BundleWire>> entry
+                            : caps.entrySet())
+                        {
+                            entry.getValue().remove(bw);
+                            if (entry.getValue().isEmpty())
+                            {
+                                gc.add(entry.getKey());
+                            }
+                        }
+                        for (BundleCapability cap : gc)
+                        {
+                            caps.remove(cap);
+                        }
+                        if (caps.isEmpty())
+                        {
+                            m_dependentsMap.remove(bw.getProvider());
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    public synchronized void dump()
+    {
+/*
+System.out.println("DEPENDENTS:");
+        for (Entry<BundleRevision, Map<BundleCapability, Set<BundleRevision>>> entry
+            : m_dependentsMap.entrySet())
+        {
+            System.out.println("Revision " + entry.getKey() + " DEPS:");
+            for (Entry<BundleCapability, Set<BundleRevision>> capEntry : entry.getValue().entrySet())
+            {
+                System.out.println("   " + capEntry.getKey() + " <-- " + capEntry.getValue());
+            }
+        }
+*/
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/felix/framework/BundleRevisionImpl.java b/src/main/java/org/apache/felix/framework/BundleRevisionImpl.java
new file mode 100644
index 0000000..bc8de71
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/BundleRevisionImpl.java
@@ -0,0 +1,674 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.security.ProtectionDomain;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Map;
+import org.apache.felix.framework.cache.Content;
+import org.apache.felix.framework.util.FelixConstants;
+import org.apache.felix.framework.util.SecureAction;
+import org.apache.felix.framework.util.Util;
+import org.apache.felix.framework.util.manifestparser.ManifestParser;
+import org.apache.felix.framework.util.manifestparser.R4Library;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Version;
+import org.osgi.framework.wiring.BundleCapability;
+import org.osgi.framework.wiring.BundleRequirement;
+import org.osgi.framework.wiring.BundleRevision;
+import org.osgi.framework.wiring.BundleWiring;
+import org.osgi.resource.Capability;
+import org.osgi.resource.Requirement;
+import org.osgi.resource.Resource;
+
+public class BundleRevisionImpl implements BundleRevision, Resource
+{
+    public final static int EAGER_ACTIVATION = 0;
+    public final static int LAZY_ACTIVATION = 1;
+
+    private final String m_id;
+    private final Map m_headerMap;
+
+    private final String m_manifestVersion;
+    private final boolean m_isExtension;
+    private final boolean m_isFragment;
+    private final String m_symbolicName;
+    private final Version m_version;
+
+    private final List<BundleCapability> m_declaredCaps;
+    private final List<BundleRequirement> m_declaredReqs;
+    private final List<R4Library> m_declaredNativeLibs;
+    private final int m_declaredActivationPolicy;
+    private final List<String> m_activationIncludes;
+    private final List<String> m_activationExcludes;
+
+    private final Bundle m_bundle;
+
+    private Content m_content;
+    private List<Content> m_contentPath;
+    private ProtectionDomain m_protectionDomain = null;
+    private final static SecureAction m_secureAction = new SecureAction();
+
+    // Bundle wiring when resolved.
+    private volatile BundleWiringImpl m_wiring = null;
+
+    /**
+     * This constructor is used by the extension manager, since it needs
+     * a constructor that does not throw an exception.
+     * @param logger
+     * @param bundle
+     * @param id
+     * @param bootPkgs
+     * @param bootPkgWildcards
+     * @throws org.osgi.framework.BundleException
+     */
+    public BundleRevisionImpl(Bundle bundle, String id)
+    {
+        m_bundle = bundle;
+        m_id = id;
+        m_headerMap = null;
+        m_content = null;
+        m_manifestVersion = "";
+        m_symbolicName = null;
+        m_isExtension = false;
+        m_isFragment = false;
+        m_version = null;
+        m_declaredCaps = Collections.EMPTY_LIST;
+        m_declaredReqs = Collections.EMPTY_LIST;
+        m_declaredNativeLibs = null;
+        m_declaredActivationPolicy = EAGER_ACTIVATION;
+        m_activationExcludes = null;
+        m_activationIncludes = null;
+    }
+
+    BundleRevisionImpl(
+        Bundle bundle, String id, Map headerMap, Content content)
+        throws BundleException
+    {
+        m_bundle = bundle;
+        m_id = id;
+        m_headerMap = headerMap;
+        m_content = content;
+
+        ManifestParser mp = new ManifestParser(
+            ((BundleImpl) bundle).getFramework().getLogger(),
+            ((BundleImpl) bundle).getFramework().getConfig(),
+            this,
+            m_headerMap);
+
+        // Record some of the parsed metadata. Note, if this is an extension
+        // bundle it's exports are removed, since they will be added to the
+        // system bundle directly later on.
+        m_manifestVersion = mp.getManifestVersion();
+        m_version = mp.getBundleVersion();
+        m_declaredCaps = mp.isExtension() ? Collections.EMPTY_LIST : mp.getCapabilities();
+        m_declaredReqs = mp.getRequirements();
+        m_declaredNativeLibs = mp.getLibraries();
+        m_declaredActivationPolicy = mp.getActivationPolicy();
+        m_activationExcludes = (mp.getActivationExcludeDirective() == null)
+            ? null
+            : ManifestParser.parseDelimitedString(mp.getActivationExcludeDirective(), ",");
+        m_activationIncludes = (mp.getActivationIncludeDirective() == null)
+            ? null
+            : ManifestParser.parseDelimitedString(mp.getActivationIncludeDirective(), ",");
+        m_symbolicName = mp.getSymbolicName();
+        m_isExtension = mp.isExtension();
+        m_isFragment = m_headerMap.containsKey(Constants.FRAGMENT_HOST);
+    }
+
+    static SecureAction getSecureAction()
+    {
+        return m_secureAction;
+    }
+
+    int getDeclaredActivationPolicy()
+    {
+        return m_declaredActivationPolicy;
+    }
+
+    boolean isActivationTrigger(String pkgName)
+    {
+        if ((m_activationIncludes == null) && (m_activationExcludes == null))
+        {
+            return true;
+        }
+
+        // If there are no include filters then all classes are included
+        // by default, otherwise try to find one match.
+        boolean included = (m_activationIncludes == null);
+        for (int i = 0;
+            (!included) && (m_activationIncludes != null) && (i < m_activationIncludes.size());
+            i++)
+        {
+            included = m_activationIncludes.get(i).equals(pkgName);
+        }
+
+        // If there are no exclude filters then no classes are excluded
+        // by default, otherwise try to find one match.
+        boolean excluded = false;
+        for (int i = 0;
+            (!excluded) && (m_activationExcludes != null) && (i < m_activationExcludes.size());
+            i++)
+        {
+            excluded = m_activationExcludes.get(i).equals(pkgName);
+        }
+        return included && !excluded;
+    }
+
+    //
+    // BundleRevision methods.
+    //
+
+    public String getSymbolicName()
+    {
+        return m_symbolicName;
+    }
+
+    public Version getVersion()
+    {
+        return m_version;
+    }
+
+    public List<Capability> getCapabilities(String namespace)
+    {
+        return asCapabilityList(getDeclaredCapabilities(namespace));
+    }
+
+    static List<Capability> asCapabilityList(List reqs)
+    {
+        return (List<Capability>) reqs;
+    }
+
+    public List<BundleCapability> getDeclaredCapabilities(String namespace)
+    {
+        List<BundleCapability> result = m_declaredCaps;
+        if (namespace != null)
+        {
+            result = new ArrayList<BundleCapability>();
+            for (BundleCapability cap : m_declaredCaps)
+            {
+                if (cap.getNamespace().equals(namespace))
+                {
+                    result.add(cap);
+                }
+            }
+        }
+        return result;
+    }
+
+    public List<Requirement> getRequirements(String namespace)
+    {
+        return asRequirementList(getDeclaredRequirements(namespace));
+    }
+
+    static List<Requirement> asRequirementList(List reqs)
+    {
+        return (List<Requirement>) reqs;
+    }
+
+    public List<BundleRequirement> getDeclaredRequirements(String namespace)
+    {
+        List<BundleRequirement> result = m_declaredReqs;
+        if (namespace != null)
+        {
+            result = new ArrayList<BundleRequirement>();
+            for (BundleRequirement req : m_declaredReqs)
+            {
+                if (req.getNamespace().equals(namespace))
+                {
+                    result.add(req);
+                }
+            }
+        }
+        return result;
+    }
+
+    public int getTypes()
+    {
+        return (getManifestVersion().equals("2") && m_isFragment) ? BundleRevision.TYPE_FRAGMENT : 0;
+    }
+
+    public BundleWiring getWiring()
+    {
+        return m_wiring;
+    }
+
+    public Bundle getBundle()
+    {
+        return m_bundle;
+    }
+
+    //
+    // Implementating details.
+    //
+
+    public Map getHeaders()
+    {
+        return m_headerMap;
+    }
+
+    public boolean isExtension()
+    {
+        return m_isExtension;
+    }
+
+    public String getManifestVersion()
+    {
+        return m_manifestVersion;
+    }
+
+    public List<R4Library> getDeclaredNativeLibraries()
+    {
+        return m_declaredNativeLibs;
+    }
+
+    public String getId()
+    {
+        return m_id;
+    }
+
+    public synchronized void resolve(BundleWiringImpl wiring)
+    {
+        if (m_wiring != null)
+        {
+            m_wiring.dispose();
+            m_wiring = null;
+        }
+
+        if (wiring != null)
+        {
+            // If the wiring has fragments, then close the old content path,
+            // since it'll need to be recalculated to include fragments.
+            if (!Util.getFragments(wiring).isEmpty())
+            {
+                for (int i = 0; (m_contentPath != null) && (i < m_contentPath.size()); i++)
+                {
+                    // Don't close this module's content, if it is on the content path.
+                    if (m_content != m_contentPath.get(i))
+                    {
+                        m_contentPath.get(i).close();
+                    }
+                }
+                m_contentPath = null;
+            }
+
+            m_wiring = wiring;
+        }
+    }
+
+    public synchronized void setProtectionDomain(ProtectionDomain pd)
+    {
+        m_protectionDomain = pd;
+    }
+
+    public synchronized ProtectionDomain getProtectionDomain()
+    {
+        return m_protectionDomain;
+    }
+
+    //
+    // Content access methods.
+    //
+
+    public synchronized Content getContent()
+    {
+        return m_content;
+    }
+
+    synchronized void resetContent(Content content)
+    {
+        m_content = content;
+    }
+
+    synchronized List<Content> getContentPath()
+    {
+        if (m_contentPath == null)
+        {
+            try
+            {
+                m_contentPath = initializeContentPath();
+            }
+            catch (Exception ex)
+            {
+                ((BundleImpl) m_bundle).getFramework().getLogger().log(
+                    m_bundle, Logger.LOG_ERROR, "Unable to get module class path.", ex);
+            }
+        }
+        return m_contentPath;
+    }
+
+    private List<Content> initializeContentPath() throws Exception
+    {
+        List<Content> contentList = new ArrayList();
+        calculateContentPath(this, getContent(), contentList, true);
+
+        List<BundleRevision> fragments = null;
+        List<Content> fragmentContents = null;
+        if (m_wiring != null)
+        {
+            fragments = Util.getFragments(m_wiring);
+            fragmentContents = m_wiring.getFragmentContents();
+        }
+        if (fragments != null)
+        {
+            for (int i = 0; i < fragments.size(); i++)
+            {
+                calculateContentPath(
+                    fragments.get(i), fragmentContents.get(i), contentList, false);
+            }
+        }
+        return contentList;
+    }
+
+    private List calculateContentPath(
+        BundleRevision revision, Content content, List<Content> contentList,
+        boolean searchFragments)
+        throws Exception
+    {
+        // Creating the content path entails examining the bundle's
+        // class path to determine whether the bundle JAR file itself
+        // is on the bundle's class path and then creating content
+        // objects for everything on the class path.
+
+        // Create a list to contain the content path for the specified content.
+        List localContentList = new ArrayList();
+
+        // Find class path meta-data.
+        String classPath = (String) ((BundleRevisionImpl) revision)
+            .getHeaders().get(FelixConstants.BUNDLE_CLASSPATH);
+        // Parse the class path into strings.
+        List<String> classPathStrings = ManifestParser.parseDelimitedString(
+            classPath, FelixConstants.CLASS_PATH_SEPARATOR);
+
+        if (classPathStrings == null)
+        {
+            classPathStrings = new ArrayList<String>(0);
+        }
+
+        // Create the bundles class path.
+        for (int i = 0; i < classPathStrings.size(); i++)
+        {
+            // Remove any leading slash, since all bundle class path
+            // entries are relative to the root of the bundle.
+            classPathStrings.set(i, (classPathStrings.get(i).startsWith("/"))
+                ? classPathStrings.get(i).substring(1)
+                : classPathStrings.get(i));
+
+            // Check for the bundle itself on the class path.
+            if (classPathStrings.get(i).equals(FelixConstants.CLASS_PATH_DOT))
+            {
+                localContentList.add(content);
+            }
+            else
+            {
+                // Try to find the embedded class path entry in the current
+                // content.
+                Content embeddedContent = content.getEntryAsContent(classPathStrings.get(i));
+                // If the embedded class path entry was not found, it might be
+                // in one of the fragments if the current content is the bundle,
+                // so try to search the fragments if necessary.
+                List<Content> fragmentContents = (m_wiring == null)
+                    ? null : m_wiring.getFragmentContents();
+                for (int fragIdx = 0;
+                    searchFragments && (embeddedContent == null)
+                        && (fragmentContents != null) && (fragIdx < fragmentContents.size());
+                    fragIdx++)
+                {
+                    embeddedContent =
+                        fragmentContents.get(fragIdx).getEntryAsContent(classPathStrings.get(i));
+                }
+                // If we found the embedded content, then add it to the
+                // class path content list.
+                if (embeddedContent != null)
+                {
+                    localContentList.add(embeddedContent);
+                }
+                else
+                {
+// TODO: FRAMEWORK - Per the spec, this should fire a FrameworkEvent.INFO event;
+//       need to create an "Eventer" class like "Logger" perhaps.
+                    ((BundleImpl) m_bundle).getFramework().getLogger().log(
+                        getBundle(), Logger.LOG_INFO,
+                        "Class path entry not found: "
+                        + classPathStrings.get(i));
+                }
+            }
+        }
+
+        // If there is nothing on the class path, then include
+        // "." by default, as per the spec.
+        if (localContentList.isEmpty())
+        {
+            localContentList.add(content);
+        }
+
+        // Now add the local contents to the global content list and return it.
+        contentList.addAll(localContentList);
+        return contentList;
+    }
+
+    URL getResourceLocal(String name)
+    {
+        URL url = null;
+
+        // Remove leading slash, if present, but special case
+        // "/" so that it returns a root URL...this isn't very
+        // clean or meaninful, but the Spring guys want it.
+        if (name.equals("/"))
+        {
+            // Just pick a class path index since it doesn't really matter.
+            url = createURL(1, name);
+        }
+        else if (name.startsWith("/"))
+        {
+            name = name.substring(1);
+        }
+
+        // Check the module class path.
+        List<Content> contentPath = getContentPath();
+        for (int i = 0;
+            (url == null) &&
+            (i < contentPath.size()); i++)
+        {
+            if (contentPath.get(i).hasEntry(name))
+            {
+                url = createURL(i + 1, name);
+            }
+        }
+
+        return url;
+    }
+
+    Enumeration getResourcesLocal(String name)
+    {
+        List l = new ArrayList();
+
+        // Special case "/" so that it returns a root URLs for
+        // each bundle class path entry...this isn't very
+        // clean or meaningful, but the Spring guys want it.
+        final List<Content> contentPath = getContentPath();
+        if (name.equals("/"))
+        {
+            for (int i = 0; i < contentPath.size(); i++)
+            {
+                l.add(createURL(i + 1, name));
+            }
+        }
+        else
+        {
+            // Remove leading slash, if present.
+            if (name.startsWith("/"))
+            {
+                name = name.substring(1);
+            }
+
+            // Check the module class path.
+            for (int i = 0; i < contentPath.size(); i++)
+            {
+                if (contentPath.get(i).hasEntry(name))
+                {
+                    // Use the class path index + 1 for creating the path so
+                    // that we can differentiate between module content URLs
+                    // (where the path will start with 0) and module class
+                    // path URLs.
+                    l.add(createURL(i + 1, name));
+                }
+            }
+        }
+
+        return Collections.enumeration(l);
+    }
+
+    // TODO: API: Investigate how to handle this better, perhaps we need
+    // multiple URL policies, one for content -- one for class path.
+    public URL getEntry(String name)
+    {
+        URL url = null;
+
+        // Check for the special case of "/", which represents
+        // the root of the bundle according to the spec.
+        if (name.equals("/"))
+        {
+            url = createURL(0, "/");
+        }
+
+        if (url == null)
+        {
+            // Remove leading slash, if present.
+            if (name.startsWith("/"))
+            {
+                name = name.substring(1);
+            }
+
+            // Check the module content.
+            if (getContent().hasEntry(name))
+            {
+                // Module content URLs start with 0, whereas module
+                // class path URLs start with the index into the class
+                // path + 1.
+                url = createURL(0, name);
+            }
+        }
+
+        return url;
+    }
+
+    public boolean hasInputStream(int index, String urlPath)
+    {
+        if (urlPath.startsWith("/"))
+        {
+            urlPath = urlPath.substring(1);
+        }
+        if (index == 0)
+        {
+            return getContent().hasEntry(urlPath);
+        }
+        return getContentPath().get(index - 1).hasEntry(urlPath);
+    }
+
+    public InputStream getInputStream(int index, String urlPath)
+        throws IOException
+    {
+        if (urlPath.startsWith("/"))
+        {
+            urlPath = urlPath.substring(1);
+        }
+        if (index == 0)
+        {
+            return getContent().getEntryAsStream(urlPath);
+        }
+        return getContentPath().get(index - 1).getEntryAsStream(urlPath);
+    }
+
+    public URL getLocalURL(int index, String urlPath)
+    {
+        if (urlPath.startsWith("/"))
+        {
+            urlPath = urlPath.substring(1);
+        }
+        if (index == 0)
+        {
+            return getContent().getEntryAsURL(urlPath);
+        }
+        return getContentPath().get(index - 1).getEntryAsURL(urlPath);
+    }
+
+    private URL createURL(int port, String path)
+    {
+        // Add a slash if there is one already, otherwise
+        // the is no slash separating the host from the file
+        // in the resulting URL.
+        if (!path.startsWith("/"))
+        {
+            path = "/" + path;
+        }
+
+        try
+        {
+            return m_secureAction.createURL(null,
+                FelixConstants.BUNDLE_URL_PROTOCOL + "://" +
+                m_id + ":" + port + path,
+                ((BundleImpl) getBundle()).getFramework().getBundleStreamHandler());
+        }
+        catch (MalformedURLException ex)
+        {
+            ((BundleImpl) m_bundle).getFramework().getLogger().log(
+                m_bundle,
+                Logger.LOG_ERROR,
+                "Unable to create resource URL.",
+                ex);
+        }
+        return null;
+    }
+
+    synchronized void close()
+    {
+        try
+        {
+            resolve(null);
+        }
+        catch (Exception ex)
+        {
+            ((BundleImpl) m_bundle).getFramework().getLogger().log(
+                Logger.LOG_ERROR, "Error releasing revision: " + ex.getMessage(), ex);
+        }
+        m_content.close();
+        m_content = null;
+        for (int i = 0; (m_contentPath != null) && (i < m_contentPath.size()); i++)
+        {
+            m_contentPath.get(i).close();
+        }
+        m_contentPath = null;
+    }
+
+    @Override
+    public String toString()
+    {
+        return m_id;
+    }
+}
diff --git a/src/main/java/org/apache/felix/framework/BundleWiringImpl.java b/src/main/java/org/apache/felix/framework/BundleWiringImpl.java
new file mode 100644
index 0000000..beecd41
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/BundleWiringImpl.java
@@ -0,0 +1,2870 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework;
+
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.security.SecureClassLoader;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.TreeMap;
+import java.util.TreeSet;
+
+import org.apache.felix.framework.cache.Content;
+import org.apache.felix.framework.cache.JarContent;
+import org.apache.felix.framework.capabilityset.SimpleFilter;
+import org.apache.felix.framework.resolver.ResolveException;
+import org.apache.felix.framework.resolver.ResourceNotFoundException;
+import org.apache.felix.framework.util.CompoundEnumeration;
+import org.apache.felix.framework.util.FelixConstants;
+import org.apache.felix.framework.util.ImmutableList;
+import org.apache.felix.framework.util.SecurityManagerEx;
+import org.apache.felix.framework.util.Util;
+import org.apache.felix.framework.util.manifestparser.ManifestParser;
+import org.apache.felix.framework.util.manifestparser.R4Library;
+import org.apache.felix.framework.wiring.BundleRequirementImpl;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.BundleReference;
+import org.osgi.framework.CapabilityPermission;
+import org.osgi.framework.Constants;
+import org.osgi.framework.FrameworkEvent;
+import org.osgi.framework.PackagePermission;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.hooks.weaving.WeavingException;
+import org.osgi.framework.hooks.weaving.WeavingHook;
+import org.osgi.framework.namespace.IdentityNamespace;
+import org.osgi.framework.wiring.BundleCapability;
+import org.osgi.framework.wiring.BundleRequirement;
+import org.osgi.framework.wiring.BundleRevision;
+import org.osgi.framework.wiring.BundleWire;
+import org.osgi.framework.wiring.BundleWiring;
+import org.osgi.resource.Capability;
+import org.osgi.resource.Requirement;
+import org.osgi.resource.Wire;
+
+public class BundleWiringImpl implements BundleWiring
+{
+    public final static int LISTRESOURCES_DEBUG = 1048576;
+
+    public final static int EAGER_ACTIVATION = 0;
+    public final static int LAZY_ACTIVATION = 1;
+
+    private final Logger m_logger;
+    private final Map m_configMap;
+    private final StatefulResolver m_resolver;
+    private final BundleRevisionImpl m_revision;
+    private final List<BundleRevision> m_fragments;
+    // Wire list is copy-on-write since it may change due to
+    // dynamic imports.
+    private volatile List<BundleWire> m_wires;
+    // Imported package map is copy-on-write since it may change
+    // due to dynamic imports.
+    private volatile Map<String, BundleRevision> m_importedPkgs;
+    private final Map<String, List<BundleRevision>> m_requiredPkgs;
+    private final List<BundleCapability> m_resolvedCaps;
+    private final Map<String, List<List<String>>> m_includedPkgFilters;
+    private final Map<String, List<List<String>>> m_excludedPkgFilters;
+    private final List<BundleRequirement> m_resolvedReqs;
+    private final List<R4Library> m_resolvedNativeLibs;
+    private final List<Content> m_fragmentContents;
+
+    private volatile List<BundleRequirement> m_wovenReqs = null;
+
+    private BundleClassLoader m_classLoader;
+
+    // Bundle-specific class loader for boot delegation.
+    private final ClassLoader m_bootClassLoader;
+    // Default class loader for boot delegation.
+    private final static ClassLoader m_defBootClassLoader;
+
+    // Statically define the default class loader for boot delegation.
+    static
+    {
+        ClassLoader cl = null;
+        try
+        {
+            Constructor ctor = BundleRevisionImpl.getSecureAction().getDeclaredConstructor(
+                SecureClassLoader.class, new Class[] { ClassLoader.class });
+            BundleRevisionImpl.getSecureAction().setAccesssible(ctor);
+            cl = (ClassLoader) BundleRevisionImpl.getSecureAction().invoke(
+                ctor, new Object[] { null });
+        }
+        catch (Throwable ex)
+        {
+            // On Android we get an exception if we set the parent class loader
+            // to null, so we will work around that case by setting the parent
+            // class loader to the system class loader in getClassLoader() below.
+            cl = null;
+            System.err.println("Problem creating boot delegation class loader: " + ex);
+        }
+        m_defBootClassLoader = cl;
+    }
+
+    // Boolean flag to enable/disable implicit boot delegation.
+    private final boolean m_implicitBootDelegation;
+    // Boolean flag to enable/disable local URLs.
+    private final boolean m_useLocalURLs;
+
+    // Re-usable security manager for accessing class context.
+    private static SecurityManagerEx m_sm = new SecurityManagerEx();
+
+    // Thread local to detect class loading cycles.
+    private final ThreadLocal m_cycleCheck = new ThreadLocal();
+
+    // Thread local to keep track of deferred activation.
+    private static final ThreadLocal m_deferredActivation = new ThreadLocal();
+
+    // Flag indicating whether we are on an old JVM or not.
+    private volatile static boolean m_isPreJava5 = false;
+
+    // Flag indicating whether this wiring has been disposed.
+    private volatile boolean m_isDisposed = false;
+
+    BundleWiringImpl(
+        Logger logger, Map configMap, StatefulResolver resolver,
+        BundleRevisionImpl revision, List<BundleRevision> fragments,
+        List<BundleWire> wires,
+        Map<String, BundleRevision> importedPkgs,
+        Map<String, List<BundleRevision>> requiredPkgs)
+        throws Exception
+    {
+        m_logger = logger;
+        m_configMap = configMap;
+        m_resolver = resolver;
+        m_revision = revision;
+        m_importedPkgs = importedPkgs;
+        m_requiredPkgs = requiredPkgs;
+        m_wires = ImmutableList.newInstance(wires);
+
+        // We need to sort the fragments and add ourself as a dependent of each one.
+        // We also need to create an array of fragment contents to attach to our
+        // content path.
+        List<Content> fragmentContents = null;
+        if (fragments != null)
+        {
+            // Sort fragments according to ID order, if necessary.
+            // Note that this sort order isn't 100% correct since
+            // it uses a string, but it is likely close enough and
+            // avoids having to create more objects.
+            if (fragments.size() > 1)
+            {
+                SortedMap<String, BundleRevision> sorted = new TreeMap<String, BundleRevision>();
+                for (BundleRevision f : fragments)
+                {
+                    sorted.put(((BundleRevisionImpl) f).getId(), f);
+                }
+                fragments = new ArrayList(sorted.values());
+            }
+            fragmentContents = new ArrayList<Content>(fragments.size());
+            for (int i = 0; (fragments != null) && (i < fragments.size()); i++)
+            {
+                fragmentContents.add(
+                    ((BundleRevisionImpl) fragments.get(i)).getContent()
+                        .getEntryAsContent(FelixConstants.CLASS_PATH_DOT));
+            }
+        }
+        m_fragments = fragments;
+        m_fragmentContents = fragmentContents;
+
+        // Calculate resolved list of requirements, which includes:
+        // 1. All requirements for which we have a wire.
+        // 2. All dynamic imports from the host and any fragments.
+        // Also create set of imported packages so we can eliminate any
+        // substituted exports from our resolved capabilities.
+        Set<String> imports = new HashSet<String>();
+        List<BundleRequirement> reqList = new ArrayList<BundleRequirement>();
+        // First add resolved requirements from wires.
+        for (BundleWire bw : wires)
+        {
+            // Fragments may have multiple wires for the same requirement, so we
+            // need to check for and avoid duplicates in that case.
+            if (!bw.getRequirement().getNamespace().equals(BundleRevision.HOST_NAMESPACE)
+                || !reqList.contains(bw.getRequirement()))
+            {
+                reqList.add(bw.getRequirement());
+                if (bw.getRequirement().getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE))
+                {
+                    imports.add((String)
+                        bw.getCapability().getAttributes().get(BundleRevision.PACKAGE_NAMESPACE));
+                }
+            }
+        }
+        // Next add dynamic requirements from host.
+        for (BundleRequirement req : m_revision.getDeclaredRequirements(null))
+        {
+            if (req.getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE))
+            {
+                String resolution = req.getDirectives().get(Constants.RESOLUTION_DIRECTIVE);
+                if ((resolution != null) && (resolution.equals("dynamic")))
+                {
+                    reqList.add(req);
+                }
+            }
+        }
+        // Finally, add dynamic requirements from fragments.
+        if (m_fragments != null)
+        {
+            for (BundleRevision fragment : m_fragments)
+            {
+                for (BundleRequirement req : fragment.getDeclaredRequirements(null))
+                {
+                    if (req.getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE))
+                    {
+                        String resolution = req.getDirectives().get(Constants.RESOLUTION_DIRECTIVE);
+                        if ((resolution != null) && (resolution.equals("dynamic")))
+                        {
+                            reqList.add(req);
+                        }
+                    }
+                }
+            }
+        }
+        m_resolvedReqs = ImmutableList.newInstance(reqList);
+
+        // Calculate resolved list of capabilities, which includes:
+        // 1. All capabilities from host and any fragments except for exported
+        //    packages that we have an import (i.e., the export was substituted).
+        // 2. For fragments the identity capability only.
+        // And nothing else at this time.
+        boolean isFragment = Util.isFragment(revision);
+        List<BundleCapability> capList = new ArrayList<BundleCapability>();
+        // Also keep track of whether any resolved package capabilities are filtered.
+        Map<String, List<List<String>>> includedPkgFilters =
+            new HashMap<String, List<List<String>>>();
+        Map<String, List<List<String>>> excludedPkgFilters =
+            new HashMap<String, List<List<String>>>();
+
+        if (isFragment)
+        {
+            // This is a fragment, add its identity capability
+            for (BundleCapability cap : m_revision.getDeclaredCapabilities(null))
+            {
+                if (IdentityNamespace.IDENTITY_NAMESPACE.equals(cap.getNamespace()))
+                {
+                    String effective = cap.getDirectives().get(Constants.EFFECTIVE_DIRECTIVE);
+                    if ((effective == null) || (effective.equals(Constants.EFFECTIVE_RESOLVE)))
+                    {
+                        capList.add(cap);
+                    }
+                }
+            }
+        }
+        else
+        {
+            for (BundleCapability cap : m_revision.getDeclaredCapabilities(null))
+            {
+                if (!cap.getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE)
+                    || (cap.getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE)
+                        && !imports.contains(cap.getAttributes()
+                            .get(BundleRevision.PACKAGE_NAMESPACE).toString())))
+                {
+// TODO: OSGi R4.4 - We may need to make this more flexible since in the future it may
+//       be possible to consider other effective values via OBR's Environment.isEffective().
+                    String effective = cap.getDirectives().get(Constants.EFFECTIVE_DIRECTIVE);
+                    if ((effective == null) || (effective.equals(Constants.EFFECTIVE_RESOLVE)))
+                    {
+                        capList.add(cap);
+                        if (cap.getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE))
+                        {
+                            List<List<String>> filters =
+                                parsePkgFilters(cap, Constants.INCLUDE_DIRECTIVE);
+                            if (filters != null)
+                            {
+                                includedPkgFilters.put((String)
+                                    cap.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE),
+                                    filters);
+                            }
+                            filters = parsePkgFilters(cap, Constants.EXCLUDE_DIRECTIVE);
+                            if (filters != null)
+                            {
+                                excludedPkgFilters.put((String)
+                                    cap.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE),
+                                    filters);
+                            }
+                        }
+                    }
+                }
+            }
+            if (m_fragments != null)
+            {
+                for (BundleRevision fragment : m_fragments)
+                {
+                    for (BundleCapability cap : fragment.getDeclaredCapabilities(null))
+                    {
+                        if (IdentityNamespace.IDENTITY_NAMESPACE.equals(cap.getNamespace())) {
+                            // The identity capability is not transferred from the fragment to the bundle
+                            continue;
+                        }
+
+                        if (!cap.getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE)
+                            || (cap.getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE)
+                                && !imports.contains(cap.getAttributes()
+                                    .get(BundleRevision.PACKAGE_NAMESPACE).toString())))
+                        {
+// TODO: OSGi R4.4 - We may need to make this more flexible since in the future it may
+//       be possible to consider other effective values via OBR's Environment.isEffective().
+                            String effective = cap.getDirectives().get(Constants.EFFECTIVE_DIRECTIVE);
+                            if ((effective == null) || (effective.equals(Constants.EFFECTIVE_RESOLVE)))
+                            {
+                                capList.add(cap);
+                                if (cap.getNamespace().equals(
+                                    BundleRevision.PACKAGE_NAMESPACE))
+                                {
+                                    List<List<String>> filters =
+                                        parsePkgFilters(
+                                            cap, Constants.INCLUDE_DIRECTIVE);
+                                    if (filters != null)
+                                    {
+                                        includedPkgFilters.put((String)
+                                            cap.getAttributes()
+                                                .get(BundleRevision.PACKAGE_NAMESPACE),
+                                            filters);
+                                    }
+                                    filters = parsePkgFilters(cap, Constants.EXCLUDE_DIRECTIVE);
+                                    if (filters != null)
+                                    {
+                                        excludedPkgFilters.put((String)
+                                            cap.getAttributes()
+                                                .get(BundleRevision.PACKAGE_NAMESPACE),
+                                            filters);
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        if (System.getSecurityManager() != null)
+        {
+            for (Iterator<BundleCapability> iter = capList.iterator();iter.hasNext();)
+            {
+                BundleCapability cap = iter.next();
+                if (cap.getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE))
+                {
+                    if (!((BundleProtectionDomain) ((BundleRevisionImpl) cap.getRevision()).getProtectionDomain()).impliesDirect(
+                        new PackagePermission((String) cap.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE), PackagePermission.EXPORTONLY)))
+                    {
+                        iter.remove();
+                    }
+                }
+                else if (!cap.getNamespace().equals(BundleRevision.HOST_NAMESPACE) && !cap.getNamespace().equals(BundleRevision.BUNDLE_NAMESPACE) &&
+                    !cap.getNamespace().equals("osgi.ee"))
+                {
+                    if (!((BundleProtectionDomain) ((BundleRevisionImpl) cap.getRevision()).getProtectionDomain()).impliesDirect(
+                        new CapabilityPermission(cap.getNamespace(), CapabilityPermission.PROVIDE)))
+                    {
+                        iter.remove();
+                    }
+                }
+            }
+        }
+
+        m_resolvedCaps = ImmutableList.newInstance(capList);
+        m_includedPkgFilters = (includedPkgFilters.isEmpty())
+            ? Collections.EMPTY_MAP : includedPkgFilters;
+        m_excludedPkgFilters = (excludedPkgFilters.isEmpty())
+            ? Collections.EMPTY_MAP : excludedPkgFilters;
+
+        List<R4Library> libList = (m_revision.getDeclaredNativeLibraries() == null)
+            ? new ArrayList<R4Library>()
+            : new ArrayList<R4Library>(m_revision.getDeclaredNativeLibraries());
+        for (int fragIdx = 0;
+            (m_fragments != null) && (fragIdx < m_fragments.size());
+            fragIdx++)
+        {
+            List<R4Library> libs =
+                ((BundleRevisionImpl) m_fragments.get(fragIdx))
+                    .getDeclaredNativeLibraries();
+            for (int reqIdx = 0;
+                (libs != null) && (reqIdx < libs.size());
+                reqIdx++)
+            {
+                libList.add(libs.get(reqIdx));
+            }
+        }
+        // We need to return null here if we don't have any libraries, since a
+        // zero-length array is used to indicate that matching native libraries
+        // could not be found when resolving the bundle.
+        m_resolvedNativeLibs = (libList.isEmpty())
+            ? null
+            : ImmutableList.newInstance(libList);
+
+        ClassLoader bootLoader = m_defBootClassLoader;
+        if (revision.getBundle().getBundleId() != 0)
+        {
+            Object map = m_configMap.get(FelixConstants.BOOT_CLASSLOADERS_PROP);
+            if (map instanceof Map)
+            {
+                Object l = ((Map) map).get(m_revision.getBundle());
+                if (l instanceof ClassLoader)
+                {
+                    bootLoader = (ClassLoader) l;
+                }
+            }
+        }
+        m_bootClassLoader = bootLoader;
+
+        m_implicitBootDelegation =
+            (m_configMap.get(FelixConstants.IMPLICIT_BOOT_DELEGATION_PROP) == null)
+            || Boolean.valueOf(
+                (String) m_configMap.get(
+                    FelixConstants.IMPLICIT_BOOT_DELEGATION_PROP)).booleanValue();
+
+        m_useLocalURLs =
+            (m_configMap.get(FelixConstants.USE_LOCALURLS_PROP) == null)
+                ? false : true;
+    }
+
+    private static List<List<String>> parsePkgFilters(BundleCapability cap, String filtername)
+    {
+        List<List<String>> filters = null;
+        String include = cap.getDirectives().get(filtername);
+        if (include != null)
+        {
+            List<String> filterStrings = ManifestParser.parseDelimitedString(include, ",");
+            filters = new ArrayList<List<String>>(filterStrings.size());
+
+            for (int filterIdx = 0; filterIdx < filterStrings.size(); filterIdx++)
+            {
+                List<String> substrings =
+                    SimpleFilter.parseSubstring(filterStrings.get(filterIdx));
+                filters.add(substrings);
+            }
+        }
+        return filters;
+    }
+
+    @Override
+    public String toString()
+    {
+        return m_revision.getBundle().toString();
+    }
+
+    public synchronized void dispose()
+    {
+        if (m_fragmentContents != null)
+        {
+            for (Content content : m_fragmentContents)
+            {
+                content.close();
+            }
+        }
+        m_classLoader = null;
+        m_isDisposed = true;
+    }
+
+// TODO: OSGi R4.3 - This really shouldn't be public, but it is needed by the
+//       resolver to determine if a bundle can dynamically import.
+    public boolean hasPackageSource(String pkgName)
+    {
+        return (m_importedPkgs.containsKey(pkgName) || m_requiredPkgs.containsKey(pkgName));
+    }
+
+// TODO: OSGi R4.3 - This really shouldn't be public, but it is needed by the
+//       to implement dynamic imports.
+    public BundleRevision getImportedPackageSource(String pkgName)
+    {
+        return m_importedPkgs.get(pkgName);
+    }
+
+    List<Content> getFragmentContents()
+    {
+        return m_fragmentContents;
+    }
+
+    public boolean isCurrent()
+    {
+        BundleRevision current = getBundle().adapt(BundleRevision.class);
+        return (current != null) && (current.getWiring() == this);
+    }
+
+    public synchronized boolean isInUse()
+    {
+        return !m_isDisposed;
+    }
+
+    public List<Capability> getResourceCapabilities(String namespace)
+    {
+        return BundleRevisionImpl.asCapabilityList(getCapabilities(namespace));
+    }
+
+    public List<BundleCapability> getCapabilities(String namespace)
+    {
+        if (isInUse())
+        {
+            List<BundleCapability> result = m_resolvedCaps;
+            if (namespace != null)
+            {
+                result = new ArrayList<BundleCapability>();
+                for (BundleCapability cap : m_resolvedCaps)
+                {
+                    if (cap.getNamespace().equals(namespace))
+                    {
+                        result.add(cap);
+                    }
+                }
+            }
+            return result;
+        }
+        return null;
+    }
+
+    public List<Requirement> getResourceRequirements(String namespace)
+    {
+        return BundleRevisionImpl.asRequirementList(getRequirements(namespace));
+    }
+
+    public List<BundleRequirement> getRequirements(String namespace)
+    {
+        if (isInUse())
+        {
+            List<BundleRequirement> searchReqs = m_resolvedReqs;
+            List<BundleRequirement> wovenReqs = m_wovenReqs;
+            List<BundleRequirement> result = m_resolvedReqs;
+
+            if (wovenReqs != null)
+            {
+                searchReqs = new ArrayList<BundleRequirement>(m_resolvedReqs);
+                searchReqs.addAll(wovenReqs);
+                result = searchReqs;
+            }
+
+            if (namespace != null)
+            {
+                result = new ArrayList<BundleRequirement>();
+                for (BundleRequirement req : searchReqs)
+                {
+                    if (req.getNamespace().equals(namespace))
+                    {
+                        result.add(req);
+                    }
+                }
+            }
+            return result;
+        }
+        return null;
+    }
+
+    public List<R4Library> getNativeLibraries()
+    {
+        return m_resolvedNativeLibs;
+    }
+
+    private static List<Wire> asWireList(List wires)
+    {
+        return (List<Wire>) wires;
+    }
+
+    public List<Wire> getProvidedResourceWires(String namespace)
+    {
+        return asWireList(getProvidedWires(namespace));
+    }
+
+    public List<BundleWire> getProvidedWires(String namespace)
+    {
+        if (isInUse())
+        {
+            return ((BundleImpl) m_revision.getBundle())
+                .getFramework().getDependencies().getProvidedWires(m_revision, namespace);
+        }
+        return null;
+    }
+
+    public List<Wire> getRequiredResourceWires(String namespace)
+    {
+        return asWireList(getRequiredWires(namespace));
+    }
+
+    public List<BundleWire> getRequiredWires(String namespace)
+    {
+        if (isInUse())
+        {
+            List<BundleWire> result = m_wires;
+            if (namespace != null)
+            {
+                result = new ArrayList<BundleWire>();
+                for (BundleWire bw : m_wires)
+                {
+                    if (bw.getRequirement().getNamespace().equals(namespace))
+                    {
+                        result.add(bw);
+                    }
+                }
+            }
+            return result;
+        }
+        return null;
+    }
+
+    public synchronized void addDynamicWire(BundleWire wire)
+    {
+        // Make new wires list.
+        List<BundleWire> wires = new ArrayList<BundleWire>(m_wires);
+        wires.add(wire);
+        // Make new imported package map.
+        Map<String, BundleRevision> importedPkgs =
+            new HashMap<String, BundleRevision>(m_importedPkgs);
+        importedPkgs.put(
+            (String) wire.getCapability().getAttributes().get(BundleRevision.PACKAGE_NAMESPACE),
+            wire.getProviderWiring().getRevision());
+        // Update associated member values.
+        // Technically, there is a window here where readers won't see
+        // both values updates at the same time, but it seems unlikely
+        // to cause any issues.
+        m_wires = ImmutableList.newInstance(wires);
+        m_importedPkgs = importedPkgs;
+    }
+
+    public BundleRevision getResource()
+    {
+        return m_revision;
+    }
+
+    public BundleRevision getRevision()
+    {
+        return m_revision;
+    }
+
+    public ClassLoader getClassLoader()
+    {
+        if (m_isDisposed)
+        {
+            return null;
+        }
+        return getClassLoaderInternal();
+    }
+
+    private synchronized ClassLoader getClassLoaderInternal()
+    {
+        // Only try to create the class loader if the bundle
+        // is not disposed.
+        if (!m_isDisposed && (m_classLoader == null))
+        {
+            // Determine which class loader to use based on which
+            // Java platform we are running on.
+            Class clazz;
+            if (m_isPreJava5)
+            {
+                clazz = BundleClassLoader.class;
+            }
+            else
+            {
+                try
+                {
+                    clazz = BundleClassLoaderJava5.class;
+                }
+                catch (Throwable th)
+                {
+                    // If we are on pre-Java5 then we will get a verify error
+                    // here since we try to override a getResources() which is
+                    // a final method in pre-Java5.
+                    m_isPreJava5 = true;
+                    clazz = BundleClassLoader.class;
+                }
+            }
+
+            // Use SecureAction to create the class loader if security is
+            // enabled; otherwise, create it directly.
+            try
+            {
+                Constructor ctor = (Constructor) BundleRevisionImpl.getSecureAction()
+                    .getConstructor(clazz, new Class[] { BundleWiringImpl.class, ClassLoader.class });
+                m_classLoader = (BundleClassLoader)
+                    BundleRevisionImpl.getSecureAction().invoke(ctor,
+                    new Object[] { this, determineParentClassLoader() });
+            }
+            catch (Exception ex)
+            {
+                throw new RuntimeException("Unable to create module class loader: "
+                    + ex.getMessage() + " [" + ex.getClass().getName() + "]");
+            }
+        }
+        return m_classLoader;
+    }
+
+    public List<URL> findEntries(String path, String filePattern, int options)
+    {
+        if (isInUse())
+        {
+            if (!Util.isFragment(m_revision))
+            {
+                Enumeration<URL> e =
+                    ((BundleImpl) m_revision.getBundle()).getFramework()
+                        .findBundleEntries(m_revision, path, filePattern,
+                           (options & BundleWiring.FINDENTRIES_RECURSE) > 0);
+                List<URL> entries = new ArrayList<URL>();
+                while ((e != null) && e.hasMoreElements())
+                {
+                    entries.add(e.nextElement());
+                }
+                return ImmutableList.newInstance(entries);
+            }
+            return Collections.EMPTY_LIST;
+        }
+        return null;
+    }
+
+    // Thread local to detect class loading cycles.
+    private final ThreadLocal m_listResourcesCycleCheck = new ThreadLocal();
+
+// TODO: OSGi R4.3 - Should this be synchronized or should we take a snapshot?
+    public synchronized Collection<String> listResources(
+        String path, String filePattern, int options)
+    {
+        // Implementation note: If you enable the DEBUG option for
+        // listResources() to print from where each resource comes,
+        // it will not give 100% accurate answers in the face of
+        // Require-Bundle cycles with overlapping content since
+        // the actual source will depend on who does the class load
+        // first. Further, normal class loaders cache class load
+        // results so it is always the same subsequently, but we
+        // don't do that here so it will always return a different
+        // result depending upon who is asking. Moral to the story:
+        // don't do cycles and certainly don't do them with
+        // overlapping content.
+
+        Collection<String> resources = null;
+
+        // Normalize path.
+        if ((path.length() > 0) && (path.charAt(0) == '/'))
+        {
+            path = path.substring(1);
+        }
+        if ((path.length() > 0) && (path.charAt(path.length() - 1) != '/'))
+        {
+            path = path + '/';
+        }
+
+        // Parse the file filter.
+        filePattern = (filePattern == null) ? "*" : filePattern;
+        List<String> pattern = SimpleFilter.parseSubstring(filePattern);
+
+        // We build an internal collection of ResourceSources, since this
+        // allows us to print out additional debug information.
+        Collection<ResourceSource> sources = listResourcesInternal(path, pattern, options);
+        if (sources != null)
+        {
+            boolean debug = (options & LISTRESOURCES_DEBUG) > 0;
+            resources = new TreeSet<String>();
+            for (ResourceSource source : sources)
+            {
+                if (debug)
+                {
+                    resources.add(source.toString());
+                }
+                else
+                {
+                    resources.add(source.m_resource);
+                }
+            }
+        }
+        return resources;
+    }
+
+    private Collection<ResourceSource> listResourcesInternal(
+        String path, List<String> pattern, int options)
+    {
+        if (isInUse())
+        {
+            boolean recurse = (options & BundleWiring.LISTRESOURCES_RECURSE) > 0;
+            boolean localOnly = (options & BundleWiring.LISTRESOURCES_LOCAL) > 0;
+
+            // Check for cycles, which can happen with Require-Bundle.
+            Set<String> cycles = (Set<String>) m_listResourcesCycleCheck.get();
+            if (cycles == null)
+            {
+                cycles = new HashSet<String>();
+                m_listResourcesCycleCheck.set(cycles);
+            }
+            if (cycles.contains(path))
+            {
+                return Collections.EMPTY_LIST;
+            }
+            cycles.add(path);
+
+            try
+            {
+                // Calculate set of remote resources (i.e., those either
+                // imported or required).
+                Collection<ResourceSource> remoteResources = new TreeSet<ResourceSource>();
+                // Imported packages cannot have merged content, so we need to
+                // keep track of these packages.
+                Set<String> noMerging = new HashSet<String>();
+                // Loop through wires to compute remote resources.
+                for (BundleWire bw : m_wires)
+                {
+                    if (bw.getCapability().getNamespace()
+                        .equals(BundleRevision.PACKAGE_NAMESPACE))
+                    {
+                        // For imported packages, we only need to calculate
+                        // the remote resources of the specific imported package.
+                        remoteResources.addAll(
+                            calculateRemotePackageResources(
+                                bw, bw.getCapability(), recurse,
+                                    path, pattern, noMerging));
+                    }
+                    else if (bw.getCapability().getNamespace()
+                        .equals(BundleRevision.BUNDLE_NAMESPACE))
+                    {
+                        // For required bundles, all declared package capabilities
+                        // from the required bundle will be available to requirers,
+                        // so get the target required bundle's declared packages
+                        // and handle them in a similar fashion to a normal import
+                        // except that their content can be merged with local
+                        // packages.
+                        List<BundleCapability> exports =
+                            bw.getProviderWiring().getRevision()
+                                .getDeclaredCapabilities(BundleRevision.PACKAGE_NAMESPACE);
+                        for (BundleCapability export : exports)
+                        {
+                            remoteResources.addAll(
+                                calculateRemotePackageResources(
+                                    bw, export, recurse, path, pattern, null));
+                        }
+
+                        // Since required bundle may reexport bundles it requires,
+                        // check its wires for this case.
+                        List<BundleWire> requiredBundles =
+                            bw.getProviderWiring().getRequiredWires(
+                                BundleRevision.BUNDLE_NAMESPACE);
+                        for (BundleWire rbWire : requiredBundles)
+                        {
+                            String visibility =
+                                rbWire.getRequirement().getDirectives()
+                                    .get(Constants.VISIBILITY_DIRECTIVE);
+                            if ((visibility != null)
+                                && (visibility.equals(Constants.VISIBILITY_REEXPORT)))
+                            {
+                                // For each reexported required bundle, treat them
+                                // in a similar fashion as a normal required bundle
+                                // by including all of their declared package
+                                // capabilities in the requiring bundle's class
+                                // space.
+                                List<BundleCapability> reexports =
+                                    rbWire.getProviderWiring().getRevision()
+                                        .getDeclaredCapabilities(BundleRevision.PACKAGE_NAMESPACE);
+                                for (BundleCapability reexport : reexports)
+                                {
+                                    remoteResources.addAll(
+                                        calculateRemotePackageResources(
+                                            bw, reexport, recurse, path, pattern, null));
+                                }
+                            }
+                        }
+                    }
+                }
+
+                // Calculate set of local resources (i.e., those contained
+                // in the revision or its fragments).
+                Collection<ResourceSource> localResources = new TreeSet<ResourceSource>();
+                // Get the revision's content path, which includes contents
+                // from fragments.
+                List<Content> contentPath = m_revision.getContentPath();
+                for (Content content : contentPath)
+                {
+                    Enumeration<String> e = content.getEntries();
+                    if (e != null)
+                    {
+                        while (e.hasMoreElements())
+                        {
+                            String resource = e.nextElement();
+                            String resourcePath = getTrailingPath(resource);
+                            if (!noMerging.contains(resourcePath))
+                            {
+                                if ((!recurse && resourcePath.equals(path))
+                                    || (recurse && resourcePath.startsWith(path)))
+                                {
+                                    if (matchesPattern(pattern, getPathHead(resource)))
+                                    {
+                                        localResources.add(
+                                            new ResourceSource(resource, m_revision));
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+
+                if (localOnly)
+                {
+                    return localResources;
+                }
+                else
+                {
+                    remoteResources.addAll(localResources);
+                    return remoteResources;
+                }
+            }
+            finally
+            {
+                cycles.remove(path);
+                if (cycles.isEmpty())
+                {
+                    m_listResourcesCycleCheck.set(null);
+                }
+            }
+        }
+        return null;
+    }
+
+    private Collection<ResourceSource> calculateRemotePackageResources(
+        BundleWire bw, BundleCapability cap, boolean recurse,
+        String path, List<String> pattern, Set<String> noMerging)
+    {
+        Collection<ResourceSource> resources = Collections.EMPTY_SET;
+
+        // Convert package name to a path.
+        String subpath = (String) cap.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE);
+        subpath = subpath.replace('.', '/') + '/';
+        // If necessary, record that this package should not be merged
+        // with local content.
+        if (noMerging != null)
+        {
+            noMerging.add(subpath);
+        }
+
+        // If we are not recuring, check for path equality or if
+        // we are recursing, check that the subpath starts with
+        // the target path.
+        if ((!recurse && subpath.equals(path))
+            || (recurse && subpath.startsWith(path)))
+        {
+            // Delegate to the original provider wiring to have it calculate
+            // the list of resources in the package. In this case, we don't
+            // want to recurse since we want the precise package.
+            resources =
+                ((BundleWiringImpl) bw.getProviderWiring()).listResourcesInternal(
+                    subpath, pattern, 0);
+
+            // The delegatedResources result will include subpackages
+            // which need to be filtered out, since imported packages
+            // do not give access to subpackages. If a subpackage is
+            // imported, it will be added by its own wire.
+            for (Iterator<ResourceSource> it = resources.iterator();
+                it.hasNext(); )
+            {
+                ResourceSource reqResource = it.next();
+                if (reqResource.m_resource.charAt(
+                    reqResource.m_resource.length() - 1) == '/')
+                {
+                    it.remove();
+                }
+            }
+        }
+        // If we are not recursing, but the required package
+        // is a child of the desired path, then include its
+        // immediate child package. We do this so that it is
+        // possible to use listResources() to walk the resource
+        // tree similar to doing a directory walk one level
+        // at a time.
+        else if (!recurse && subpath.startsWith(path))
+        {
+            int idx = subpath.indexOf('/', path.length());
+            if (idx >= 0)
+            {
+                subpath = subpath.substring(0, idx + 1);
+            }
+            if (matchesPattern(pattern, getPathHead(subpath)))
+            {
+                resources = Collections.singleton(
+                    new ResourceSource(subpath, bw.getProviderWiring().getRevision()));
+            }
+        }
+
+        return resources;
+    }
+
+    private static String getPathHead(String resource)
+    {
+        if (resource.length() == 0)
+        {
+            return resource;
+        }
+        int idx = (resource.charAt(resource.length() - 1) == '/')
+            ? resource.lastIndexOf('/', resource.length() - 2)
+            : resource.lastIndexOf('/');
+        if (idx < 0)
+        {
+            return resource;
+        }
+        return resource.substring(idx + 1);
+    }
+
+    private static String getTrailingPath(String resource)
+    {
+        if (resource.length() == 0)
+        {
+            return null;
+        }
+        int idx = (resource.charAt(resource.length() - 1) == '/')
+            ? resource.lastIndexOf('/', resource.length() - 2)
+            : resource.lastIndexOf('/');
+        if (idx < 0)
+        {
+            return "";
+        }
+        return resource.substring(0, idx + 1);
+    }
+
+    private static boolean matchesPattern(List<String> pattern, String resource)
+    {
+        if (resource.charAt(resource.length() - 1) == '/')
+        {
+            resource = resource.substring(0, resource.length() - 1);
+        }
+        return SimpleFilter.compareSubstring(pattern, resource);
+    }
+
+    public Bundle getBundle()
+    {
+        return m_revision.getBundle();
+    }
+
+    //
+    // Class loader implementation methods.
+    //
+
+    private URL createURL(int port, String path)
+    {
+        // Add a slash if there is one already, otherwise
+        // the is no slash separating the host from the file
+        // in the resulting URL.
+        if (!path.startsWith("/"))
+        {
+            path = "/" + path;
+        }
+
+        try
+        {
+            return BundleRevisionImpl.getSecureAction().createURL(null,
+                FelixConstants.BUNDLE_URL_PROTOCOL + "://" +
+                m_revision.getId() + ":" + port + path,
+                ((BundleImpl) getBundle()).getFramework().getBundleStreamHandler());
+        }
+        catch (MalformedURLException ex)
+        {
+            m_logger.log(m_revision.getBundle(),
+                Logger.LOG_ERROR,
+                "Unable to create resource URL.",
+                ex);
+        }
+        return null;
+    }
+
+    public Enumeration getResourcesByDelegation(String name)
+    {
+        Set requestSet = (Set) m_cycleCheck.get();
+        if (requestSet == null)
+        {
+            requestSet = new HashSet();
+            m_cycleCheck.set(requestSet);
+        }
+        if (!requestSet.contains(name))
+        {
+            requestSet.add(name);
+            try
+            {
+                return findResourcesByDelegation(name);
+            }
+            finally
+            {
+                requestSet.remove(name);
+            }
+        }
+
+        return null;
+    }
+
+    private Enumeration findResourcesByDelegation(String name)
+    {
+        Enumeration urls = null;
+        List completeUrlList = new ArrayList();
+
+        // Get the package of the target class/resource.
+        String pkgName = Util.getResourcePackage(name);
+
+        // Delegate any packages listed in the boot delegation
+        // property to the parent class loader.
+        if (shouldBootDelegate(pkgName))
+        {
+            try
+            {
+                // Get the appropriate class loader for delegation.
+                ClassLoader bdcl = getBootDelegationClassLoader();
+                urls = bdcl.getResources(name);
+            }
+            catch (IOException ex)
+            {
+                // This shouldn't happen and even if it does, there
+                // is nothing we can do, so just ignore it.
+            }
+            // If this is a java.* package, then always terminate the
+            // search; otherwise, continue to look locally.
+            if (pkgName.startsWith("java."))
+            {
+                return urls;
+            }
+
+            completeUrlList.add(urls);
+        }
+
+        // Look in the revisions's imported packages. If the package is
+        // imported, then we stop searching no matter the result since
+        // imported packages cannot be split.
+        BundleRevision provider = m_importedPkgs.get(pkgName);
+        if (provider != null)
+        {
+            // Delegate to the provider revision.
+            urls = ((BundleWiringImpl) provider.getWiring()).getResourcesByDelegation(name);
+
+            // If we find any resources, then add them.
+            if ((urls != null) && (urls.hasMoreElements()))
+            {
+                completeUrlList.add(urls);
+            }
+
+            // Always return here since imported packages cannot be split
+            // across required bundles or the revision's content.
+            return new CompoundEnumeration((Enumeration[])
+                completeUrlList.toArray(new Enumeration[completeUrlList.size()]));
+        }
+
+        // See whether we can get the resource from the required bundles and
+        // regardless of whether or not this is the case continue to the next
+        // step potentially passing on the result of this search (if any).
+        List<BundleRevision> providers = m_requiredPkgs.get(pkgName);
+        if (providers != null)
+        {
+            for (BundleRevision p : providers)
+            {
+                // Delegate to the provider revision.
+                urls = ((BundleWiringImpl) p.getWiring()).getResourcesByDelegation(name);
+
+                // If we find any resources, then add them.
+                if ((urls != null) && (urls.hasMoreElements()))
+                {
+                    completeUrlList.add(urls);
+                }
+
+                // Do not return here, since required packages can be split
+                // across the revision's content.
+            }
+        }
+
+        // Try the module's own class path. If we can find the resource then
+        // return it together with the results from the other searches else
+        // try to look into the dynamic imports.
+        urls = m_revision.getResourcesLocal(name);
+        if ((urls != null) && (urls.hasMoreElements()))
+        {
+            completeUrlList.add(urls);
+        }
+        else
+        {
+            // If not found, then try the module's dynamic imports.
+            // At this point, the module's imports were searched and so was the
+            // the module's content. Now we make an attempt to load the
+            // class/resource via a dynamic import, if possible.
+            try
+            {
+                provider = m_resolver.resolve(m_revision, pkgName);
+            }
+            catch (ResolveException ex)
+            {
+                // Ignore this since it is likely normal.
+            }
+            catch (BundleException ex)
+            {
+                // Ignore this since it is likely the result of a resolver hook.
+            }
+            if (provider != null)
+            {
+                // Delegate to the provider revision.
+                urls = ((BundleWiringImpl) provider.getWiring()).getResourcesByDelegation(name);
+
+                // If we find any resources, then add them.
+                if ((urls != null) && (urls.hasMoreElements()))
+                {
+                    completeUrlList.add(urls);
+                }
+            }
+        }
+
+        return new CompoundEnumeration((Enumeration[])
+            completeUrlList.toArray(new Enumeration[completeUrlList.size()]));
+    }
+
+    private ClassLoader determineParentClassLoader()
+    {
+        // Determine the class loader's parent based on the
+        // configuration property; use boot class loader by
+        // default.
+        String cfg = (String) m_configMap.get(Constants.FRAMEWORK_BUNDLE_PARENT);
+        cfg = (cfg == null) ? Constants.FRAMEWORK_BUNDLE_PARENT_BOOT : cfg;
+        final ClassLoader parent;
+        if (cfg.equalsIgnoreCase(Constants.FRAMEWORK_BUNDLE_PARENT_APP))
+        {
+            parent = BundleRevisionImpl.getSecureAction().getSystemClassLoader();
+        }
+        else if (cfg.equalsIgnoreCase(Constants.FRAMEWORK_BUNDLE_PARENT_EXT))
+        {
+            parent = BundleRevisionImpl.getSecureAction().getParentClassLoader(
+                BundleRevisionImpl.getSecureAction().getSystemClassLoader());
+        }
+        else if (cfg.equalsIgnoreCase(Constants.FRAMEWORK_BUNDLE_PARENT_FRAMEWORK))
+        {
+            parent = BundleRevisionImpl.getSecureAction()
+                .getClassLoader(BundleRevisionImpl.class);
+        }
+        // On Android we cannot set the parent class loader to be null, so
+        // we special case that situation here and set it to the system
+        // class loader by default instead, which is not really spec.
+        else if (m_bootClassLoader == null)
+        {
+            parent = BundleRevisionImpl.getSecureAction().getSystemClassLoader();
+        }
+        else
+        {
+            parent = null;
+        }
+        return parent;
+    }
+
+    boolean shouldBootDelegate(String pkgName)
+    {
+        // Always boot delegate if the bundle has a configured
+        // boot class loader.
+        if (m_bootClassLoader != m_defBootClassLoader)
+        {
+            return true;
+        }
+
+        boolean result = false;
+
+        // Only consider delegation if we have a package name, since
+        // we don't want to promote the default package. The spec does
+        // not take a stand on this issue.
+        if (pkgName.length() > 0)
+        {
+            for (int i = 0;
+                !result
+                    && (i < ((BundleImpl) getBundle())
+                        .getFramework().getBootPackages().length);
+                i++)
+            {
+                // Check if the boot package is wildcarded.
+                // A wildcarded boot package will be in the form "foo.",
+                // so a matching subpackage will start with "foo.", e.g.,
+                // "foo.bar".
+                if (((BundleImpl) getBundle()).getFramework().getBootPackageWildcards()[i]
+                    && pkgName.startsWith(
+                        ((BundleImpl) getBundle()).getFramework().getBootPackages()[i]))
+                {
+                    return true;
+                }
+                // If not wildcarded, then check for an exact match.
+                else if (((BundleImpl) getBundle())
+                    .getFramework().getBootPackages()[i].equals(pkgName))
+                {
+                    return true;
+                }
+            }
+        }
+
+        return result;
+    }
+
+    synchronized ClassLoader getBootDelegationClassLoader()
+    {
+        // Get the appropriate class loader for delegation.
+        ClassLoader parent = (m_classLoader == null)
+            ? determineParentClassLoader() :
+            BundleRevisionImpl.getSecureAction().getParentClassLoader(m_classLoader);
+        return (parent == null) ? m_bootClassLoader : parent;
+    }
+
+    private static final Constructor m_dexFileClassConstructor;
+    private static final Method m_dexFileClassLoadDex;
+    private static final Method m_dexFileClassLoadClass;
+
+    static
+    {
+        Constructor dexFileClassConstructor = null;
+        Method dexFileClassLoadDex = null;
+        Method dexFileClassLoadClass = null;
+        try
+        {
+            Class dexFileClass;
+            try
+            {
+                dexFileClass = Class.forName("dalvik.system.DexFile");
+            }
+            catch (Exception ex)
+            {
+                dexFileClass = Class.forName("android.dalvik.DexFile");
+            }
+
+            try
+            {
+                dexFileClassLoadDex = dexFileClass.getMethod("loadDex",
+                    new Class[]{String.class, String.class, Integer.TYPE});
+            }
+            catch (Exception ex)
+            {
+                // Nothing we need to do
+            }
+            dexFileClassConstructor = dexFileClass.getConstructor(
+                new Class[] { java.io.File.class });
+            dexFileClassLoadClass = dexFileClass.getMethod("loadClass",
+                new Class[] { String.class, ClassLoader.class });
+        }
+        catch (Throwable ex)
+        {
+           dexFileClassConstructor = null;
+           dexFileClassLoadDex = null;
+           dexFileClassLoadClass = null;
+        }
+        m_dexFileClassConstructor = dexFileClassConstructor;
+        m_dexFileClassLoadDex = dexFileClassLoadDex;
+        m_dexFileClassLoadClass = dexFileClassLoadClass;
+    }
+
+    public Class getClassByDelegation(String name) throws ClassNotFoundException
+    {
+        // We do not call getClassLoader().loadClass() for arrays because
+        // it does not correctly handle array types, which is necessary in
+        // cases like deserialization using a wrapper class loader.
+        if ((name != null) && (name.length() > 0) && (name.charAt(0) == '['))
+        {
+            return Class.forName(name, false, getClassLoader());
+        }
+
+        // Check to see if the requested class is filtered.
+        if (isFiltered(name))
+        {
+            throw new ClassNotFoundException(name);
+        }
+
+        ClassLoader cl = getClassLoaderInternal();
+        if (cl == null)
+        {
+            throw new ClassNotFoundException(
+                "Unable to load class '"
+                + name
+                + "' because the bundle wiring for "
+                + m_revision.getSymbolicName()
+                + " is no longer valid.");
+        }
+        return cl.loadClass(name);
+    }
+
+    private boolean isFiltered(String name)
+    {
+        String pkgName = Util.getClassPackage(name);
+        List<List<String>> includeFilters = m_includedPkgFilters.get(pkgName);
+        List<List<String>> excludeFilters = m_excludedPkgFilters.get(pkgName);
+
+        if ((includeFilters == null) && (excludeFilters == null))
+        {
+            return false;
+        }
+
+        // Get the class name portion of the target class.
+        String className = Util.getClassName(name);
+
+        // If there are no include filters then all classes are included
+        // by default, otherwise try to find one match.
+        boolean included = (includeFilters == null);
+        for (int i = 0;
+            (!included) && (includeFilters != null) && (i < includeFilters.size());
+            i++)
+        {
+            included = SimpleFilter.compareSubstring(includeFilters.get(i), className);
+        }
+
+        // If there are no exclude filters then no classes are excluded
+        // by default, otherwise try to find one match.
+        boolean excluded = false;
+        for (int i = 0;
+            (!excluded) && (excludeFilters != null) && (i < excludeFilters.size());
+            i++)
+        {
+            excluded = SimpleFilter.compareSubstring(excludeFilters.get(i), className);
+        }
+        return !included || excluded;
+    }
+
+    public URL getResourceByDelegation(String name)
+    {
+        try
+        {
+            return (URL) findClassOrResourceByDelegation(name, false);
+        }
+        catch (ClassNotFoundException ex)
+        {
+            // This should never be thrown because we are loading resources.
+        }
+        catch (ResourceNotFoundException ex)
+        {
+            m_logger.log(m_revision.getBundle(),
+                Logger.LOG_DEBUG,
+                ex.getMessage());
+        }
+        return null;
+    }
+
+    private Object findClassOrResourceByDelegation(String name, boolean isClass)
+        throws ClassNotFoundException, ResourceNotFoundException
+    {
+        Object result = null;
+
+        Set requestSet = (Set) m_cycleCheck.get();
+        if (requestSet == null)
+        {
+            requestSet = new HashSet();
+            m_cycleCheck.set(requestSet);
+        }
+        if (requestSet.add(name))
+        {
+            try
+            {
+                // Get the package of the target class/resource.
+                String pkgName = (isClass)
+                    ? Util.getClassPackage(name)
+                    : Util.getResourcePackage(name);
+
+                // Delegate any packages listed in the boot delegation
+                // property to the parent class loader.
+                if (shouldBootDelegate(pkgName))
+                {
+                    try
+                    {
+                        // Get the appropriate class loader for delegation.
+                        ClassLoader bdcl = getBootDelegationClassLoader();
+                        result = (isClass)
+                            ? (Object) bdcl.loadClass(name)
+                            : (Object) bdcl.getResource(name);
+                        // If this is a java.* package, then always terminate the
+                        // search; otherwise, continue to look locally if not found.
+                        if (pkgName.startsWith("java.") || (result != null))
+                        {
+                            return result;
+                        }
+                    }
+                    catch (ClassNotFoundException ex)
+                    {
+                        // If this is a java.* package, then always terminate the
+                        // search; otherwise, continue to look locally if not found.
+                        if (pkgName.startsWith("java."))
+                        {
+                            throw ex;
+                        }
+                    }
+                }
+
+                // Look in the revision's imports. Note that the search may
+                // be aborted if this method throws an exception, otherwise
+                // it continues if a null is returned.
+                result = searchImports(pkgName, name, isClass);
+
+                // If not found, try the revision's own class path.
+                if (result == null)
+                {
+                    if (isClass)
+                    {
+                        ClassLoader cl = getClassLoaderInternal();
+                        if (cl == null)
+                        {
+                            throw new ClassNotFoundException(
+                                "Unable to load class '"
+                                + name
+                                + "' because the bundle wiring for "
+                                + m_revision.getSymbolicName()
+                                + " is no longer valid.");
+                        }
+                        result = (Object) ((BundleClassLoader) cl).findClass(name);
+                    }
+                    else
+                    {
+                        result = (Object) m_revision.getResourceLocal(name);
+                    }
+
+                    // If still not found, then try the revision's dynamic imports.
+                    if (result == null)
+                    {
+                        result = searchDynamicImports(pkgName, name, isClass);
+                    }
+                }
+            }
+            finally
+            {
+                requestSet.remove(name);
+            }
+        }
+        else
+        {
+            // If a cycle is detected, we should return null to break the
+            // cycle. This should only ever be return to internal class
+            // loading code and not to the actual instigator of the class load.
+            return null;
+        }
+
+        if (result == null)
+        {
+            if (isClass)
+            {
+                throw new ClassNotFoundException(
+                    name + " not found by " + this.getBundle());
+            }
+            else
+            {
+                throw new ResourceNotFoundException(
+                    name + " not found by " + this.getBundle());
+            }
+        }
+
+        return result;
+    }
+
+    private Object searchImports(String pkgName, String name, boolean isClass)
+        throws ClassNotFoundException, ResourceNotFoundException
+    {
+        // Check if the package is imported.
+        BundleRevision provider = m_importedPkgs.get(pkgName);
+        if (provider != null)
+        {
+            // If we find the class or resource, then return it.
+            Object result = (isClass)
+                ? (Object) ((BundleWiringImpl) provider.getWiring()).getClassByDelegation(name)
+                : (Object) ((BundleWiringImpl) provider.getWiring()).getResourceByDelegation(name);
+            if (result != null)
+            {
+                return result;
+            }
+
+            // If no class or resource was found, then we must throw an exception
+            // since the provider of this package did not contain the
+            // requested class and imported packages are atomic.
+            if (isClass)
+            {
+                throw new ClassNotFoundException(name);
+            }
+            throw new ResourceNotFoundException(name);
+        }
+
+        // Check if the package is required.
+        List<BundleRevision> providers = m_requiredPkgs.get(pkgName);
+        if (providers != null)
+        {
+            for (BundleRevision p : providers)
+            {
+                // If we find the class or resource, then return it.
+                try
+                {
+                    Object result = (isClass)
+                        ? (Object) ((BundleWiringImpl) p.getWiring()).getClassByDelegation(name)
+                        : (Object) ((BundleWiringImpl) p.getWiring()).getResourceByDelegation(name);
+                    if (result != null)
+                    {
+                        return result;
+                    }
+                }
+                catch (ClassNotFoundException ex)
+                {
+                    // Since required packages can be split, don't throw an
+                    // exception here if it is not found. Instead, we'll just
+                    // continue searching other required bundles and the
+                    // revision's local content.
+                }
+            }
+        }
+
+        return null;
+    }
+
+    private Object searchDynamicImports(
+        final String pkgName, final String name, final boolean isClass)
+        throws ClassNotFoundException, ResourceNotFoundException
+    {
+        // At this point, the module's imports were searched and so was the
+        // the module's content. Now we make an attempt to load the
+        // class/resource via a dynamic import, if possible.
+        BundleRevision provider = null;
+        try
+        {
+            provider = m_resolver.resolve(m_revision, pkgName);
+        }
+        catch (ResolveException ex)
+        {
+            // Ignore this since it is likely normal.
+        }
+        catch (BundleException ex)
+        {
+            // Ignore this since it is likely the result of a resolver hook.
+        }
+
+        // If the dynamic import was successful, then this initial
+        // time we must directly return the result from dynamically
+        // created package sources, but subsequent requests for
+        // classes/resources in the associated package will be
+        // processed as part of normal static imports.
+        if (provider != null)
+        {
+            // Return the class or resource.
+            return (isClass)
+                ? (Object) ((BundleWiringImpl) provider.getWiring()).getClassByDelegation(name)
+                : (Object) ((BundleWiringImpl) provider.getWiring()).getResourceByDelegation(name);
+        }
+
+        // If implicit boot delegation is enabled, then try to guess whether
+        // we should boot delegate.
+        if (m_implicitBootDelegation)
+        {
+            // At this point, the class/resource could not be found by the bundle's
+            // static or dynamic imports, nor its own content. Before we throw
+            // an exception, we will try to determine if the instigator of the
+            // class/resource load was a class from a bundle or not. This is necessary
+            // because the specification mandates that classes on the class path
+            // should be hidden (except for java.*), but it does allow for these
+            // classes/resources to be exposed by the system bundle as an export.
+            // However, in some situations classes on the class path make the faulty
+            // assumption that they can access everything on the class path from
+            // every other class loader that they come in contact with. This is
+            // not true if the class loader in question is from a bundle. Thus,
+            // this code tries to detect that situation. If the class instigating
+            // the load request was NOT from a bundle, then we will make the
+            // assumption that the caller actually wanted to use the parent class
+            // loader and we will delegate to it. If the class was
+            // from a bundle, then we will enforce strict class loading rules
+            // for the bundle and throw an exception.
+
+            // Get the class context to see the classes on the stack.
+            final Class[] classes = m_sm.getClassContext();
+            try
+            {
+                if (System.getSecurityManager() != null)
+                {
+                    return AccessController
+                        .doPrivileged(new PrivilegedExceptionAction()
+                        {
+                            public Object run() throws Exception
+                            {
+                                return doImplicitBootDelegation(classes, name,
+                                    isClass);
+                            }
+                        });
+                }
+                else
+                {
+                    return doImplicitBootDelegation(classes, name, isClass);
+                }
+            }
+            catch (PrivilegedActionException ex)
+            {
+                Exception cause = ex.getException();
+                if (cause instanceof ClassNotFoundException)
+                {
+                    throw (ClassNotFoundException) cause;
+                }
+                else
+                {
+                    throw (ResourceNotFoundException) cause;
+                }
+            }
+        }
+        return null;
+    }
+
+    private Object doImplicitBootDelegation(Class[] classes, String name, boolean isClass)
+        throws ClassNotFoundException, ResourceNotFoundException
+    {
+        // Start from 1 to skip security manager class.
+        for (int i = 1; i < classes.length; i++)
+        {
+            // Find the first class on the call stack that is not from
+            // the class loader that loaded the Felix classes or is not
+            // a class loader or class itself, because we want to ignore
+            // calls to ClassLoader.loadClass() and Class.forName() since
+            // we are trying to find out who instigated the class load.
+            // Also ignore inner classes of class loaders, since we can
+            // assume they are a class loader too.
+
+            // TODO: FRAMEWORK - This check is a hack and we should see if we can think
+            // of another way to do it, since it won't necessarily work in all situations.
+            // Since Felix uses threads for changing the start level
+            // and refreshing packages, it is possible that there are no
+            // bundle classes on the call stack; therefore, as soon as we
+            // see Thread on the call stack we exit this loop. Other cases
+            // where bundles actually use threads are not an issue because
+            // the bundle classes will be on the call stack before the
+            // Thread class.
+            if (Thread.class.equals(classes[i]))
+            {
+                break;
+            }
+            // Break if the current class came from a bundle, since we should
+            // not implicitly boot delegate in that case.
+            else if (isClassLoadedFromBundleRevision(classes[i]))
+            {
+                break;
+            }
+            // Break if this goes through BundleImpl because it must be a call
+            // to Bundle.loadClass() which should not implicitly boot delegate.
+            else if (BundleImpl.class.equals(classes[i]))
+            {
+                break;
+            }
+            else if (isClassExternal(classes[i]))
+            {
+                try
+                {
+                    // Return the class or resource from the parent class loader.
+                    return (isClass)
+                        ? (Object) BundleRevisionImpl.getSecureAction()
+                            .getClassLoader(this.getClass()).loadClass(name)
+                        : (Object) BundleRevisionImpl.getSecureAction()
+                            .getClassLoader(this.getClass()).getResource(name);
+                }
+                catch (NoClassDefFoundError ex)
+                {
+                    // Ignore, will return null
+                }
+                break;
+            }
+        }
+
+        return null;
+    }
+
+    private boolean isClassLoadedFromBundleRevision(Class clazz)
+    {
+        // The target class is loaded by a bundle class loader,
+        // then return true.
+        if (BundleClassLoader.class.isInstance(
+            BundleRevisionImpl.getSecureAction().getClassLoader(clazz)))
+        {
+            return true;
+        }
+
+        // If the target class was loaded from a class loader that
+        // came from a bundle, then return true.
+        ClassLoader last = null;
+        for (ClassLoader cl = BundleRevisionImpl.getSecureAction().getClassLoader(clazz);
+            (cl != null) && (last != cl);
+            cl = BundleRevisionImpl.getSecureAction().getClassLoader(cl.getClass()))
+        {
+            last = cl;
+            if (BundleClassLoader.class.isInstance(cl))
+            {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Tries to determine whether the given class is part of the framework or not.
+     * Framework classes include everything in org.apache.felix.framework.* and
+     * org.osgi.framework.*. We also consider ClassLoader and Class to be internal
+     * classes, because they are inserted into the stack trace as a result of
+     * method overloading. Typically, ClassLoader or Class will be mixed in
+     * between framework classes or will be at the point where the class loading
+     * request enters the framework class loading mechanism, which will then be
+     * followed by either bundle or external code, which will then exit our
+     * attempt to determine if we should boot delegate or not. Other standard
+     * class loaders, like URLClassLoader, are considered external classes and
+     * should trigger boot delegation. This means that bundles can create standard
+     * class loaders to get access to boot packages, but this is the standard
+     * behavior of class loaders.
+     * @param clazz the class to determine if it is external or not.
+     * @return <tt>true</tt> if the class is external, otherwise <tt>false</tt>.
+     */
+    private boolean isClassExternal(Class clazz)
+    {
+        if (clazz.getName().startsWith("org.apache.felix.framework."))
+        {
+            return false;
+        }
+        else if (clazz.getName().startsWith("org.osgi.framework."))
+        {
+            return false;
+        }
+        else if (ClassLoader.class.equals(clazz))
+        {
+            return false;
+        }
+        else if (Class.class.equals(clazz))
+        {
+            return false;
+        }
+        return true;
+    }
+
+    static class ToLocalUrlEnumeration implements Enumeration
+    {
+        final Enumeration m_enumeration;
+
+        ToLocalUrlEnumeration(Enumeration enumeration)
+        {
+            m_enumeration = enumeration;
+        }
+
+        public boolean hasMoreElements()
+        {
+            return m_enumeration.hasMoreElements();
+        }
+
+        public Object nextElement()
+        {
+            return convertToLocalUrl((URL) m_enumeration.nextElement());
+        }
+    }
+
+    public static class BundleClassLoaderJava5 extends BundleClassLoader
+    {
+        static final boolean m_isParallel;
+        static
+        {
+            boolean registered = false;
+            try
+            {
+                Method method = BundleRevisionImpl.getSecureAction()
+                    .getDeclaredMethod(ClassLoader.class, "registerAsParallelCapable", null);
+
+                BundleRevisionImpl.getSecureAction().setAccesssible(method);
+
+                registered = ((Boolean) method.invoke(null)).booleanValue();
+            }
+            catch (Throwable th)
+            {
+                // This is OK on older java versions
+            }
+
+            m_isParallel = registered;
+        }
+
+        protected boolean isParallel()
+        {
+            return m_isParallel;
+        }
+
+        private final BundleWiringImpl m_wiring;
+
+        public BundleClassLoaderJava5(BundleWiringImpl wiring, ClassLoader parent)
+        {
+            super(wiring, parent);
+            m_wiring = wiring;
+        }
+
+        @Override
+        public Enumeration getResources(String name)
+        {
+            Enumeration urls = m_wiring.getResourcesByDelegation(name);
+            if (m_wiring.m_useLocalURLs)
+            {
+                urls = new ToLocalUrlEnumeration(urls);
+            }
+            return urls;
+        }
+
+        @Override
+        protected Enumeration findResources(String name)
+        {
+            return m_wiring.m_revision.getResourcesLocal(name);
+        }
+    }
+
+    public static class BundleClassLoader extends SecureClassLoader implements BundleReference
+    {
+         static
+         {
+             try
+             {
+                 Method method = BundleRevisionImpl.getSecureAction()
+                    .getDeclaredMethod(ClassLoader.class, "registerAsParallelCapable", null);
+
+                 BundleRevisionImpl.getSecureAction().setAccesssible(method);
+
+                 method.invoke(null);
+             }
+             catch (Throwable th)
+             {
+                 // This is OK on older java versions
+             }
+         }
+
+        // Flag used to determine if a class has been loaded from this class
+        // loader or not.
+        private volatile boolean m_isActivationTriggered = false;
+
+        private final Map m_jarContentToDexFile;
+        private Object[][] m_cachedLibs = new Object[0][];
+        private static final int LIBNAME_IDX = 0;
+        private static final int LIBPATH_IDX = 1;
+        private final Map<String, Thread> m_classLocks = new HashMap<String, Thread>();
+        private final BundleWiringImpl m_wiring;
+
+        public BundleClassLoader(BundleWiringImpl wiring, ClassLoader parent)
+        {
+            super(parent);
+            if (m_dexFileClassLoadClass != null)
+            {
+                m_jarContentToDexFile = new HashMap();
+            }
+            else
+            {
+                m_jarContentToDexFile = null;
+            }
+            m_wiring = wiring;
+        }
+
+        protected boolean isParallel()
+        {
+            return false;
+        }
+
+        public boolean isActivationTriggered()
+        {
+            return m_isActivationTriggered;
+        }
+
+        public Bundle getBundle()
+        {
+            return m_wiring.getBundle();
+        }
+
+        @Override
+        protected Class loadClass(String name, boolean resolve)
+            throws ClassNotFoundException
+        {
+            Class clazz;
+
+            // Make sure the class was not already loaded.
+            Object lock = (isParallel()) ? m_classLocks : this;
+            synchronized (lock)
+            {
+                clazz = findLoadedClass(name);
+            }
+
+            if (clazz == null)
+            {
+                try
+                {
+                    clazz = (Class) m_wiring.findClassOrResourceByDelegation(name, true);
+                }
+                catch (ResourceNotFoundException ex)
+                {
+                    // This should never happen since we are asking for a class,
+                    // so just ignore it.
+                }
+                catch (ClassNotFoundException cnfe)
+                {
+                    ClassNotFoundException ex = cnfe;
+                    if (m_wiring.m_logger.getLogLevel() >= Logger.LOG_DEBUG)
+                    {
+                        String msg = diagnoseClassLoadError(m_wiring.m_resolver, m_wiring.m_revision, name);
+                        ex = (msg != null)
+                            ? new ClassNotFoundException(msg, cnfe)
+                            : ex;
+                    }
+                    throw ex;
+                }
+            }
+
+            // Resolve the class and return it.
+            if (resolve)
+            {
+                resolveClass(clazz);
+            }
+            return clazz;
+        }
+
+        @Override
+        protected Class findClass(String name) throws ClassNotFoundException
+        {
+            Class clazz = null;
+
+            // Do a quick check to try to avoid searching for classes on a
+            // disposed class loader, which will avoid some odd exception.
+            // This won't prevent all weird exception, since the wiring could
+            // still get disposed of after this check, but it will prevent
+            // some, perhaps.
+            if (m_wiring.m_isDisposed)
+            {
+                throw new ClassNotFoundException(
+                    "Unable to load class '"
+                    + name
+                    + "' because the bundle wiring for "
+                    + m_wiring.m_revision.getSymbolicName()
+                    + " is no longer valid.");
+            }
+
+            // Search for class in bundle revision.
+            if (clazz == null)
+            {
+                String actual = name.replace('.', '/') + ".class";
+
+                byte[] bytes = null;
+
+                // Check the bundle class path.
+                List<Content> contentPath = m_wiring.m_revision.getContentPath();
+                Content content = null;
+                for (int i = 0;
+                    (bytes == null) &&
+                    (i < contentPath.size()); i++)
+                {
+                    bytes = contentPath.get(i).getEntryAsBytes(actual);
+                    content = contentPath.get(i);
+                }
+
+                if (bytes != null)
+                {
+                    // Get package name.
+                    String pkgName = Util.getClassPackage(name);
+
+                    // Get weaving hooks and invoke them to give them a
+                    // chance to weave the class' byte code before we
+                    // define it.
+                    // NOTE: We don't try to dynamically track hook addition
+                    // or removal, we just get a snapshot and leave any changes
+                    // as a race condition, doing any necessary clean up in
+                    // the error handling.
+                    Felix felix = ((BundleImpl) m_wiring.m_revision.getBundle()).getFramework();
+                    Set<ServiceReference<WeavingHook>> hooks =
+                        felix.getHooks(WeavingHook.class);
+                    WovenClassImpl wci = null;
+                    if (!hooks.isEmpty())
+                    {
+                        // Create woven class to be used for hooks.
+                        wci = new WovenClassImpl(name, m_wiring, bytes);
+                        // Loop through hooks in service ranking order.
+                        for (ServiceReference<WeavingHook> sr : hooks)
+                        {
+                            // Only use the hook if it is not black listed.
+                            if (!felix.isHookBlackListed(sr))
+                            {
+                                // Get the hook service object.
+                                // Note that we don't use the bundle context
+                                // to get the service object since that would
+                                // perform sercurity checks.
+                                WeavingHook wh = felix.getService(felix, sr);
+                                if (wh != null)
+                                {
+                                    try
+                                    {
+                                        BundleRevisionImpl.getSecureAction()
+                                            .invokeWeavingHook(wh, wci);
+                                    }
+                                    catch (Throwable th)
+                                    {
+                                        if (!(th instanceof WeavingException))
+                                        {
+                                            felix.blackListHook(sr);
+                                        }
+                                        felix.fireFrameworkEvent(
+                                            FrameworkEvent.ERROR,
+                                            sr.getBundle(),
+                                            th);
+
+                                        // Mark the woven class as incomplete.
+                                        wci.complete(null, null, null);
+                                        // Throw class format exception per spec.
+                                        Error error = new ClassFormatError("Weaving hook failed.");
+                                        error.initCause(th);
+                                        throw error;
+                                    }
+                                    finally
+                                    {
+                                        felix.ungetService(felix, sr);
+                                    }
+                                }
+                            }
+                        }
+                    }
+
+                    // Before we actually attempt to define the class, grab
+                    // the lock for this class loader and make sure than no
+                    // other thread has defined this class in the meantime.
+                    Object lock = (isParallel()) ? m_classLocks : this;
+                    synchronized (lock)
+                    {
+                        Thread me = Thread.currentThread();
+                        while (m_classLocks.containsKey(name) && (m_classLocks.get(name) != me))
+                        {
+                            try
+                            {
+                                lock.wait();
+                            }
+                            catch (InterruptedException e)
+                            {
+                                // TODO: WHAT TO DO HERE?
+                                throw new RuntimeException(e);
+                            }
+                        }
+                        // Lock released, try loading class.
+                        clazz = findLoadedClass(name);
+                        if (clazz == null)
+                        {
+                            // Not found, we should try load it.
+                            m_classLocks.put(name, me);
+                        }
+                    }
+
+                    byte[] wovenBytes = null;
+                    Class wovenClass = null;
+                    List<String> wovenImports = null;
+                    try
+                    {
+                        if (clazz == null)
+                        {
+                            // If we have a woven class then get the class bytes from
+                            // it since they may have changed.
+                            // NOTE: We are taking a snapshot of these values and
+                            // are not preventing a malbehaving weaving hook from
+                            // modifying them after the fact. The price of preventing
+                            // this isn't worth it, since they can already wreck
+                            // havoc via weaving anyway. However, we do pass the
+                            // snapshot values into the woven class when we mark it
+                            // as complete so that it will refect the actual values
+                            // we used to define the class.
+                            if (wci != null)
+                            {
+                                bytes = wovenBytes = wci._getBytes();
+                                wovenImports = wci.getDynamicImportsInternal();
+
+                                // Try to add any woven dynamic imports, since they
+                                // could potentially be needed when defining the class.
+                                List<BundleRequirement> allWovenReqs =
+                                    new ArrayList<BundleRequirement>();
+                                for (String s : wovenImports)
+                                {
+                                    try
+                                    {
+                                        List<BundleRequirement> wovenReqs =
+                                            ManifestParser.parseDynamicImportHeader(
+                                                m_wiring.m_logger, m_wiring.m_revision, s);
+                                        allWovenReqs.addAll(wovenReqs);
+                                    }
+                                    catch (BundleException ex)
+                                    {
+                                        // There should be no exception here
+                                        // since we checked syntax before adding
+                                        // dynamic import strings to list.
+                                    }
+                                }
+                                // Add the dynamic requirements.
+                                if (!allWovenReqs.isEmpty())
+                                {
+                                    // Check for duplicate woven imports.
+                                    // First grab existing woven imports, if any.
+                                    Set<String> filters = new HashSet<String>();
+                                    if (m_wiring.m_wovenReqs != null)
+                                    {
+                                        for (BundleRequirement req : m_wiring.m_wovenReqs)
+                                        {
+                                            filters.add(
+                                                ((BundleRequirementImpl) req)
+                                                    .getFilter().toString());
+                                        }
+                                    }
+                                    // Then check new woven imports for duplicates
+                                    // against existing and self.
+                                    int idx = allWovenReqs.size();
+                                    while (idx < allWovenReqs.size())
+                                    {
+                                        BundleRequirement wovenReq = allWovenReqs.get(idx);
+                                        String filter = ((BundleRequirementImpl)
+                                            wovenReq).getFilter().toString();
+                                        if (!filters.contains(filter))
+                                        {
+                                            filters.add(filter);
+                                            idx++;
+                                        }
+                                        else
+                                        {
+                                            allWovenReqs.remove(idx);
+                                        }
+                                    }
+                                    // Merge existing with new imports, if any.
+                                    if (!allWovenReqs.isEmpty())
+                                    {
+                                        if (m_wiring.m_wovenReqs != null)
+                                        {
+                                            allWovenReqs.addAll(0, m_wiring.m_wovenReqs);
+                                        }
+                                        m_wiring.m_wovenReqs = allWovenReqs;
+                                    }
+                                }
+                            }
+
+                            int activationPolicy =
+                                ((BundleImpl) getBundle()).isDeclaredActivationPolicyUsed()
+                                    ? ((BundleRevisionImpl) getBundle()
+                                        .adapt(BundleRevision.class)).getDeclaredActivationPolicy()
+                                    : EAGER_ACTIVATION;
+
+                            // If the revision is using deferred activation, then if
+                            // we load this class from this revision we need to activate
+                            // the bundle before returning the class. We will short
+                            // circuit the trigger matching if the trigger is already
+                            // tripped.
+                            boolean isTriggerClass = m_isActivationTriggered
+                                ? false : m_wiring.m_revision.isActivationTrigger(pkgName);
+                            if (!m_isActivationTriggered
+                                && isTriggerClass
+                                && (activationPolicy == BundleRevisionImpl.LAZY_ACTIVATION)
+                                && (getBundle().getState() == Bundle.STARTING))
+                            {
+                                List deferredList = (List) m_deferredActivation.get();
+                                if (deferredList == null)
+                                {
+                                    deferredList = new ArrayList();
+                                    m_deferredActivation.set(deferredList);
+                                }
+                                deferredList.add(new Object[] { name, getBundle() });
+                            }
+                            // We need to try to define a Package object for the class
+                            // before we call defineClass() if we haven't already
+                            // created it.
+                            if (pkgName.length() > 0)
+                            {
+                                if (getPackage(pkgName) == null)
+                                {
+                                    Object[] params = definePackage(pkgName);
+
+                                    // This is a harmless check-then-act situation,
+                                    // where threads might be racing to create different
+                                    // classes in the same package, so catch and ignore
+                                    // any IAEs that may occur.
+                                    try
+                                    {
+                                        definePackage(
+                                            pkgName,
+                                            (String) params[0],
+                                            (String) params[1],
+                                            (String) params[2],
+                                            (String) params[3],
+                                            (String) params[4],
+                                            (String) params[5],
+                                            null);
+                                    }
+                                    catch (IllegalArgumentException ex)
+                                    {
+                                        // Ignore.
+                                    }
+                                }
+                            }
+
+                            // If we can load the class from a dex file do so
+                            if (content instanceof JarContent)
+                            {
+                                try
+                                {
+                                    clazz = getDexFileClass((JarContent) content, name, this);
+                                }
+                                catch (Exception ex)
+                                {
+                                    // Looks like we can't
+                                }
+                            }
+
+                            if (clazz == null)
+                            {
+                                // If we have a security context, then use it to
+                                // define the class with it for security purposes,
+                                // otherwise define the class without a protection domain.
+                                if (m_wiring.m_revision.getProtectionDomain() != null)
+                                {
+                                    clazz = defineClass(name, bytes, 0, bytes.length,
+                                        m_wiring.m_revision.getProtectionDomain());
+                                }
+                                else
+                                {
+                                    clazz = defineClass(name, bytes, 0, bytes.length);
+                                }
+
+                                wovenClass = clazz;
+                            }
+
+                            // At this point if we have a trigger class, then the deferred
+                            // activation trigger has tripped.
+                            if (!m_isActivationTriggered && isTriggerClass && (clazz != null))
+                            {
+                                m_isActivationTriggered = true;
+                            }
+                        }
+                    }
+                    finally
+                    {
+                        // If we have a woven class, mark it as complete.
+                        // Not exactly clear how we should deal with the
+                        // case where the weaving didn't happen because
+                        // someone else beat us in defining the class.
+                        if (wci != null)
+                        {
+                            wci.complete(wovenClass, wovenBytes, wovenImports);
+                        }
+
+                        synchronized (lock)
+                        {
+                            m_classLocks.remove(name);
+                            lock.notifyAll();
+                        }
+                    }
+
+                    // Perform deferred activation without holding the class loader lock,
+                    // if the class we are returning is the instigating class.
+                    List deferredList = (List) m_deferredActivation.get();
+                    if ((deferredList != null)
+                        && (deferredList.size() > 0)
+                        && ((Object[]) deferredList.get(0))[0].equals(name))
+                    {
+                        // Null the deferred list.
+                        m_deferredActivation.set(null);
+                        while (!deferredList.isEmpty())
+                        {
+                            // Lazy bundles should be activated in the reverse order
+                            // of when they were added to the deferred list, so grab
+                            // them from the end of the deferred list.
+                            Object[] lazy = (Object[]) deferredList.remove(deferredList.size() - 1);
+                            try
+                            {
+                                felix.getFramework().activateBundle((BundleImpl) (lazy)[1], true);
+                            }
+                            catch (Throwable ex)
+                            {
+                                m_wiring.m_logger.log((BundleImpl) (lazy)[1],
+                                    Logger.LOG_WARNING,
+                                    "Unable to lazily start bundle.",
+                                    ex);
+                            }
+                        }
+                    }
+                }
+            }
+
+            return clazz;
+        }
+
+        private Object[] definePackage(String pkgName)
+        {
+            String spectitle = (String) m_wiring.m_revision.getHeaders().get("Specification-Title");
+            String specversion = (String) m_wiring.m_revision.getHeaders().get("Specification-Version");
+            String specvendor = (String) m_wiring.m_revision.getHeaders().get("Specification-Vendor");
+            String impltitle = (String) m_wiring.m_revision.getHeaders().get("Implementation-Title");
+            String implversion = (String) m_wiring.m_revision.getHeaders().get("Implementation-Version");
+            String implvendor = (String) m_wiring.m_revision.getHeaders().get("Implementation-Vendor");
+            if ((spectitle != null)
+                || (specversion != null)
+                || (specvendor != null)
+                || (impltitle != null)
+                || (implversion != null)
+                || (implvendor != null))
+            {
+                return new Object[] {
+                    spectitle, specversion, specvendor, impltitle, implversion, implvendor
+                };
+            }
+            return new Object[] {null, null, null, null, null, null};
+        }
+
+        private Class getDexFileClass(JarContent content, String name, ClassLoader loader)
+            throws Exception
+        {
+            if (m_jarContentToDexFile == null)
+            {
+                return null;
+            }
+
+            Object dexFile = null;
+
+            if (!m_jarContentToDexFile.containsKey(content))
+            {
+                try
+                {
+                    if (m_dexFileClassLoadDex != null)
+                    {
+                        dexFile = m_dexFileClassLoadDex.invoke(null,
+                            new Object[]{content.getFile().getAbsolutePath(),
+                                content.getFile().getAbsolutePath() + ".dex", new Integer(0)});
+                    }
+                    else
+                    {
+                        dexFile = m_dexFileClassConstructor.newInstance(
+                            new Object[] { content.getFile() });
+                    }
+                }
+                finally
+                {
+                    m_jarContentToDexFile.put(content, dexFile);
+                }
+            }
+            else
+            {
+                dexFile = m_jarContentToDexFile.get(content);
+            }
+
+            if (dexFile != null)
+            {
+                return (Class) m_dexFileClassLoadClass.invoke(dexFile,
+                    new Object[] { name.replace('.','/'), loader });
+            }
+            return null;
+        }
+
+        @Override
+        public URL getResource(String name)
+        {
+            URL url = m_wiring.getResourceByDelegation(name);
+            if (m_wiring.m_useLocalURLs)
+            {
+                url = convertToLocalUrl(url);
+            }
+            return url;
+        }
+
+        @Override
+        protected URL findResource(String name)
+        {
+            return m_wiring.m_revision.getResourceLocal(name);
+        }
+
+        // The findResources() method should only look at the revision itself, but
+        // instead it tries to delegate because in Java version prior to 1.5 the
+        // getResources() method was final and could not be overridden. We should
+        // override getResources() like getResource() to make it delegate, but we
+        // can't. As a workaround, we make findResources() delegate instead.
+        @Override
+        protected Enumeration findResources(String name)
+        {
+            Enumeration urls = m_wiring.getResourcesByDelegation(name);
+            if (m_wiring.m_useLocalURLs)
+            {
+                urls = new ToLocalUrlEnumeration(urls);
+            }
+            return urls;
+        }
+
+        @Override
+        protected String findLibrary(String name)
+        {
+            // Remove leading slash, if present.
+            if (name.startsWith("/"))
+            {
+                name = name.substring(1);
+            }
+
+            String result = null;
+            // CONCURRENCY: In the long run, we might want to break this
+            // sync block in two to avoid manipulating the cache while
+            // holding the lock, but for now we will do it the simple way.
+            synchronized (this)
+            {
+                // Check to make sure we haven't already found this library.
+                for (int i = 0; (result == null) && (i < m_cachedLibs.length); i++)
+                {
+                    if (m_cachedLibs[i][LIBNAME_IDX].equals(name))
+                    {
+                        result = (String) m_cachedLibs[i][LIBPATH_IDX];
+                    }
+                }
+
+                // If we don't have a cached result, see if we have a matching
+                // native library.
+                if (result == null)
+                {
+                    List<R4Library> libs = m_wiring.getNativeLibraries();
+                    for (int libIdx = 0; (libs != null) && (libIdx < libs.size()); libIdx++)
+                    {
+                        if (libs.get(libIdx).match(m_wiring.m_configMap, name))
+                        {
+                            // Search bundle content first for native library.
+                            result = m_wiring.m_revision.getContent().getEntryAsNativeLibrary(
+                                libs.get(libIdx).getEntryName());
+                            // If not found, then search fragments in order.
+                            for (int i = 0;
+                                (result == null) && (m_wiring.m_fragmentContents != null)
+                                    && (i < m_wiring.m_fragmentContents.size());
+                                i++)
+                            {
+                                result = m_wiring.m_fragmentContents.get(i).getEntryAsNativeLibrary(
+                                    libs.get(libIdx).getEntryName());
+                            }
+                        }
+                    }
+
+                    // Remember the result for future requests.
+                    if (result != null)
+                    {
+                        Object[][] tmp = new Object[m_cachedLibs.length + 1][];
+                        System.arraycopy(m_cachedLibs, 0, tmp, 0, m_cachedLibs.length);
+                        tmp[m_cachedLibs.length] = new Object[] { name, result };
+                        m_cachedLibs = tmp;
+                    }
+                }
+            }
+
+            return result;
+        }
+
+        @Override
+        public String toString()
+        {
+            return m_wiring.toString();
+        }
+    }
+
+    static URL convertToLocalUrl(URL url)
+    {
+        if (url.getProtocol().equals("bundle"))
+        {
+            try
+            {
+                url = ((URLHandlersBundleURLConnection)
+                    url.openConnection()).getLocalURL();
+            }
+            catch (IOException ex)
+            {
+                // Ignore and add original url.
+            }
+        }
+        return url;
+    }
+
+    private static class ResourceSource implements Comparable<ResourceSource>
+    {
+        public final String m_resource;
+        public final BundleRevision m_revision;
+
+        public ResourceSource(String resource, BundleRevision revision)
+        {
+            m_resource = resource;
+            m_revision = revision;
+        }
+
+        @Override
+        public boolean equals(Object o)
+        {
+            if (o instanceof ResourceSource)
+            {
+                return m_resource.equals(((ResourceSource) o).m_resource);
+            }
+            return false;
+        }
+
+        @Override
+        public int hashCode()
+        {
+            return m_resource.hashCode();
+        }
+
+        public int compareTo(ResourceSource t)
+        {
+            return m_resource.compareTo(t.m_resource);
+        }
+
+        @Override
+        public String toString()
+        {
+            return m_resource
+                + " -> "
+                + m_revision.getSymbolicName()
+                + " [" + m_revision + "]";
+        }
+    }
+
+    private static String diagnoseClassLoadError(
+        StatefulResolver resolver, BundleRevision revision, String name)
+    {
+        // We will try to do some diagnostics here to help the developer
+        // deal with this exception.
+
+        // Get package name.
+        String pkgName = Util.getClassPackage(name);
+        if (pkgName.length() == 0)
+        {
+            return null;
+        }
+
+        // First, get the bundle string of the revision doing the class loader.
+        String importer = revision.getBundle().toString();
+
+        // Next, check to see if the revision imports the package.
+        List<BundleWire> wires = (revision.getWiring() == null)
+            ? null : revision.getWiring().getProvidedWires(null);
+        for (int i = 0; (wires != null) && (i < wires.size()); i++)
+        {
+            if (wires.get(i).getCapability().getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE) &&
+                wires.get(i).getCapability().getAttributes().get(BundleRevision.PACKAGE_NAMESPACE).equals(pkgName))
+            {
+                String exporter = wires.get(i).getProviderWiring().getBundle().toString();
+
+                StringBuffer sb = new StringBuffer("*** Package '");
+                sb.append(pkgName);
+                sb.append("' is imported by bundle ");
+                sb.append(importer);
+                sb.append(" from bundle ");
+                sb.append(exporter);
+                sb.append(", but the exported package from bundle ");
+                sb.append(exporter);
+                sb.append(" does not contain the requested class '");
+                sb.append(name);
+                sb.append("'. Please verify that the class name is correct in the importing bundle ");
+                sb.append(importer);
+                sb.append(" and/or that the exported package is correctly bundled in ");
+                sb.append(exporter);
+                sb.append(". ***");
+
+                return sb.toString();
+            }
+        }
+
+        // Next, check to see if the package was optionally imported and
+        // whether or not there is an exporter available.
+        List<BundleRequirement> reqs = revision.getWiring().getRequirements(null);
+/*
+* TODO: RB - Fix diagnostic message for optional imports.
+        for (int i = 0; (reqs != null) && (i < reqs.length); i++)
+        {
+            if (reqs[i].getName().equals(pkgName) && reqs[i].isOptional())
+            {
+                // Try to see if there is an exporter available.
+                IModule[] exporters = getResolvedExporters(reqs[i], true);
+                exporters = (exporters.length == 0)
+                    ? getUnresolvedExporters(reqs[i], true) : exporters;
+
+                // An exporter might be available, but it may have attributes
+                // that do not match the importer's required attributes, so
+                // check that case by simply looking for an exporter of the
+                // desired package without any attributes.
+                if (exporters.length == 0)
+                {
+                    IRequirement pkgReq = new Requirement(
+                        ICapability.PACKAGE_NAMESPACE, "(package=" + pkgName + ")");
+                    exporters = getResolvedExporters(pkgReq, true);
+                    exporters = (exporters.length == 0)
+                        ? getUnresolvedExporters(pkgReq, true) : exporters;
+                }
+
+                long expId = (exporters.length == 0)
+                    ? -1 : Util.getBundleIdFromModuleId(exporters[0].getId());
+
+                StringBuffer sb = new StringBuffer("*** Class '");
+                sb.append(name);
+                sb.append("' was not found, but this is likely normal since package '");
+                sb.append(pkgName);
+                sb.append("' is optionally imported by bundle ");
+                sb.append(impId);
+                sb.append(".");
+                if (exporters.length > 0)
+                {
+                    sb.append(" However, bundle ");
+                    sb.append(expId);
+                    if (reqs[i].isSatisfied(
+                        Util.getExportPackage(exporters[0], reqs[i].getName())))
+                    {
+                        sb.append(" does export this package. Bundle ");
+                        sb.append(expId);
+                        sb.append(" must be installed before bundle ");
+                        sb.append(impId);
+                        sb.append(" is resolved or else the optional import will be ignored.");
+                    }
+                    else
+                    {
+                        sb.append(" does export this package with attributes that do not match.");
+                    }
+                }
+                sb.append(" ***");
+
+                return sb.toString();
+            }
+        }
+*/
+        // Next, check to see if the package is dynamically imported by the revision.
+        if (resolver.isAllowedDynamicImport(revision, pkgName))
+        {
+            // Try to see if there is an exporter available.
+            Map<String, String> dirs = Collections.EMPTY_MAP;
+            Map<String, Object> attrs = Collections.singletonMap(
+                BundleRevision.PACKAGE_NAMESPACE, (Object) pkgName);
+            BundleRequirementImpl req = new BundleRequirementImpl(
+                revision, BundleRevision.PACKAGE_NAMESPACE, dirs, attrs);
+            List<BundleCapability> exporters = resolver.findProviders(req, false);
+
+            BundleRevision provider = null;
+            try
+            {
+                provider = resolver.resolve(revision, pkgName);
+            }
+            catch (Exception ex)
+            {
+                provider = null;
+            }
+
+            String exporter = (exporters.isEmpty())
+                ? null : exporters.iterator().next().getRevision().getBundle().toString();
+
+            StringBuffer sb = new StringBuffer("*** Class '");
+            sb.append(name);
+            sb.append("' was not found, but this is likely normal since package '");
+            sb.append(pkgName);
+            sb.append("' is dynamically imported by bundle ");
+            sb.append(importer);
+            sb.append(".");
+            if ((exporters.size() > 0) && (provider == null))
+            {
+                sb.append(" However, bundle ");
+                sb.append(exporter);
+                sb.append(" does export this package with attributes that do not match.");
+            }
+            sb.append(" ***");
+
+            return sb.toString();
+        }
+
+        // Next, check to see if there are any exporters for the package at all.
+        Map<String, String> dirs = Collections.EMPTY_MAP;
+        Map<String, Object> attrs = Collections.singletonMap(
+            BundleRevision.PACKAGE_NAMESPACE, (Object) pkgName);
+        BundleRequirementImpl req = new BundleRequirementImpl(
+            revision, BundleRevision.PACKAGE_NAMESPACE, dirs, attrs);
+        List<BundleCapability> exports = resolver.findProviders(req, false);
+        if (exports.size() > 0)
+        {
+            boolean classpath = false;
+            try
+            {
+                BundleRevisionImpl.getSecureAction()
+                    .getClassLoader(BundleClassLoader.class).loadClass(name);
+                classpath = true;
+            }
+            catch (NoClassDefFoundError err)
+            {
+                // Ignore
+            }
+            catch (Exception ex)
+            {
+                // Ignore
+            }
+
+            String exporter = exports.iterator().next().getRevision().getBundle().toString();
+
+            StringBuffer sb = new StringBuffer("*** Class '");
+            sb.append(name);
+            sb.append("' was not found because bundle ");
+            sb.append(importer);
+            sb.append(" does not import '");
+            sb.append(pkgName);
+            sb.append("' even though bundle ");
+            sb.append(exporter);
+            sb.append(" does export it.");
+            if (classpath)
+            {
+                sb.append(" Additionally, the class is also available from the system class loader. There are two fixes: 1) Add an import for '");
+                sb.append(pkgName);
+                sb.append("' to bundle ");
+                sb.append(importer);
+                sb.append("; imports are necessary for each class directly touched by bundle code or indirectly touched, such as super classes if their methods are used. ");
+                sb.append("2) Add package '");
+                sb.append(pkgName);
+                sb.append("' to the '");
+                sb.append(Constants.FRAMEWORK_BOOTDELEGATION);
+                sb.append("' property; a library or VM bug can cause classes to be loaded by the wrong class loader. The first approach is preferable for preserving modularity.");
+            }
+            else
+            {
+                sb.append(" To resolve this issue, add an import for '");
+                sb.append(pkgName);
+                sb.append("' to bundle ");
+                sb.append(importer);
+                sb.append(".");
+            }
+            sb.append(" ***");
+
+            return sb.toString();
+        }
+
+        // Next, try to see if the class is available from the system
+        // class loader.
+        try
+        {
+            BundleRevisionImpl.getSecureAction()
+                .getClassLoader(BundleClassLoader.class).loadClass(name);
+
+            StringBuffer sb = new StringBuffer("*** Package '");
+            sb.append(pkgName);
+            sb.append("' is not imported by bundle ");
+            sb.append(importer);
+            sb.append(", nor is there any bundle that exports package '");
+            sb.append(pkgName);
+            sb.append("'. However, the class '");
+            sb.append(name);
+            sb.append("' is available from the system class loader. There are two fixes: 1) Add package '");
+            sb.append(pkgName);
+            sb.append("' to the '");
+            sb.append(Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA);
+            sb.append("' property and modify bundle ");
+            sb.append(importer);
+            sb.append(" to import this package; this causes the system bundle to export class path packages. 2) Add package '");
+            sb.append(pkgName);
+            sb.append("' to the '");
+            sb.append(Constants.FRAMEWORK_BOOTDELEGATION);
+            sb.append("' property; a library or VM bug can cause classes to be loaded by the wrong class loader. The first approach is preferable for preserving modularity.");
+            sb.append(" ***");
+
+            return sb.toString();
+        }
+        catch (Exception ex2)
+        {
+        }
+
+        // Finally, if there are no imports or exports for the package
+        // and it is not available on the system class path, simply
+        // log a message saying so.
+        StringBuffer sb = new StringBuffer("*** Class '");
+        sb.append(name);
+        sb.append("' was not found. Bundle ");
+        sb.append(importer);
+        sb.append(" does not import package '");
+        sb.append(pkgName);
+        sb.append("', nor is the package exported by any other bundle or available from the system class loader.");
+        sb.append(" ***");
+
+        return sb.toString();
+    }
+}
diff --git a/src/main/java/org/apache/felix/framework/EntryFilterEnumeration.java b/src/main/java/org/apache/felix/framework/EntryFilterEnumeration.java
new file mode 100644
index 0000000..290de06
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/EntryFilterEnumeration.java
@@ -0,0 +1,245 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.*;
+import org.apache.felix.framework.capabilityset.SimpleFilter;
+import org.apache.felix.framework.util.Util;
+import org.osgi.framework.wiring.BundleRevision;
+
+class EntryFilterEnumeration implements Enumeration
+{
+    private final BundleRevision m_revision;
+    private final List<Enumeration> m_enumerations;
+    private final List<BundleRevision> m_revisions;
+    private int m_revisionIndex = 0;
+    private final String m_path;
+    private final List<String> m_filePattern;
+    private final boolean m_recurse;
+    private final boolean m_isURLValues;
+    private final Set<String> m_dirEntries = new HashSet();
+    private final List<Object> m_nextEntries = new ArrayList(2);
+
+    public EntryFilterEnumeration(
+        BundleRevision revision, boolean includeFragments, String path,
+        String filePattern, boolean recurse, boolean isURLValues)
+    {
+        m_revision = revision;
+        List<BundleRevision> fragments = Util.getFragments(revision.getWiring());
+        if (includeFragments && !fragments.isEmpty())
+        {
+            m_revisions = fragments;
+        }
+        else
+        {
+            m_revisions = new ArrayList(1);
+        }
+        m_revisions.add(0, m_revision);
+        m_enumerations = new ArrayList(m_revisions.size());
+        for (int i = 0; i < m_revisions.size(); i++)
+        {
+            m_enumerations.add(((BundleRevisionImpl) m_revisions.get(i)).getContent() != null ?
+                ((BundleRevisionImpl) m_revisions.get(i)).getContent().getEntries() : null);
+        }
+        m_recurse = recurse;
+        m_isURLValues = isURLValues;
+
+        // Sanity check the parameters.
+        if (path == null)
+        {
+            throw new IllegalArgumentException("The path for findEntries() cannot be null.");
+        }
+        // Strip leading '/' if present.
+        if ((path.length() > 0) && (path.charAt(0) == '/'))
+        {
+            path = path.substring(1);
+        }
+        // Add a '/' to the end if not present.
+        if ((path.length() > 0) && (path.charAt(path.length() - 1) != '/'))
+        {
+            path = path + "/";
+        }
+        m_path = path;
+
+        // File pattern defaults to "*" if not specified.
+        filePattern = (filePattern == null) ? "*" : filePattern;
+
+        m_filePattern = SimpleFilter.parseSubstring(filePattern);
+
+        findNext();
+    }
+
+    public synchronized boolean hasMoreElements()
+    {
+        return !m_nextEntries.isEmpty();
+    }
+
+    public synchronized Object nextElement()
+    {
+        if (m_nextEntries.isEmpty())
+        {
+            throw new NoSuchElementException("No more entries.");
+        }
+        Object last = m_nextEntries.remove(0);
+        findNext();
+        return last;
+    }
+
+    private void findNext()
+    {
+        // This method filters the content entry enumeration, such that
+        // it only displays the contents of the directory specified by
+        // the path argument either recursively or not; much like using
+        // "ls -R" or "ls" to list the contents of a directory, respectively.
+        if (m_enumerations == null)
+        {
+            return;
+        }
+        while ((m_revisionIndex < m_enumerations.size()) && m_nextEntries.isEmpty())
+        {
+            while (m_enumerations.get(m_revisionIndex) != null
+                && m_enumerations.get(m_revisionIndex).hasMoreElements()
+                && m_nextEntries.isEmpty())
+            {
+                // Get the current entry to determine if it should be filtered or not.
+                String entryName = (String) m_enumerations.get(m_revisionIndex).nextElement();
+                // Check to see if the current entry is a descendent of the specified path.
+                if (!entryName.equals(m_path) && entryName.startsWith(m_path))
+                {
+                    // Cached entry URL. If we are returning URLs, we use this
+                    // cached URL to avoid doing multiple URL lookups from a revision
+                    // when synthesizing directory URLs.
+                    URL entryURL = null;
+
+                    // If the current entry is in a subdirectory of the specified path,
+                    // get the index of the slash character.
+                    int dirSlashIdx = entryName.indexOf('/', m_path.length());
+
+                    // JAR files are supposed to contain entries for directories,
+                    // but not all do. So determine the directory for this entry
+                    // and see if we've already seen an entry for for it. If not,
+                    // synthesize an entry for it. If we are doing a recursive
+                    // match, we need to synthesize each matching subdirectory
+                    // of the entry.
+                    if (dirSlashIdx >= 0)
+                    {
+                        // Start synthesizing directories for the current entry
+                        // at the subdirectory after the initial path.
+                        int subDirSlashIdx = dirSlashIdx;
+                        String dir;
+                        do
+                        {
+                            // Calculate the subdirectory name.
+                            dir = entryName.substring(0, subDirSlashIdx + 1);
+                            // If we have not seen this directory before, then record
+                            // it and add it to the list of available next entries. If
+                            // the original entry is actually a directory, then it will
+                            // be added to next entries like normal if it matches, but if
+                            // it is not a directory, its parent directory entries may be
+                            // synthesized depending on the matching filter and recursion.
+                            if (!m_dirEntries.contains(dir))
+                            {
+                                // Record the directory entry.
+                                m_dirEntries.add(dir);
+                                // Add the directory to the list of
+                                if (SimpleFilter.compareSubstring(
+                                    m_filePattern, getLastPathElement(dir)))
+                                {
+                                    // Add synthesized directory entry to the next
+                                    // entries list in the correct form.
+                                    if (m_isURLValues)
+                                    {
+                                        entryURL = (entryURL == null)
+                                            ? ((BundleRevisionImpl) m_revisions.
+                                                get(m_revisionIndex)).getEntry(entryName)
+                                            : entryURL;
+                                        try
+                                        {
+                                            m_nextEntries.add(new URL(entryURL, "/" + dir));
+                                        }
+                                        catch (MalformedURLException ex)
+                                        {
+                                        }
+                                    }
+                                    else
+                                    {
+                                        m_nextEntries.add(dir);
+                                    }
+                                }
+                            }
+                            // Now prepare to synthesize the next subdirectory
+                            // if we are matching recursively.
+                            subDirSlashIdx = entryName.indexOf('/', dir.length());
+                        }
+                        while (m_recurse && (subDirSlashIdx >= 0));
+                    }
+
+                    // Now we actually need to check if the current entry itself should
+                    // be filtered or not. If we are recursive or the current entry
+                    // is a child (not a grandchild) of the initial path, then we need
+                    // to check if it matches the file pattern. If we've already added
+                    // or synthesized the directory entry, then we can ignore it.
+                    if (!m_dirEntries.contains(entryName)
+                        && (m_recurse || (dirSlashIdx < 0)
+                            || (dirSlashIdx == entryName.length() - 1)))
+                    {
+                        // See if the file pattern matches the last element of the path.
+                        if (SimpleFilter.compareSubstring(
+                            m_filePattern, getLastPathElement(entryName)))
+                        {
+                            if (m_isURLValues)
+                            {
+                                entryURL = (entryURL == null)
+                                    ? ((BundleRevisionImpl)
+                                        m_revisions.get(m_revisionIndex)).getEntry(entryName)
+                                    : entryURL;
+                                m_nextEntries.add(entryURL);
+                            }
+                            else
+                            {
+                                m_nextEntries.add(entryName);
+                            }
+                        }
+                    }
+                }
+            }
+            if (m_nextEntries.isEmpty())
+            {
+                m_revisionIndex++;
+                // Reset directory entries, since fragments may
+                // have overlapping directory entries that need
+                // to be returned.
+                m_dirEntries.clear();
+            }
+        }
+    }
+
+    private static String getLastPathElement(String entryName)
+    {
+        int endIdx = (entryName.charAt(entryName.length() - 1) == '/')
+            ? entryName.length() - 1
+            : entryName.length();
+        int startIdx = (entryName.charAt(entryName.length() - 1) == '/')
+            ? entryName.lastIndexOf('/', endIdx - 1) + 1
+            : entryName.lastIndexOf('/', endIdx) + 1;
+        return entryName.substring(startIdx, endIdx);
+    }
+}
diff --git a/src/main/java/org/apache/felix/framework/ExportedPackageImpl.java b/src/main/java/org/apache/felix/framework/ExportedPackageImpl.java
new file mode 100644
index 0000000..be2b1db
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/ExportedPackageImpl.java
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework;
+
+import java.util.Set;
+import org.apache.felix.framework.wiring.BundleCapabilityImpl;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.Version;
+import org.osgi.framework.wiring.BundleCapability;
+import org.osgi.framework.wiring.BundleRevision;
+import org.osgi.service.packageadmin.ExportedPackage;
+
+class ExportedPackageImpl implements ExportedPackage
+{
+    private final Felix m_felix;
+    private final BundleImpl m_exportingBundle;
+    private final BundleRevision m_exportingRevision;
+    private final BundleCapability m_export;
+    private final String m_pkgName;
+    private final Version m_version;
+
+    public ExportedPackageImpl(
+        Felix felix, BundleImpl exporter, BundleRevision revision, BundleCapability export)
+    {
+        m_felix = felix;
+        m_exportingBundle = exporter;
+        m_exportingRevision = revision;
+        m_export = export;
+        m_pkgName = (String) m_export.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE);
+        m_version = (!m_export.getAttributes().containsKey(BundleCapabilityImpl.VERSION_ATTR))
+            ? Version.emptyVersion
+            : (Version) m_export.getAttributes().get(BundleCapabilityImpl.VERSION_ATTR);
+    }
+
+    public Bundle getExportingBundle()
+    {
+        // If the package is stale, then return null per the spec.
+        if (m_exportingBundle.isStale())
+        {
+            return null;
+        }
+        return m_exportingBundle;
+    }
+
+    public Bundle[] getImportingBundles()
+    {
+        // If the package is stale, then return null per the spec.
+        if (m_exportingBundle.isStale())
+        {
+            return null;
+        }
+        Set<Bundle> set = m_felix.getImportingBundles(m_exportingBundle, m_export);
+        return set.toArray(new Bundle[set.size()]);
+    }
+
+    public String getName()
+    {
+        return m_pkgName;
+    }
+
+    public String getSpecificationVersion()
+    {
+        return m_version.toString();
+    }
+
+    public Version getVersion()
+    {
+        return m_version;
+    }
+
+    public boolean isRemovalPending()
+    {
+        return m_exportingBundle.isRemovalPending();
+    }
+
+    public String toString()
+    {
+        return m_pkgName + "; version=" + m_version;
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/felix/framework/ExtensionManager.java b/src/main/java/org/apache/felix/framework/ExtensionManager.java
new file mode 100644
index 0000000..002cea3
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/ExtensionManager.java
@@ -0,0 +1,903 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.io.InputStream;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.URLStreamHandler;
+import java.security.AllPermission;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.NoSuchElementException;
+import java.util.Set;
+import org.apache.felix.framework.util.FelixConstants;
+import org.apache.felix.framework.util.ImmutableList;
+import org.apache.felix.framework.util.StringMap;
+import org.apache.felix.framework.util.Util;
+import org.apache.felix.framework.util.manifestparser.ManifestParser;
+import org.apache.felix.framework.cache.Content;
+import org.apache.felix.framework.util.manifestparser.R4Library;
+import org.apache.felix.framework.wiring.BundleCapabilityImpl;
+import org.apache.felix.framework.wiring.BundleWireImpl;
+import org.osgi.framework.AdminPermission;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Version;
+import org.osgi.framework.wiring.BundleCapability;
+import org.osgi.framework.wiring.BundleRequirement;
+import org.osgi.framework.wiring.BundleRevision;
+import org.osgi.framework.wiring.BundleWire;
+import org.osgi.framework.wiring.BundleWiring;
+
+/**
+ * The ExtensionManager class is used in several ways.
+ * <p>
+ * First, a private instance is added (as URL with the instance as
+ * URLStreamHandler) to the classloader that loaded the class.
+ * It is assumed that this is an instance of URLClassloader (if not extension
+ * bundles will not work). Subsequently, extension bundles can be managed by
+ * instances of this class (their will be one instance per framework instance).
+ * </p>
+ * <p>
+ * Second, it is used as module definition of the systembundle. Added extension
+ * bundles with exported packages will contribute their exports to the
+ * systembundle export.
+ * </p>
+ * <p>
+ * Third, it is used as content loader of the systembundle. Added extension
+ * bundles exports will be available via this loader.
+ * </p>
+ */
+// The general approach is to have one private static instance that we register
+// with the parent classloader and one instance per framework instance that
+// keeps track of extension bundles and systembundle exports for that framework
+// instance.
+class ExtensionManager extends URLStreamHandler implements Content
+{
+    // The private instance that is added to Felix.class.getClassLoader() -
+    // will be null if extension bundles are not supported (i.e., we are not
+    // loaded by an instance of URLClassLoader)
+    static final ExtensionManager m_extensionManager;
+
+    static
+    {
+        // pre-init the url sub-system as otherwise we don't work on gnu/classpath
+        ExtensionManager extensionManager = new ExtensionManager();
+        try
+        {
+            (new URL("http://felix.extensions:9/")).openConnection();
+        }
+        catch (Throwable t)
+        {
+            // This doesn't matter much - we only need the above to init the url subsystem
+        }
+
+        // We use the secure action of Felix to add a new instance to the parent
+        // classloader.
+        try
+        {
+            Felix.m_secureAction.addURLToURLClassLoader(Felix.m_secureAction.createURL(
+                Felix.m_secureAction.createURL(null, "http:", extensionManager),
+                "http://felix.extensions:9/", extensionManager),
+                Felix.class.getClassLoader());
+        }
+        catch (Throwable ex)
+        {
+            // extension bundles will not be supported.
+            extensionManager = null;
+        }
+        m_extensionManager = extensionManager;
+    }
+
+    private final Logger m_logger;
+    private final Map m_configMap;
+    private final Map m_headerMap = new StringMap();
+    private final BundleRevision m_systemBundleRevision;
+    private volatile List<BundleCapability> m_capabilities = Collections.EMPTY_LIST;
+    private volatile Set<String> m_exportNames = Collections.EMPTY_SET;
+    private volatile Object m_securityContext = null;
+    private final List m_extensions;
+    private volatile Bundle[] m_extensionsCache;
+    private final Set m_names;
+    private final Map m_sourceToExtensions;
+
+    // This constructor is only used for the private instance added to the parent
+    // classloader.
+    private ExtensionManager()
+    {
+        m_logger = null;
+        m_configMap = null;
+        m_systemBundleRevision = null;
+        m_extensions = new ArrayList();
+        m_extensionsCache = new Bundle[0];
+        m_names = new HashSet();
+        m_sourceToExtensions = new HashMap();
+    }
+
+    /**
+     * This constructor is used to create one instance per framework instance.
+     * The general approach is to have one private static instance that we register
+     * with the parent classloader and one instance per framework instance that
+     * keeps track of extension bundles and systembundle exports for that framework
+     * instance.
+     *
+     * @param logger the logger to use.
+     * @param config the configuration to read properties from.
+     * @param systemBundleInfo the info to change if we need to add exports.
+     */
+    ExtensionManager(Logger logger, Map configMap, Felix felix)
+    {
+        m_logger = logger;
+        m_configMap = configMap;
+        m_systemBundleRevision = new ExtensionManagerRevision(felix);
+        m_extensions = null;
+        m_extensionsCache = null;
+        m_names = null;
+        m_sourceToExtensions = null;
+
+// TODO: FRAMEWORK - Not all of this stuff really belongs here, probably only exports.
+        // Populate system bundle header map.
+        m_headerMap.put(FelixConstants.BUNDLE_VERSION,
+            m_configMap.get(FelixConstants.FELIX_VERSION_PROPERTY));
+        m_headerMap.put(FelixConstants.BUNDLE_SYMBOLICNAME,
+            FelixConstants.SYSTEM_BUNDLE_SYMBOLICNAME);
+        m_headerMap.put(FelixConstants.BUNDLE_NAME, "System Bundle");
+        m_headerMap.put(FelixConstants.BUNDLE_DESCRIPTION,
+            "This bundle is system specific; it implements various system services.");
+        m_headerMap.put(FelixConstants.EXPORT_SERVICE,
+            "org.osgi.service.packageadmin.PackageAdmin," +
+            "org.osgi.service.startlevel.StartLevel," +
+            "org.osgi.service.url.URLHandlers");
+
+        // The system bundle exports framework packages as well as
+        // arbitrary user-defined packages from the system class path.
+        // We must construct the system bundle's export metadata.
+        // Get configuration property that specifies which class path
+        // packages should be exported by the system bundle.
+        String syspkgs =
+            (String) m_configMap.get(FelixConstants.FRAMEWORK_SYSTEMPACKAGES);
+        // If no system packages were specified, load our default value.
+        syspkgs = (syspkgs == null)
+            ? Util.getDefaultProperty(logger, Constants.FRAMEWORK_SYSTEMPACKAGES)
+            : syspkgs;
+        syspkgs = (syspkgs == null) ? "" : syspkgs;
+        // If any extra packages are specified, then append them.
+        String pkgextra =
+            (String) m_configMap.get(FelixConstants.FRAMEWORK_SYSTEMPACKAGES_EXTRA);
+        syspkgs = ((pkgextra == null) || (pkgextra.trim().length() == 0))
+            ? syspkgs : syspkgs + "," + pkgextra;
+        m_headerMap.put(FelixConstants.BUNDLE_MANIFESTVERSION, "2");
+        m_headerMap.put(FelixConstants.EXPORT_PACKAGE, syspkgs);
+
+        // The system bundle alsp provides framework generic capabilities
+        // as well as arbitrary user-defined generic capabilities. We must
+        // construct the system bundle's capabilitie metadata. Get the
+        // configuration property that specifies which capabilities should
+        // be provided by the system bundle.
+        String syscaps =
+            (String) m_configMap.get(FelixConstants.FRAMEWORK_SYSTEMCAPABILITIES);
+        // If no system capabilities were specified, load our default value.
+        syscaps = (syscaps == null)
+            ? Util.getDefaultProperty(logger, Constants.FRAMEWORK_SYSTEMCAPABILITIES)
+            : syscaps;
+        syscaps = (syscaps == null) ? "" : syscaps;
+        // If any extra capabilities are specified, then append them.
+        String capextra =
+            (String) m_configMap.get(FelixConstants.FRAMEWORK_SYSTEMCAPABILITIES_EXTRA);
+        syscaps = ((capextra == null) || (capextra.trim().length() == 0))
+            ? syscaps : syscaps + "," + capextra;
+        m_headerMap.put(FelixConstants.PROVIDE_CAPABILITY, syscaps);
+        try
+        {
+            ManifestParser mp = new ManifestParser(
+                m_logger, m_configMap, m_systemBundleRevision, m_headerMap);
+            List<BundleCapability> caps = aliasSymbolicName(mp.getCapabilities());
+            appendCapabilities(caps);
+        }
+        catch (Exception ex)
+        {
+            m_capabilities = Collections.EMPTY_LIST;
+            m_logger.log(
+                Logger.LOG_ERROR,
+                "Error parsing system bundle export statement: "
+                + syspkgs, ex);
+        }
+    }
+
+    private static List<BundleCapability> aliasSymbolicName(List<BundleCapability> caps)
+    {
+        if (caps == null)
+        {
+            return new ArrayList<BundleCapability>(0);
+        }
+
+        List<BundleCapability> aliasCaps = new ArrayList<BundleCapability>(caps);
+
+        String[] aliases = {
+            FelixConstants.SYSTEM_BUNDLE_SYMBOLICNAME,
+            Constants.SYSTEM_BUNDLE_SYMBOLICNAME };
+
+        for (int capIdx = 0; capIdx < aliasCaps.size(); capIdx++)
+        {
+            BundleCapability cap = aliasCaps.get(capIdx);
+
+            // Need to alias bundle and host capabilities.
+            if (cap.getNamespace().equals(BundleRevision.BUNDLE_NAMESPACE)
+                || cap.getNamespace().equals(BundleRevision.HOST_NAMESPACE))
+            {
+                // Make a copy of the attribute array.
+                Map<String, Object> aliasAttrs =
+                    new HashMap<String, Object>(cap.getAttributes());
+                // Add the aliased value.
+                aliasAttrs.put(cap.getNamespace(), aliases);
+                // Create the aliased capability to replace the old capability.
+                cap = new BundleCapabilityImpl(
+                    cap.getRevision(),
+                    cap.getNamespace(),
+                    cap.getDirectives(),
+                    aliasAttrs);
+                aliasCaps.set(capIdx, cap);
+            }
+
+            // Further, search attributes for bundle symbolic name and alias it too.
+            for (Entry<String, Object> entry : cap.getAttributes().entrySet())
+            {
+                // If there is a bundle symbolic name attribute, add the
+                // standard alias as a value.
+                if (entry.getKey().equalsIgnoreCase(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE))
+                {
+                    // Make a copy of the attribute array.
+                    Map<String, Object> aliasAttrs =
+                        new HashMap<String, Object>(cap.getAttributes());
+                    // Add the aliased value.
+                    aliasAttrs.put(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE, aliases);
+                    // Create the aliased capability to replace the old capability.
+                    aliasCaps.set(capIdx, new BundleCapabilityImpl(
+                        cap.getRevision(),
+                        cap.getNamespace(),
+                        cap.getDirectives(),
+                        aliasAttrs));
+                    // Continue with the next capability.
+                    break;
+                }
+            }
+        }
+
+        return aliasCaps;
+    }
+
+    public BundleRevision getRevision()
+    {
+        return m_systemBundleRevision;
+    }
+
+    public Object getSecurityContext()
+    {
+        return m_securityContext;
+    }
+
+    public synchronized void setSecurityContext(Object securityContext)
+    {
+        m_securityContext = securityContext;
+    }
+
+    /**
+     * Add an extension bundle. The bundle will be added to the parent classloader
+     * and it's exported packages will be added to the module definition
+     * exports of this instance. Subsequently, they are available form the
+     * instance in it's role as content loader.
+     *
+     * @param felix the framework instance the given extension bundle comes from.
+     * @param bundle the extension bundle to add.
+     * @throws BundleException if extension bundles are not supported or this is
+     *          not a framework extension.
+     * @throws SecurityException if the caller does not have the needed
+     *          AdminPermission.EXTENSIONLIFECYCLE and security is enabled.
+     * @throws Exception in case something goes wrong.
+     */
+    synchronized void addExtensionBundle(Felix felix, BundleImpl bundle)
+        throws SecurityException, BundleException, Exception
+    {
+        Object sm = System.getSecurityManager();
+        if (sm != null)
+        {
+            ((SecurityManager) sm).checkPermission(
+                new AdminPermission(bundle, AdminPermission.EXTENSIONLIFECYCLE));
+
+            if (!((BundleProtectionDomain) bundle.getProtectionDomain()).impliesDirect(new AllPermission()))
+            {
+                throw new SecurityException("Extension Bundles must have AllPermission");
+            }
+        }
+
+        String directive = ManifestParser.parseExtensionBundleHeader((String)
+            ((BundleRevisionImpl) bundle.adapt(BundleRevision.class))
+                .getHeaders().get(Constants.FRAGMENT_HOST));
+
+        // We only support classpath extensions (not bootclasspath).
+        if (!Constants.EXTENSION_FRAMEWORK.equals(directive))
+        {
+            throw new BundleException("Unsupported Extension Bundle type: " +
+                directive, new UnsupportedOperationException(
+                "Unsupported Extension Bundle type!"));
+        }
+
+        try
+        {
+            // Merge the exported packages with the exported packages of the systembundle.
+            List<BundleCapability> exports = null;
+            try
+            {
+                exports = ManifestParser.parseExportHeader(
+                    m_logger, m_systemBundleRevision,
+                    (String) ((BundleRevisionImpl) bundle.adapt(BundleRevision.class))
+                        .getHeaders().get(Constants.EXPORT_PACKAGE),
+                    m_systemBundleRevision.getSymbolicName(), m_systemBundleRevision.getVersion());
+                exports = aliasSymbolicName(exports);
+            }
+            catch (Exception ex)
+            {
+                m_logger.log(
+                    bundle,
+                    Logger.LOG_ERROR,
+                    "Error parsing extension bundle export statement: "
+                    + ((BundleRevisionImpl) bundle.adapt(BundleRevision.class))
+                        .getHeaders().get(Constants.EXPORT_PACKAGE), ex);
+                return;
+            }
+
+            // Add the bundle as extension if we support extensions
+            if (m_extensionManager != null)
+            {
+                // This needs to be the private instance.
+                m_extensionManager.addExtension(felix, bundle);
+            }
+            else
+            {
+                // We don't support extensions (i.e., the parent is not an URLClassLoader).
+                m_logger.log(bundle, Logger.LOG_WARNING,
+                    "Unable to add extension bundle to FrameworkClassLoader - Maybe not an URLClassLoader?");
+                throw new UnsupportedOperationException(
+                    "Unable to add extension bundle to FrameworkClassLoader - Maybe not an URLClassLoader?");
+            }
+            appendCapabilities(exports);
+        }
+        catch (Exception ex)
+        {
+            throw ex;
+        }
+
+        BundleRevisionImpl bri = (BundleRevisionImpl) bundle.adapt(BundleRevision.class);
+        List<BundleRequirement> reqs = bri.getDeclaredRequirements(BundleRevision.HOST_NAMESPACE);
+        List<BundleCapability> caps = getCapabilities(BundleRevision.HOST_NAMESPACE);
+        BundleWire bw = new BundleWireImpl(bri, reqs.get(0), m_systemBundleRevision, caps.get(0));
+        bri.resolve(
+            new BundleWiringImpl(
+                m_logger,
+                m_configMap,
+                null,
+                bri,
+                null,
+                Collections.singletonList(bw),
+                Collections.EMPTY_MAP,
+                Collections.EMPTY_MAP));
+        felix.getDependencies().addDependent(bw);
+        felix.setBundleStateAndNotify(bundle, Bundle.RESOLVED);
+    }
+
+    /**
+     * This is a Felix specific extension mechanism that allows extension bundles
+     * to have activators and be started via this method.
+     *
+     * @param felix the framework instance the extension bundle is installed in.
+     * @param bundle the extension bundle to start if it has a Felix specific activator.
+     */
+    void startExtensionBundle(Felix felix, BundleImpl bundle)
+    {
+        String activatorClass = (String)
+            ((BundleRevisionImpl) bundle.adapt(BundleRevision.class))
+                .getHeaders().get(FelixConstants.FELIX_EXTENSION_ACTIVATOR);
+
+        if (activatorClass != null)
+        {
+            try
+            {
+// TODO: SECURITY - Should this consider security?
+                BundleActivator activator = (BundleActivator)
+                    felix.getClass().getClassLoader().loadClass(
+                        activatorClass.trim()).newInstance();
+
+// TODO: EXTENSIONMANAGER - This is kind of hacky, can we improve it?
+                felix.m_activatorList.add(activator);
+
+                BundleContext context = felix._getBundleContext();
+
+                bundle.setBundleContext(context);
+
+                if ((felix.getState() == Bundle.ACTIVE) || (felix.getState() == Bundle.STARTING))
+                {
+                    Felix.m_secureAction.startActivator(activator, context);
+                }
+            }
+            catch (Throwable ex)
+            {
+                m_logger.log(bundle, Logger.LOG_WARNING,
+                    "Unable to start Felix Extension Activator", ex);
+            }
+        }
+    }
+
+    /**
+     * Remove all extension registered by the given framework instance. Note, it
+     * is not possible to unregister allready loaded classes form those extensions.
+     * That is why the spec requires a JVM restart.
+     *
+     * @param felix the framework instance whose extensions need to be unregistered.
+     */
+    void removeExtensions(Felix felix)
+    {
+        if (m_extensionManager != null)
+        {
+            m_extensionManager._removeExtensions(felix);
+        }
+    }
+
+    private List<BundleCapability> getCapabilities(String namespace)
+    {
+        List<BundleCapability> caps = m_capabilities;
+        List<BundleCapability> result = caps;
+        if (namespace != null)
+        {
+            result = new ArrayList<BundleCapability>();
+            for (BundleCapability cap : caps)
+            {
+                if (cap.getNamespace().equals(namespace))
+                {
+                    result.add(cap);
+                }
+            }
+        }
+        return result;
+    }
+
+    private synchronized void appendCapabilities(List<BundleCapability> caps)
+    {
+        List<BundleCapability> newCaps =
+            new ArrayList<BundleCapability>(m_capabilities.size() + caps.size());
+        newCaps.addAll(m_capabilities);
+        newCaps.addAll(caps);
+        m_capabilities = ImmutableList.newInstance(newCaps);
+        m_headerMap.put(Constants.EXPORT_PACKAGE, convertCapabilitiesToHeaders(m_headerMap));
+    }
+
+    private String convertCapabilitiesToHeaders(Map headers)
+    {
+        StringBuffer exportSB = new StringBuffer("");
+        Set<String> exportNames = new HashSet<String>();
+
+        List<BundleCapability> caps = m_capabilities;
+        for (BundleCapability cap : caps)
+        {
+            if (cap.getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE))
+            {
+                // Add a comma separate if there is an existing package.
+                if (exportSB.length() > 0)
+                {
+                    exportSB.append(", ");
+                }
+
+                // Append exported package information.
+                exportSB.append(cap.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE));
+                for (Entry<String, String> entry : cap.getDirectives().entrySet())
+                {
+                    exportSB.append("; ");
+                    exportSB.append(entry.getKey());
+                    exportSB.append(":=\"");
+                    exportSB.append(entry.getValue());
+                    exportSB.append("\"");
+                }
+                for (Entry<String, Object> entry : cap.getAttributes().entrySet())
+                {
+                    if (!entry.getKey().equals(BundleRevision.PACKAGE_NAMESPACE)
+                        && !entry.getKey().equals(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE)
+                        && !entry.getKey().equals(Constants.BUNDLE_VERSION_ATTRIBUTE))
+                    {
+                        exportSB.append("; ");
+                        exportSB.append(entry.getKey());
+                        exportSB.append("=\"");
+                        exportSB.append(entry.getValue());
+                        exportSB.append("\"");
+                    }
+                }
+
+                // Remember exported packages.
+                exportNames.add(
+                    (String) cap.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE));
+            }
+        }
+
+        m_exportNames = exportNames;
+
+        return exportSB.toString();
+    }
+
+    //
+    // Classpath Extension
+    //
+
+    /*
+     * See whether any registered extension provides the class requested. If not
+     * throw an IOException.
+     */
+    public URLConnection openConnection(URL url) throws IOException
+    {
+        String path = url.getPath();
+
+        if (path.trim().equals("/"))
+        {
+            return new URLHandlersBundleURLConnection(url);
+        }
+
+        Bundle[] extensions = m_extensionsCache;
+        URL result = null;
+        for (Bundle extBundle : extensions)
+        {
+            try
+            {
+                BundleRevisionImpl bri =
+                    (BundleRevisionImpl) extBundle.adapt(BundleRevision.class);
+                if (bri != null)
+                {
+                    result = bri.getResourceLocal(path);
+                }
+            }
+            catch (Exception ex)
+            {
+                // Maybe the bundle went away, so ignore this exception.
+            }
+            if (result != null)
+            {
+                return result.openConnection();
+            }
+        }
+
+        return new URLConnection(url)
+            {
+                public void connect() throws IOException
+                {
+                    throw new IOException("Resource not provided by any extension!");
+                }
+            };
+    }
+
+    @Override
+    protected InetAddress getHostAddress(URL u)
+    {
+        // the extension URLs do not address real hosts
+        return null;
+    }
+
+    private synchronized void addExtension(Object source, Bundle extension)
+    {
+        List sourceExtensions = (List) m_sourceToExtensions.get(source);
+
+        if (sourceExtensions == null)
+        {
+            sourceExtensions = new ArrayList();
+            m_sourceToExtensions.put(source, sourceExtensions);
+        }
+
+        sourceExtensions.add(extension);
+
+        _add(extension.getSymbolicName(), extension);
+        m_extensionsCache = (Bundle[])
+                m_extensions.toArray(new Bundle[m_extensions.size()]);
+    }
+
+    private synchronized void _removeExtensions(Object source)
+    {
+        if (m_sourceToExtensions.remove(source) == null)
+        {
+            return;
+        }
+
+        m_extensions.clear();
+        m_names.clear();
+
+        for (Iterator iter = m_sourceToExtensions.values().iterator(); iter.hasNext();)
+        {
+            List extensions = (List) iter.next();
+            for (Iterator extIter = extensions.iterator(); extIter.hasNext();)
+            {
+                Bundle bundle = (Bundle) extIter.next();
+                _add(bundle.getSymbolicName(), bundle);
+            }
+            m_extensionsCache = (Bundle[])
+                m_extensions.toArray(new Bundle[m_extensions.size()]);
+        }
+    }
+
+    private void _add(String name, Bundle extension)
+    {
+        if (!m_names.contains(name))
+        {
+            m_names.add(name);
+            m_extensions.add(extension);
+        }
+    }
+
+    public void close()
+    {
+        // Do nothing on close, since we have nothing open.
+    }
+
+    public Enumeration getEntries()
+    {
+        return new Enumeration()
+        {
+            public boolean hasMoreElements()
+            {
+                return false;
+            }
+
+            public Object nextElement() throws NoSuchElementException
+            {
+                throw new NoSuchElementException();
+            }
+        };
+    }
+
+    public boolean hasEntry(String name) {
+        return false;
+    }
+
+    public byte[] getEntryAsBytes(String name)
+    {
+        return null;
+    }
+
+    public InputStream getEntryAsStream(String name) throws IOException
+    {
+        return null;
+    }
+
+    public Content getEntryAsContent(String name)
+    {
+        return null;
+    }
+
+    public String getEntryAsNativeLibrary(String name)
+    {
+        return null;
+    }
+
+    public URL getEntryAsURL(String name)
+    {
+        return null;
+    }
+
+    //
+    // Utility methods.
+    //
+
+    class ExtensionManagerRevision extends BundleRevisionImpl
+    {
+        private final Version m_version;
+        private volatile BundleWiring m_wiring;
+
+        ExtensionManagerRevision(Felix felix)
+        {
+            super(felix, "0");
+            m_version = new Version((String)
+                m_configMap.get(FelixConstants.FELIX_VERSION_PROPERTY));
+        }
+
+        @Override
+        public Map getHeaders()
+        {
+            synchronized (ExtensionManager.this)
+            {
+                return m_headerMap;
+            }
+        }
+
+        @Override
+        public List<BundleCapability> getDeclaredCapabilities(String namespace)
+        {
+            return ExtensionManager.this.getCapabilities(namespace);
+        }
+
+        @Override
+        public String getSymbolicName()
+        {
+            return FelixConstants.SYSTEM_BUNDLE_SYMBOLICNAME;
+        }
+
+        @Override
+        public Version getVersion()
+        {
+            return m_version;
+        }
+
+        @Override
+        public void close()
+        {
+            // Nothing needed here.
+        }
+
+        @Override
+        public Content getContent()
+        {
+            return ExtensionManager.this;
+        }
+
+        @Override
+        public URL getEntry(String name)
+        {
+            // There is no content for the system bundle, so return null.
+            return null;
+        }
+
+        @Override
+        public boolean hasInputStream(int index, String urlPath)
+        {
+            return (getClass().getClassLoader().getResource(urlPath) != null);
+        }
+
+        @Override
+        public InputStream getInputStream(int index, String urlPath)
+        {
+            return getClass().getClassLoader().getResourceAsStream(urlPath);
+        }
+
+        @Override
+        public URL getLocalURL(int index, String urlPath)
+        {
+            return getClass().getClassLoader().getResource(urlPath);
+        }
+
+        @Override
+        public void resolve(BundleWiringImpl wire)
+        {
+            try
+            {
+                m_wiring = new ExtensionManagerWiring(
+                    m_logger, m_configMap, this);
+            }
+            catch (Exception ex)
+            {
+                // This should never happen.
+            }
+        }
+
+        @Override
+        public BundleWiring getWiring()
+        {
+            return m_wiring;
+        }
+    }
+
+    class ExtensionManagerWiring extends BundleWiringImpl
+    {
+        ExtensionManagerWiring(
+            Logger logger, Map configMap, BundleRevisionImpl revision)
+            throws Exception
+        {
+            super(logger, configMap, null, revision,
+                null, Collections.EMPTY_LIST, null, null);
+        }
+
+        @Override
+        public ClassLoader getClassLoader()
+        {
+            return getClass().getClassLoader();
+        }
+
+        @Override
+        public List<BundleCapability> getCapabilities(String namespace)
+        {
+            return ExtensionManager.this.getCapabilities(namespace);
+        }
+
+        @Override
+        public List<R4Library> getNativeLibraries()
+        {
+            return Collections.EMPTY_LIST;
+        }
+
+        @Override
+        public Class getClassByDelegation(String name) throws ClassNotFoundException
+        {
+            Class clazz = null;
+            String pkgName = Util.getClassPackage(name);
+            if (shouldBootDelegate(pkgName))
+            {
+                try
+                {
+                    // Get the appropriate class loader for delegation.
+                    ClassLoader bdcl = getBootDelegationClassLoader();
+                    clazz = bdcl.loadClass(name);
+                    // If this is a java.* package, then always terminate the
+                    // search; otherwise, continue to look locally if not found.
+                    if (pkgName.startsWith("java.") || (clazz != null))
+                    {
+                        return clazz;
+                    }
+                }
+                catch (ClassNotFoundException ex)
+                {
+                    // If this is a java.* package, then always terminate the
+                    // search; otherwise, continue to look locally if not found.
+                    if (pkgName.startsWith("java."))
+                    {
+                        throw ex;
+                    }
+                }
+            }
+            if (clazz == null)
+            {
+                if (!m_exportNames.contains(Util.getClassPackage(name)))
+                {
+                    throw new ClassNotFoundException(name);
+                }
+
+                clazz = getClass().getClassLoader().loadClass(name);
+            }
+            return clazz;
+        }
+
+        @Override
+        public URL getResourceByDelegation(String name)
+        {
+            return getClass().getClassLoader().getResource(name);
+        }
+
+        @Override
+        public Enumeration getResourcesByDelegation(String name)
+        {
+           try
+           {
+               return getClass().getClassLoader().getResources(name);
+           }
+           catch (IOException ex)
+           {
+               return null;
+           }
+        }
+
+        @Override
+        public void dispose()
+        {
+            // Nothing needed here.
+        }
+    }
+}
diff --git a/src/main/java/org/apache/felix/framework/FakeURLStreamHandler.java b/src/main/java/org/apache/felix/framework/FakeURLStreamHandler.java
new file mode 100644
index 0000000..4f368af
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/FakeURLStreamHandler.java
@@ -0,0 +1,41 @@
+/* 
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework;
+
+import java.io.IOException;
+import java.net.*;
+
+/**
+ * This class implements a fake stream handler. This class is necessary in
+ * some cases when assigning <tt>CodeSource</tt>s to classes in
+ * <tt>BundleClassLoader</tt>. In general, the bundle location is an URL
+ * and this URL is used as the code source for the bundle's associated
+ * classes. The OSGi specification does not require that the bundle
+ * location be an URL, though, so in that case we try to generate a
+ * fake URL for the code source of the bundle, which is just the location
+ * string prepended with the "location:" protocol, by default. We need
+ * this fake handler to avoid an unknown protocol exception.
+**/
+class FakeURLStreamHandler extends URLStreamHandler
+{
+    protected URLConnection openConnection(URL url) throws IOException
+    {
+        throw new IOException("FakeURLStreamHandler can not be used!");
+    }
+}
diff --git a/src/main/java/org/apache/felix/framework/Felix.java b/src/main/java/org/apache/felix/framework/Felix.java
new file mode 100644
index 0000000..d670d78
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/Felix.java
@@ -0,0 +1,5202 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework;
+
+import java.io.*;
+import java.net.*;
+import java.security.*;
+import java.util.*;
+
+import org.apache.felix.framework.BundleWiringImpl.BundleClassLoader;
+import org.apache.felix.framework.ServiceRegistry.ServiceRegistryCallbacks;
+import org.apache.felix.framework.cache.BundleArchive;
+import org.apache.felix.framework.cache.BundleCache;
+import org.apache.felix.framework.capabilityset.CapabilitySet;
+import org.apache.felix.framework.capabilityset.SimpleFilter;
+import org.apache.felix.framework.ext.SecurityProvider;
+import org.apache.felix.framework.resolver.ResolveException;
+import org.apache.felix.framework.util.EventDispatcher;
+import org.apache.felix.framework.util.FelixConstants;
+import org.apache.felix.framework.util.ListenerInfo;
+import org.apache.felix.framework.util.MapToDictionary;
+import org.apache.felix.framework.util.SecureAction;
+import org.apache.felix.framework.util.ShrinkableCollection;
+import org.apache.felix.framework.util.StringMap;
+import org.apache.felix.framework.util.ThreadGate;
+import org.apache.felix.framework.util.Util;
+import org.apache.felix.framework.util.manifestparser.R4LibraryClause;
+import org.apache.felix.framework.wiring.BundleRequirementImpl;
+import org.osgi.framework.AdminPermission;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.BundleListener;
+import org.osgi.framework.BundleReference;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Filter;
+import org.osgi.framework.FrameworkEvent;
+import org.osgi.framework.FrameworkListener;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceException;
+import org.osgi.framework.ServiceFactory;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServicePermission;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.framework.launch.Framework;
+import org.osgi.framework.startlevel.FrameworkStartLevel;
+import org.osgi.framework.wiring.BundleCapability;
+import org.osgi.framework.wiring.BundleRevision;
+import org.osgi.framework.wiring.BundleRevisions;
+import org.osgi.framework.wiring.BundleWiring;
+import org.osgi.framework.wiring.FrameworkWiring;
+import org.osgi.service.packageadmin.ExportedPackage;
+
+public class Felix extends BundleImpl implements Framework
+{
+    // The secure action used to do privileged calls
+    static final SecureAction m_secureAction = new SecureAction();
+
+    // The extension manager to handle extension bundles
+    private final ExtensionManager m_extensionManager;
+
+    // Framework wiring object.
+    private final FrameworkWiringImpl m_fwkWiring;
+    private final FrameworkStartLevelImpl m_fwkStartLevel;
+
+    // Logging related member variables.
+    private final Logger m_logger;
+    // Immutable config properties.
+    private final Map m_configMap;
+    // Mutable configuration properties passed into constructor.
+    private final Map m_configMutableMap;
+
+    // Resolver and resolver state.
+    private final StatefulResolver m_resolver;
+
+    // Lock object used to determine if an individual bundle
+    // lock or the global lock can be acquired.
+    private final Object[] m_bundleLock = new Object[0];
+    // Keeps track of threads wanting to acquire the global lock.
+    private final List m_globalLockWaitersList = new ArrayList();
+    // The thread currently holding the global lock.
+    private Thread m_globalLockThread = null;
+    // How many times the global lock was acquired by the thread holding
+    // the global lock; if this value is zero, then it means the global
+    // lock is free.
+    private int m_globalLockCount = 0;
+
+    // Maps a bundle location to a bundle location;
+    // used to reserve a location when installing a bundle.
+    private final Map m_installRequestMap = new HashMap();
+    // This lock must be acquired to modify m_installRequestMap;
+    // to help avoid deadlock this lock as priority 1 and should
+    // be acquired before locks with lower priority.
+    private final Object[] m_installRequestLock_Priority1 = new Object[0];
+
+    // Contains two maps, one mapping a String bundle location to a bundle
+    // and the other mapping a Long bundle identifier to a bundle.
+    // CONCURRENCY: Access guarded by the global lock for writes,
+    // but no lock for reads since it is copy on write.
+    private volatile Map[] m_installedBundles;
+    private static final int LOCATION_MAP_IDX = 0;
+    private static final int IDENTIFIER_MAP_IDX = 1;
+    // An array of uninstalled bundles before a refresh occurs.
+    // CONCURRENCY: Access guarded by the global lock for writes,
+    // but no lock for reads since it is copy on write.
+    private volatile List<BundleImpl> m_uninstalledBundles;
+    // Object to keep track of dependencies among bundle revisions.
+    private final BundleRevisionDependencies m_dependencies =
+        new BundleRevisionDependencies();
+
+    // Framework's active start level.
+    private volatile int m_activeStartLevel = FelixConstants.FRAMEWORK_INACTIVE_STARTLEVEL;
+    // Framework's target start level.
+    // Normally the target start will equal the active start level, except
+    // whem the start level is changing, in which case the target start level
+    // will report the new start level while the active start level will report
+    // the old start level. Once the start level change is complete, the two
+    // will become equal again.
+    private volatile int m_targetStartLevel = FelixConstants.FRAMEWORK_INACTIVE_STARTLEVEL;
+    // Keep track of bundles currently being processed by start level thread.
+    private final SortedSet<StartLevelTuple> m_startLevelBundles =
+        new TreeSet<StartLevelTuple>();
+
+    // Local bundle cache.
+    private BundleCache m_cache = null;
+
+    // System bundle activator list.
+    List m_activatorList = null;
+
+    // Next available bundle identifier.
+    private long m_nextId = 1L;
+    private final Object m_nextIdLock = new Object[0];
+
+    // Service registry.
+    private final ServiceRegistry m_registry;
+
+    // List of event listeners.
+    private final EventDispatcher m_dispatcher;
+
+    // Reusable bundle URL stream handler.
+    private final URLStreamHandler m_bundleStreamHandler;
+
+    // Boot package delegation.
+    private final String[] m_bootPkgs;
+    private final boolean[] m_bootPkgWildcards;
+
+    // Shutdown gate.
+    private volatile ThreadGate m_shutdownGate = null;
+
+    // Security Manager created by the framework
+    private SecurityManager m_securityManager = null;
+
+    // Do we need to consult the default java security policy if no security provider is present?
+    private volatile boolean m_securityDefaultPolicy;
+
+    /**
+     * <p>
+     * This constructor creates a framework instance with a specified <tt>Map</tt>
+     * of configuration properties. Configuration properties are used internally
+     * by the framework to alter its default behavior. The configuration properties
+     * should have a <tt>String</tt> key and an <tt>Object</tt> value. The passed
+     * in <tt>Map</tt> is copied by the framework and all keys are converted to
+     * <tt>String</tt>s.
+     * </p>
+     * <p>
+     * Configuration properties are generally the sole means to configure the
+     * framework's default behavior; the framework does not typically refer to
+     * any system properties for configuration information. If a <tt>Map</tt> is
+     * supplied to this method for configuration properties, then the framework
+     * will consult the <tt>Map</tt> instance for any and all configuration
+     * properties. It is possible to specify a <tt>null</tt> for the configuration
+     * property map, in which case the framework will use its default behavior
+     * in all cases.
+     * </p>
+     * <p>
+     * The following configuration properties can be specified (properties starting
+     * with "<tt>felix</tt>" are specific to Felix, while those starting with
+     * "<tt>org.osgi</tt>" are standard OSGi properties):
+     * </p>
+     * <ul>
+     *   <li><tt>org.osgi.framework.storage</tt> - Sets the directory to use as
+     *       the bundle cache; by default bundle cache directory is
+     *       <tt>felix-cache</tt> in the current working directory. The value
+     *       should be a valid directory name. The directory name can be either absolute
+     *       or relative. Relative directory names are relative to the current working
+     *       directory. The specified directory will be created if it does
+     *       not exist.
+     *   </li>
+     *   <li><tt>org.osgi.framework.storage.clean</tt> - Determines whether the
+     *       bundle cache is flushed. The value can either be "<tt>none</tt>"
+     *       or "<tt>onFirstInit</tt>", where "<tt>none</tt>" does not flush
+     *       the bundle cache and "<tt>onFirstInit</tt>" flushes the bundle
+     *       cache when the framework instance is first initialized. The default
+     *       value is "<tt>none</tt>".
+     *   </li>
+     *   <li><tt>felix.cache.rootdir</tt> - Sets the root directory to use to
+     *       calculate the bundle cache directory for relative directory names. If
+     *       <tt>org.osgi.framework.storage</tt> is set to a relative name, by
+     *       default it is relative to the current working directory. If this
+     *       property is set, then it will be calculated as being relative to
+     *       the specified root directory.
+     *   </li>
+     *   <li><tt>felix.cache.filelimit</tt> - The integer value of this string
+     *       sets an upper limit on how many files the cache will open. The default
+     *       value is zero, which means there is no limit.
+     *   </li>
+     *   <li><tt>felix.cache.locking</tt> - Enables or disables bundle cache locking,
+     *       which is used to prevent concurrent access to the bundle cache. This is
+     *       enabled by default, but on older/smaller JVMs file channel locking is
+     *       not available; set this property to <tt>false</tt> to disable it.
+     *   </li>
+     *   <li><tt>felix.cache.bufsize</tt> - Sets the buffer size to be used by
+     *       the cache; the default value is 4096. The integer value of this
+     *       string provides control over the size of the internal buffer of the
+     *       disk cache for performance reasons.
+     *   </li>
+     *   <li><tt>org.osgi.framework.system.packages</tt> - Specifies a
+     *       comma-delimited list of packages that should be exported via the
+     *       System Bundle from the parent class loader. The framework will set
+     *       this to a reasonable default. If the value is specified, it
+     *       replaces any default value.
+     *   </li>
+     *   <li><tt>org.osgi.framework.system.packages.extra</tt> - Specifies a
+     *       comma-delimited list of packages that should be exported via the
+     *       System Bundle from the parent class loader in addition to the
+     *       packages in <tt>org.osgi.framework.system.packages</tt>. The default
+     *       value is empty. If a value is specified, it is appended to the list
+     *       of default or specified packages in
+     *       <tt>org.osgi.framework.system.packages</tt>.
+     *   </li>
+     *   <li><tt>org.osgi.framework.bootdelegation</tt> - Specifies a
+     *       comma-delimited list of packages that should be made implicitly
+     *       available to all bundles from the parent class loader. It is
+     *       recommended not to use this property since it breaks modularity.
+     *       The default value is empty.
+     *   </li>
+     *   <li><tt>felix.systembundle.activators</tt> - A <tt>List</tt> of
+     *       <tt>BundleActivator</tt> instances that are started/stopped when
+     *       the System Bundle is started/stopped. The specified instances will
+     *       receive the System Bundle's <tt>BundleContext</tt> when invoked.
+     *   </li>
+     *   <li><tt>felix.log.logger</tt> - An instance of <tt>Logger</tt> that the
+     *       framework uses as its default logger.
+     *   </li>
+     *   <li><tt>felix.log.level</tt> - An integer value indicating the degree
+     *       of logging reported by the framework; the higher the value the more
+     *       logging is reported. If zero ('0') is specified, then logging is
+     *       turned off completely. The log levels match those specified in the
+     *       OSGi Log Service (i.e., 1 = error, 2 = warning, 3 = information,
+     *       and 4 = debug). The default value is 1.
+     *   </li>
+     *   <li><tt>org.osgi.framework.startlevel.beginning</tt> - The initial
+     *       start level of the framework once it starts execution; the default
+     *       value is 1.
+     *   </li>
+     *   <li><tt>felix.startlevel.bundle</tt> - The default start level for
+     *       newly installed bundles; the default value is 1.
+     *   </li>
+     *   <li><tt>felix.service.urlhandlers</tt> - Flag to indicate whether
+     *       to activate the URL Handlers service for the framework instance;
+     *       the default value is "<tt>true</tt>". Activating the URL Handlers
+     *       service will result in the <tt>URL.setURLStreamHandlerFactory()</tt>
+     *       and <tt>URLConnection.setContentHandlerFactory()</tt> being called.
+     *   </li>
+     *   <li><tt>felix.fragment.validation</tt> - Determines if installing
+     *       unsupported fragment bundles throws an exception or logs a warning.
+     *       Possible values are "<tt>exception</tt>" or "<tt>warning</tt>". The
+     *       default value is "<tt>exception</tt>".
+     *   </li>
+     *   <li><tt>felix.security.defaultpolicy</tt> - Flag to indicate whether
+     *       to consult the default java securtiy policy if no security extension
+     *       is present. The default value is "<tt>false</tt>".
+     *   </li>
+     * </ul>
+     * <p>
+     * The <a href="Main.html"><tt>Main</tt></a> class implements some
+     * functionality for default property file handling, which makes it
+     * possible to specify configuration properties and framework properties
+     * in files that are automatically loaded when starting the framework. If you
+     * plan to create your own framework instance, you may be
+     * able to take advantage of the features it provides; refer to its
+     * class documentation for more information.
+     * </p>
+     * <p>
+     * The framework is not actually started until the <tt>start()</tt> method
+     * is called.
+     * </p>
+     *
+     * @param configMap A map for obtaining configuration properties,
+     *        may be <tt>null</tt>.
+    **/
+    public Felix(Map configMap)
+    {
+        super();
+        // Copy the configuration properties; convert keys to strings.
+        m_configMutableMap = new StringMap();
+        if (configMap != null)
+        {
+            for (Iterator i = configMap.entrySet().iterator(); i.hasNext(); )
+            {
+                Map.Entry entry = (Map.Entry) i.next();
+                m_configMutableMap.put(entry.getKey().toString(), entry.getValue());
+            }
+        }
+        m_configMap = createUnmodifiableMap(m_configMutableMap);
+
+        // Create logger with appropriate log level. Even though the
+        // logger needs the system bundle's context for tracking log
+        // services, it is created now because it is needed before
+        // the system bundle is activated. The system bundle's context
+        // will be set in the init() method after the system bundle
+        // is activated.
+        if (m_configMutableMap.get(FelixConstants.LOG_LOGGER_PROP) != null)
+        {
+            m_logger = (Logger) m_configMutableMap.get(FelixConstants.LOG_LOGGER_PROP);
+        }
+        else
+        {
+            m_logger = new Logger();
+        }
+        try
+        {
+            m_logger.setLogLevel(
+                Integer.parseInt(
+                    (String) m_configMutableMap.get(FelixConstants.LOG_LEVEL_PROP)));
+        }
+        catch (NumberFormatException ex)
+        {
+            // Ignore and just use the default logging level.
+        }
+
+        // Initialize framework properties.
+        initializeFrameworkProperties();
+
+        // Read the boot delegation property and parse it.
+        String s = (m_configMap == null)
+            ? null
+            : (String) m_configMap.get(Constants.FRAMEWORK_BOOTDELEGATION);
+        s = (s == null) ? "java.*" : s + ",java.*";
+        StringTokenizer st = new StringTokenizer(s, " ,");
+        m_bootPkgs = new String[st.countTokens()];
+        m_bootPkgWildcards = new boolean[m_bootPkgs.length];
+        for (int i = 0; i < m_bootPkgs.length; i++)
+        {
+            s = st.nextToken();
+            if (s.equals("*") || s.endsWith(".*"))
+            {
+                m_bootPkgWildcards[i] = true;
+                s = s.substring(0, s.length() - 1);
+            }
+            m_bootPkgs[i] = s;
+        }
+
+        // Read the security default policy property
+        m_securityDefaultPolicy = "true".equals(getProperty(FelixConstants.SECURITY_DEFAULT_POLICY));
+
+        // Create default bundle stream handler.
+        m_bundleStreamHandler = new URLHandlersBundleStreamHandler(this);
+
+        // Create a resolver and its state.
+        m_resolver = new StatefulResolver(this);
+
+        // Create the extension manager, which we will use as the
+        // revision for the system bundle.
+        m_extensionManager = new ExtensionManager(m_logger, m_configMap, this);
+        try
+        {
+            addRevision(m_extensionManager.getRevision());
+        }
+        catch (Exception ex)
+        {
+            // This should not throw an exception, but if so, lets convert it to
+            // a runtime exception.
+            throw new RuntimeException(ex.getMessage());
+        }
+
+        // Create service registry.
+        m_registry = new ServiceRegistry(m_logger, new ServiceRegistryCallbacks() {
+            public void serviceChanged(ServiceEvent event, Dictionary oldProps)
+            {
+                fireServiceEvent(event, oldProps);
+            }
+        });
+
+        // Create event dispatcher.
+        m_dispatcher = new EventDispatcher(m_logger, m_registry);
+
+        // Create framework wiring object.
+        m_fwkWiring = new FrameworkWiringImpl(this, m_registry);
+        // Create framework start level object.
+        m_fwkStartLevel = new FrameworkStartLevelImpl(this, m_registry);
+    }
+
+    Logger getLogger()
+    {
+        return m_logger;
+    }
+
+    Map getConfig()
+    {
+        return m_configMap;
+    }
+
+    StatefulResolver getResolver()
+    {
+        return m_resolver;
+    }
+
+    BundleRevisionDependencies getDependencies()
+    {
+        return m_dependencies;
+    }
+
+    URLStreamHandler getBundleStreamHandler()
+    {
+        return m_bundleStreamHandler;
+    }
+
+    String[] getBootPackages()
+    {
+        return m_bootPkgs;
+    }
+
+    boolean[] getBootPackageWildcards()
+    {
+        return m_bootPkgWildcards;
+    }
+
+    private Map createUnmodifiableMap(Map mutableMap)
+    {
+        Map result = Collections.unmodifiableMap(mutableMap);
+
+        // Work around a bug in certain version of J9 where a call to
+        // Collections.unmodifiableMap().keySet().iterator() throws
+        // a NoClassDefFoundError. We try to detect this and return
+        // the given mutableMap instead.
+        try
+        {
+            result.keySet().iterator();
+        }
+        catch (NoClassDefFoundError ex)
+        {
+            return mutableMap;
+        }
+
+        return result;
+    }
+
+    // This overrides BundleImpl.close() which avoids removing the
+    // system bundle module from the resolver state.
+    @Override
+    void close()
+    {
+    }
+
+    // This overrides the default behavior of BundleImpl.getFramework()
+    // to return "this", since the system bundle is the framework.
+    Felix getFramework()
+    {
+        return this;
+    }
+
+    @Override
+    public <A> A adapt(Class<A> type)
+    {
+        checkAdapt(type);
+        if ((type == Framework.class)
+            || (type == Felix.class))
+        {
+            return (A) this;
+        }
+        else if ((type == FrameworkWiring.class)
+            || (type == FrameworkWiringImpl.class))
+        {
+            return (A) m_fwkWiring;
+        }
+        else if ((type == FrameworkStartLevel.class)
+            || (type == FrameworkStartLevelImpl.class))
+        {
+            return (A) m_fwkStartLevel;
+        }
+        return super.adapt(type);
+    }
+
+    public long getBundleId()
+    {
+        return 0;
+    }
+
+    public long getLastModified()
+    {
+        return 0;
+    }
+
+    void setLastModified(long l)
+    {
+        // Ignore.
+    }
+
+    String _getLocation()
+    {
+        return Constants.SYSTEM_BUNDLE_LOCATION;
+    }
+
+    public int getPersistentState()
+    {
+        return Bundle.ACTIVE;
+    }
+
+    public void setPersistentStateInactive()
+    {
+        // Ignore.
+    }
+
+    public void setPersistentStateActive()
+    {
+        // Ignore.
+    }
+
+    public void setPersistentStateUninstalled()
+    {
+        // Ignore.
+    }
+
+    /**
+     * Overrides standard <tt>BundleImpl.getStartLevel()</tt> behavior to
+     * always return zero for the system bundle.
+     * @param defaultLevel This parameter is ignored by the system bundle.
+     * @return Always returns zero.
+    **/
+    int getStartLevel(int defaultLevel)
+    {
+        return 0;
+    }
+
+    /**
+     * Overrides standard <tt>BundleImpl.setStartLevel()</tt> behavior to
+     * always throw an exception since the system bundle's start level cannot
+     * be changed.
+     * @param level This parameter is ignored by the system bundle.
+     * @throws IllegalArgumentException Always throws exception since system
+     *         bundle's start level cannot be changed.
+    **/
+    void setStartLevel(int level)
+    {
+        throw new IllegalArgumentException("Cannot set the system bundle's start level.");
+    }
+
+    public boolean hasPermission(Object obj)
+    {
+        return true;
+    }
+
+    /**
+     * This method initializes the framework, which is comprised of resolving
+     * the system bundle, reloading any cached bundles, and activating the system
+     * bundle. The framework is left in the <tt>Bundle.STARTING</tt> state and
+     * reloaded bundles are in the <tt>Bundle.INSTALLED</tt> state. After
+     * successfully invoking this method, <tt>getBundleContext()</tt> will
+     * return a valid <tt>BundleContext</tt> for the system bundle. To finish
+     * starting the framework, invoke the <tt>start()</tt> method.
+     *
+     * @throws org.osgi.framework.BundleException if any error occurs.
+    **/
+    public void init() throws BundleException
+    {
+        // The system bundle can only be initialized if it currently isn't started.
+        acquireBundleLock(this,
+            Bundle.INSTALLED | Bundle.RESOLVED | Bundle.STARTING | Bundle.ACTIVE);
+        try
+        {
+            if ((getState() == Bundle.INSTALLED) || (getState() == Bundle.RESOLVED))
+            {
+                String security = (String) m_configMap.get(Constants.FRAMEWORK_SECURITY);
+                if (security != null)
+                {
+                    if (System.getSecurityManager() != null)
+                    {
+                        throw new SecurityException("SecurityManager already installed");
+                    }
+                    security = security.trim();
+                    if (Constants.FRAMEWORK_SECURITY_OSGI.equalsIgnoreCase(security) || (security.length() == 0))
+                    {
+                        System.setSecurityManager(m_securityManager = new SecurityManager());
+                    }
+                    else
+                    {
+                        try
+                        {
+                            System.setSecurityManager(m_securityManager =
+                                (SecurityManager) Class.forName(security).newInstance());
+                        }
+                        catch (Throwable t)
+                        {
+                            SecurityException se =
+                                new SecurityException(
+                                    "Unable to install custom SecurityManager: " + security);
+                            se.initCause(t);
+                            throw se;
+                        }
+                    }
+                }
+
+                // Generate a framework UUID.
+                // Spec says we get a new UUID for each invocation of init().
+                m_configMutableMap.put(
+                    FelixConstants.FRAMEWORK_UUID,
+                    Util.randomUUID());
+
+                // Get any system bundle activators.
+                m_activatorList = (List) m_configMutableMap.get(FelixConstants.SYSTEMBUNDLE_ACTIVATORS_PROP);
+                m_activatorList = (m_activatorList == null) ? new ArrayList() : new ArrayList(m_activatorList);
+
+                // Initialize event dispatcher.
+                m_dispatcher.startDispatching();
+
+                // Create the bundle cache, if necessary, so that we can reload any
+                // installed bundles.
+                m_cache = (BundleCache)
+                    m_configMutableMap.get(FelixConstants.FRAMEWORK_BUNDLECACHE_IMPL);
+                if (m_cache == null)
+                {
+                       try
+                       {
+                           m_cache = new BundleCache(m_logger, m_configMap);
+                       }
+                       catch (Exception ex)
+                       {
+                           m_logger.log(Logger.LOG_ERROR, "Error creating bundle cache.", ex);
+                           throw new BundleException("Error creating bundle cache.", ex);
+                       }
+                }
+
+                // If this is the first time init is called, check to see if
+                // we need to flush the bundle cache.
+                if (getState() == Bundle.INSTALLED)
+                {
+                    String clean = (String) m_configMap.get(Constants.FRAMEWORK_STORAGE_CLEAN);
+                    if ((clean != null)
+                        && clean.equalsIgnoreCase(Constants.FRAMEWORK_STORAGE_CLEAN_ONFIRSTINIT))
+                    {
+                        try
+                        {
+                            m_cache.delete();
+                        }
+                        catch (Exception ex)
+                        {
+                            throw new BundleException("Unable to flush bundle cache.", ex);
+                        }
+                    }
+                }
+
+                // Initialize installed bundle data structures.
+                Map[] maps = new Map[] {
+                    new HashMap<String, BundleImpl>(1),
+                    new TreeMap<Long, BundleImpl>()
+                };
+                m_uninstalledBundles = new ArrayList<BundleImpl>(0);
+
+                // Add the system bundle to the set of installed bundles.
+                maps[LOCATION_MAP_IDX].put(_getLocation(), this);
+                maps[IDENTIFIER_MAP_IDX].put(new Long(0), this);
+                m_installedBundles = maps;
+
+                // Manually resolve the system bundle, which will cause its
+                // state to be set to RESOLVED.
+                try
+                {
+                    m_resolver.resolve(
+                        Collections.singleton(adapt(BundleRevision.class)),
+                        Collections.EMPTY_SET);
+                }
+                catch (ResolveException ex)
+                {
+                    // This should never happen.
+                    throw new BundleException(
+                        "Unresolved constraint in System Bundle:"
+                        + ex.getRequirement());
+                }
+
+                // Reload the cached bundles before creating and starting the
+                // system bundle, since we want all cached bundles to be reloaded
+                // when we activate the system bundle and any subsequent system
+                // bundle activators passed into the framework constructor.
+                BundleArchive[] archives = null;
+
+                // First get cached bundle identifiers.
+                try
+                {
+                    archives = m_cache.getArchives();
+                }
+                catch (Exception ex)
+                {
+                    m_logger.log(Logger.LOG_ERROR, "Unable to list saved bundles.", ex);
+                    archives = null;
+                }
+
+                // Create system bundle activator and bundle context so we can activate it.
+                setActivator(new SystemBundleActivator());
+                setBundleContext(new BundleContextImpl(m_logger, this, this));
+
+                // Now load all cached bundles.
+                for (int i = 0; (archives != null) && (i < archives.length); i++)
+                {
+                    try
+                    {
+                        // Keep track of the max bundle ID currently in use since we
+                        // will need to use this as our next bundle ID value if the
+                        // persisted value cannot be read.
+                        m_nextId = Math.max(m_nextId, archives[i].getId() + 1);
+
+                        // It is possible that a bundle in the cache was previously
+                        // uninstalled, but not completely deleted (perhaps because
+                        // of a crash or a locked file), so if we see an archive
+                        // with an UNINSTALLED persistent state, then try to remove
+                        // it now.
+                        if (archives[i].getPersistentState() == Bundle.UNINSTALLED)
+                        {
+                            archives[i].closeAndDelete();
+                        }
+                        // Otherwise re-install the cached bundle.
+                        else
+                        {
+                            // Install the cached bundle.
+                            reloadBundle(archives[i]);
+                        }
+                    }
+                    catch (Exception ex)
+                    {
+                        fireFrameworkEvent(FrameworkEvent.ERROR, this, ex);
+                        try
+                        {
+                            m_logger.log(
+                                Logger.LOG_ERROR,
+                                "Unable to re-install " + archives[i].getLocation(),
+                                ex);
+                        }
+                        catch (Exception ex2)
+                        {
+                            m_logger.log(
+                                Logger.LOG_ERROR,
+                                "Unable to re-install cached bundle.",
+                                ex);
+                        }
+                        // TODO: FRAMEWORK - Perhaps we should remove the cached bundle?
+                    }
+                }
+
+                // Now that we have loaded all cached bundles and have determined the
+                // max bundle ID of cached bundles, we need to try to load the next
+                // bundle ID from persistent storage. In case of failure, we should
+                // keep the max value.
+                m_nextId = Math.max(m_nextId, loadNextId());
+
+                // The framework is now in its startup sequence.
+                setBundleStateAndNotify(this, Bundle.STARTING);
+
+                // Now it is possible for threads to wait for the framework to stop,
+                // so create a gate for that purpose.
+                m_shutdownGate = new ThreadGate();
+
+                // Start services
+                m_fwkWiring.start();
+                m_fwkStartLevel.start();
+
+                try
+                {
+                    Felix.m_secureAction.startActivator(
+                        getActivator(), _getBundleContext());
+                }
+                catch (Throwable ex)
+                {
+                    m_dispatcher.stopDispatching();
+                    m_logger.log(Logger.LOG_ERROR, "Unable to start system bundle.", ex);
+                    throw new RuntimeException("Unable to start system bundle.");
+                }
+
+                // Now that the system bundle is successfully created we can give
+                // its bundle context to the logger so that it can track log services.
+                m_logger.setSystemBundleContext(_getBundleContext());
+
+                // We have to check with the security provider (if there is one).
+                // This is to avoid having bundles in the cache that have been tampered with
+                SecurityProvider sp = getFramework().getSecurityProvider();
+                if ((sp != null) && (System.getSecurityManager() != null))
+                {
+                    boolean locked = acquireGlobalLock();
+                    if (!locked)
+                    {
+                        throw new BundleException(
+                            "Unable to acquire the global lock to check the bundle.");
+                    }
+                    try
+                    {
+                        for (Object bundle : m_installedBundles[IDENTIFIER_MAP_IDX].values())
+                        {
+                            try
+                            {
+                                if (bundle != this)
+                                {
+                                    setBundleProtectionDomain((BundleImpl) bundle, (BundleRevisionImpl) ((BundleImpl) bundle).adapt(BundleRevisionImpl.class));
+                                }
+                            }
+                            catch (Exception ex)
+                            {
+                                ((BundleImpl) bundle).close();
+                                maps = new Map[] {
+                                    new HashMap<String, BundleImpl>(m_installedBundles[LOCATION_MAP_IDX]),
+                                    new TreeMap<Long, BundleImpl>(m_installedBundles[IDENTIFIER_MAP_IDX])
+                                };
+                                maps[LOCATION_MAP_IDX].remove(((BundleImpl) bundle)._getLocation());
+                                maps[IDENTIFIER_MAP_IDX].remove(new Long(((BundleImpl) bundle).getBundleId()));
+                                m_installedBundles = maps;
+
+                                m_logger.log(
+                                    Logger.LOG_ERROR,
+                                    "Bundle in cache doesn't pass security check anymore.",
+                                    ex);
+                            }
+                        }
+                    }
+                    finally
+                    {
+                        // Always release the global lock.
+                        releaseGlobalLock();
+                    }
+                }
+
+                // Clear the cache of classes coming from the system bundle.
+                // This is only used for Felix.getBundle(Class clazz) to speed
+                // up class lookup for the system bundle.
+                synchronized (m_systemBundleClassCache)
+                {
+                    m_systemBundleClassCache.clear();
+                }
+            }
+        }
+        finally
+        {
+            releaseBundleLock(this);
+        }
+    }
+
+    void setBundleProtectionDomain(BundleImpl bundleImpl, BundleRevisionImpl revisionImpl) throws Exception
+    {
+        Object certificates = null;
+        SecurityProvider sp = getFramework().getSecurityProvider();
+        if ((sp != null) && (System.getSecurityManager() != null))
+        {
+            sp.checkBundle(bundleImpl);
+            Map signers = (Map) sp.getSignerMatcher(bundleImpl, Bundle.SIGNERS_TRUSTED);
+            certificates = signers.keySet().toArray(new java.security.cert.Certificate[0]);
+        }
+        revisionImpl.setProtectionDomain(
+            new BundleProtectionDomain(this, bundleImpl, certificates));
+    }
+
+    /**
+     * This method starts the framework instance, which will transition the
+     * framework from start level 0 to its active start level as specified in
+     * its configuration properties (1 by default). If the <tt>init()</tt> was
+     * not explicitly invoked before calling this method, then it will be
+     * implicitly invoked before starting the framework.
+     *
+     * @throws org.osgi.framework.BundleException if any error occurs.
+    **/
+    public void start() throws BundleException
+    {
+        int startLevel = FelixConstants.FRAMEWORK_DEFAULT_STARTLEVEL;
+
+        acquireBundleLock(this,
+            Bundle.INSTALLED | Bundle.RESOLVED | Bundle.STARTING | Bundle.ACTIVE);
+        try
+        {
+            // Initialize if necessary.
+            if ((getState() == Bundle.INSTALLED) || (getState() == Bundle.RESOLVED))
+            {
+                init();
+            }
+
+            // If the current state is STARTING, then the system bundle can be started.
+            if (getState() == Bundle.STARTING)
+            {
+                // Get the framework's default start level.
+                String s = (String) m_configMap.get(Constants.FRAMEWORK_BEGINNING_STARTLEVEL);
+                if (s != null)
+                {
+                    try
+                    {
+                        startLevel = Integer.parseInt(s);
+                    }
+                    catch (NumberFormatException ex)
+                    {
+                        startLevel = FelixConstants.FRAMEWORK_DEFAULT_STARTLEVEL;
+                    }
+                }
+
+                m_fwkStartLevel.setStartLevelAndWait(startLevel);
+
+                // The framework is now running.
+                setBundleStateAndNotify(this, Bundle.ACTIVE);
+            }
+        }
+        finally
+        {
+            releaseBundleLock(this);
+        }
+
+        // Fire started event for system bundle.
+        fireBundleEvent(BundleEvent.STARTED, this);
+
+        // Send a framework event to indicate the framework has started.
+        fireFrameworkEvent(FrameworkEvent.STARTED, this, null);
+    }
+
+    public void start(int options) throws BundleException
+    {
+        start();
+    }
+
+    /**
+     * This method asynchronously shuts down the framework, it must be called at the
+     * end of a session in order to shutdown all active bundles.
+    **/
+    public void stop() throws BundleException
+    {
+        Object sm = System.getSecurityManager();
+
+        if (sm != null)
+        {
+            ((SecurityManager) sm).checkPermission(new AdminPermission(this,
+                AdminPermission.EXECUTE));
+        }
+
+        if ((getState() & (Bundle.INSTALLED | Bundle.RESOLVED)) == 0)
+        {
+            // Spec says stop() on SystemBundle should return immediately and
+            // shutdown framework on another thread.
+            new Thread(new Runnable() {
+                public void run()
+                {
+                    try
+                    {
+                        stopBundle(Felix.this, true);
+                    }
+                    catch (BundleException ex)
+                    {
+                        m_logger.log(
+                            Logger.LOG_ERROR,
+                            "Exception trying to stop framework.",
+                            ex);
+                    }
+                }
+            }, "FelixShutdown").start();
+        }
+    }
+
+    public void stop(int options) throws BundleException
+    {
+        stop();
+    }
+
+    /**
+     * This method will cause the calling thread to block until the framework
+     * shuts down.
+     * @param timeout A timeout value.
+     * @throws java.lang.InterruptedException If the thread was interrupted.
+    **/
+    public FrameworkEvent waitForStop(long timeout) throws InterruptedException
+    {
+        // Throw exception if timeout is negative.
+        if (timeout < 0)
+        {
+            throw new IllegalArgumentException("Timeout cannot be negative.");
+        }
+
+        // If there is a gate, wait on it; otherwise, return immediately.
+        // Grab a copy of the gate, since it is volatile.
+        ThreadGate gate = m_shutdownGate;
+        boolean open = false;
+        if (gate != null)
+        {
+            open = gate.await(timeout);
+        }
+
+        FrameworkEvent event;
+        if (open && (gate.getMessage() != null))
+        {
+            event = (FrameworkEvent) gate.getMessage();
+        }
+        else if (!open && (gate != null))
+        {
+            event = new FrameworkEvent(FrameworkEvent.WAIT_TIMEDOUT, this, null);
+        }
+        else
+        {
+            event = new FrameworkEvent(FrameworkEvent.STOPPED, this, null);
+        }
+        return event;
+    }
+
+    public void uninstall() throws BundleException
+    {
+        throw new BundleException("Cannot uninstall the system bundle.");
+    }
+
+    public void update() throws BundleException
+    {
+        update(null);
+    }
+
+    public void update(InputStream is) throws BundleException
+    {
+        Object sm = System.getSecurityManager();
+
+        if (sm != null)
+        {
+            ((SecurityManager) sm).checkPermission(new AdminPermission(this,
+                AdminPermission.EXECUTE));
+        }
+
+        // Spec says to close input stream first.
+        try
+        {
+            if (is != null) is.close();
+        }
+        catch (IOException ex)
+        {
+            m_logger.log(Logger.LOG_WARNING, "Exception closing input stream.", ex);
+        }
+
+        // Then to stop and restart the framework on a separate thread.
+        new Thread(new Runnable() {
+            public void run()
+            {
+                try
+                {
+                    // First acquire the system bundle lock to verify the state.
+                    acquireBundleLock(Felix.this, Bundle.STARTING | Bundle.ACTIVE);
+                    // Set the reason for the shutdown.
+                    m_shutdownGate.setMessage(
+                        new FrameworkEvent(FrameworkEvent.STOPPED_UPDATE, Felix.this, null));
+                    // Record the state and stop the system bundle.
+                    int oldState = Felix.this.getState();
+                    try
+                    {
+                        stop();
+                    }
+                    catch (BundleException ex)
+                    {
+                        m_logger.log(Logger.LOG_WARNING, "Exception stopping framework.", ex);
+                    }
+                    finally
+                    {
+                        releaseBundleLock(Felix.this);
+                    }
+
+                    // Make sure the framework is stopped.
+                    try
+                    {
+                        waitForStop(0);
+                    }
+                    catch (InterruptedException ex)
+                    {
+                        m_logger.log(Logger.LOG_WARNING, "Did not wait for framework to stop.", ex);
+                    }
+
+                    // Depending on the old state, restart the framework.
+                    try
+                    {
+                        switch (oldState)
+                        {
+                            case Bundle.STARTING:
+                                init();
+                                break;
+                            case Bundle.ACTIVE:
+                                start();
+                                break;
+                        }
+                    }
+                    catch (BundleException ex)
+                    {
+                        m_logger.log(Logger.LOG_WARNING, "Exception restarting framework.", ex);
+                    }
+                }
+                catch (Exception ex)
+                {
+                    m_logger.log(Logger.LOG_WARNING, "Cannot update an inactive framework.");
+                }
+            }
+        }).start();
+    }
+
+    public String toString()
+    {
+        return getSymbolicName() + " [" + getBundleId() +"]";
+    }
+
+    /**
+     * Returns the active start level of the framework; this method
+     * implements functionality for the Start Level service.
+     * @return The active start level of the framework.
+    **/
+    int getActiveStartLevel()
+    {
+        return m_activeStartLevel;
+    }
+
+    /**
+     * Implements the functionality of the <tt>setStartLevel()</tt>
+     * method for the StartLevel service, but does not do the security or
+     * parameter check. The security and parameter check are done in the
+     * StartLevel service implementation because this method is called on
+     * a separate thread and the caller's thread would already be gone if
+     * we did the checks in this method. This method should not be called
+     * directly.
+     * @param requestedLevel The new start level of the framework.
+    **/
+    void setActiveStartLevel(int requestedLevel, FrameworkListener[] listeners)
+    {
+        Bundle[] bundles;
+
+        // Record the target start level immediately and use this for
+        // comparisons for starting/stopping bundles to avoid race
+        // conditions to restart stopped bundles.
+        m_targetStartLevel = requestedLevel;
+
+        // Do nothing if the requested start level is the same as the
+        // active start level.
+        if (m_targetStartLevel != m_activeStartLevel)
+        {
+            // Synchronization for changing the start level is rather loose.
+            // The framework's active start level is volatile, so no lock is
+            // needed to access it. No locks are held while processing the
+            // currently installed bundles for starting/stopping based on the new
+            // active start level. The only locking that occurs is for individual
+            // bundles when startBundle()/stopBundle() is called, but this locking
+            // is done in the respective method.
+            //
+            // This approach does mean that it is possible for a for individual
+            // bundle states to change during this operation. If a bundle's start
+            // level changes, then it is possible for it to be processed out of
+            // order. Uninstalled bundles are just logged and ignored.
+            //
+            // Calls to this method are only made by the start level thread, which
+            // serializes framework start level changes. Thus, it is not possible
+            // for two requests to change the framework's start level to interfere
+            // with each other.
+
+            // Acquire global lock.
+            boolean locked = acquireGlobalLock();
+            if (!locked)
+            {
+                // If the calling thread holds bundle locks, then we might not
+                // be able to get the global lock.
+                throw new IllegalStateException(
+                    "Unable to acquire global lock to create bundle snapshot.");
+            }
+
+            boolean bundlesRemaining;
+            try
+            {
+                synchronized (m_startLevelBundles)
+                {
+                    // Get a sorted snapshot of all installed bundles
+                    // to be processed during the start level change.
+                    // We also snapshot the start level here, since it
+                    // may change and we don't want to consider any
+                    // changes since they will be queued for the start
+                    // level thread.
+                    bundles = getBundles();
+                    for (Bundle b : bundles)
+                    {
+                        m_startLevelBundles.add(
+                            new StartLevelTuple(
+                                (BundleImpl) b,
+                                ((BundleImpl) b).getStartLevel(
+                                    getInitialBundleStartLevel())));
+                    }
+                    bundlesRemaining = !m_startLevelBundles.isEmpty();
+                }
+            }
+            finally
+            {
+                releaseGlobalLock();
+            }
+
+            // Determine if we are lowering or raising the
+            // active start level.
+            boolean isLowering = (m_targetStartLevel < m_activeStartLevel);
+            // Determine the range of start levels to process.
+            int low = (isLowering) ? m_targetStartLevel + 1 : m_activeStartLevel + 1;
+            int high = (isLowering) ? m_activeStartLevel : m_targetStartLevel;
+            m_activeStartLevel = (isLowering) ? high : low;
+
+            // Process bundles and stop or start them accordingly.
+            while (bundlesRemaining)
+            {
+                StartLevelTuple tuple;
+
+                // Remove our tuple to be processed while holding the queue lock
+                // and update the active start level accordingly, which allows
+                // us to determine in startBundle() if concurrent requests to
+                // start a bundle should be handled synchronously or just added
+                // to the queue and handled asynchronously.
+                synchronized (m_startLevelBundles)
+                {
+                    if (isLowering)
+                    {
+                        tuple = m_startLevelBundles.last();
+                    }
+                    else
+                    {
+                        tuple = m_startLevelBundles.first();
+                    }
+
+                    if ((tuple.m_level >= low) && (tuple.m_level <= high))
+                    {
+                        m_activeStartLevel = tuple.m_level;
+                    }
+                }
+
+                // Ignore the system bundle, since its start() and
+                // stop() methods get called explicitly in Felix.start()
+                // and Felix.stop(), respectively.
+                if (tuple.m_bundle.getBundleId() != 0)
+                {
+                    // Lock the current bundle.
+                    try
+                    {
+                        acquireBundleLock(tuple.m_bundle,
+                            Bundle.INSTALLED | Bundle.RESOLVED | Bundle.ACTIVE
+                            | Bundle.STARTING | Bundle.STOPPING);
+                    }
+                    catch (IllegalStateException ex)
+                    {
+                        // Ignore if the bundle has been uninstalled.
+                        if (tuple.m_bundle.getState() != Bundle.UNINSTALLED)
+                        {
+                            fireFrameworkEvent(FrameworkEvent.ERROR, tuple.m_bundle, ex);
+                            m_logger.log(tuple.m_bundle,
+                                Logger.LOG_ERROR,
+                                "Error locking " + tuple.m_bundle._getLocation(), ex);
+                        }
+                        else
+                        {
+                            synchronized (m_startLevelBundles)
+                            {
+                                m_startLevelBundles.remove(tuple);
+                                bundlesRemaining = !m_startLevelBundles.isEmpty();
+                            }
+                        }
+                        continue;
+                    }
+
+                    try
+                    {
+                        // Start the bundle if necessary.
+                        // Note that we only attempt to start the bundle if
+                        // its start level is equal to the active start level,
+                        // which means we assume lower bundles are in the state
+                        // they should be in (i.e., we won't attempt to restart
+                        // them if they previously failed to start).
+                        if (!isLowering
+                            && (((tuple.m_bundle.getPersistentState() == Bundle.ACTIVE)
+                                || (tuple.m_bundle.getPersistentState() == Bundle.STARTING))
+                                && (tuple.m_level == m_activeStartLevel)))
+                        {
+                            try
+                            {
+// TODO: LAZY - Not sure if this is the best way...
+                                int options = Bundle.START_TRANSIENT;
+                                options = (tuple.m_bundle.getPersistentState() == Bundle.STARTING)
+                                    ? options | Bundle.START_ACTIVATION_POLICY
+                                    : options;
+                                startBundle(tuple.m_bundle, options);
+                            }
+                            catch (Throwable th)
+                            {
+                                fireFrameworkEvent(FrameworkEvent.ERROR, tuple.m_bundle, th);
+                                m_logger.log(tuple.m_bundle,
+                                    Logger.LOG_ERROR,
+                                    "Error starting " + tuple.m_bundle._getLocation(), th);
+                            }
+                        }
+                        // Stop the bundle if necessary.
+                        else if (isLowering
+                            && (((tuple.m_bundle.getState() == Bundle.ACTIVE)
+                                || (tuple.m_bundle.getState() == Bundle.STARTING))
+                                && (tuple.m_level == m_activeStartLevel)))
+                        {
+                            try
+                            {
+                                stopBundle(tuple.m_bundle, false);
+                            }
+                            catch (Throwable th)
+                            {
+                                fireFrameworkEvent(FrameworkEvent.ERROR, tuple.m_bundle, th);
+                                m_logger.log(tuple.m_bundle,
+                                    Logger.LOG_ERROR,
+                                    "Error stopping " + tuple.m_bundle._getLocation(), th);
+                            }
+                        }
+                    }
+                    finally
+                    {
+                        // Always release bundle lock.
+                        releaseBundleLock(tuple.m_bundle);
+                    }
+                }
+
+                synchronized (m_startLevelBundles)
+                {
+                    m_startLevelBundles.remove(tuple);
+                    bundlesRemaining = !m_startLevelBundles.isEmpty();
+                }
+            }
+
+            m_activeStartLevel = m_targetStartLevel;
+        }
+
+        if (getState() == Bundle.ACTIVE)
+        {
+            fireFrameworkEvent(FrameworkEvent.STARTLEVEL_CHANGED, this, null);
+
+            if (listeners != null)
+            {
+                FrameworkEvent event = new FrameworkEvent(
+                    FrameworkEvent.STARTLEVEL_CHANGED, this, null);
+                for (FrameworkListener l : listeners)
+                {
+                    try
+                    {
+                        l.frameworkEvent(event);
+                    }
+                    catch (Throwable th)
+                    {
+                        m_logger.log(Logger.LOG_ERROR,
+                            "Framework listener delivery error.", th);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Returns the start level into which newly installed bundles will
+     * be placed by default; this method implements functionality for
+     * the Start Level service.
+     * @return The default start level for newly installed bundles.
+    **/
+    int getInitialBundleStartLevel()
+    {
+        String s = (String) m_configMap.get(FelixConstants.BUNDLE_STARTLEVEL_PROP);
+
+        if (s != null)
+        {
+            try
+            {
+                int i = Integer.parseInt(s);
+                return (i > 0) ? i : FelixConstants.BUNDLE_DEFAULT_STARTLEVEL;
+            }
+            catch (NumberFormatException ex)
+            {
+                // Ignore and return the default value.
+            }
+        }
+        return FelixConstants.BUNDLE_DEFAULT_STARTLEVEL;
+    }
+
+    /**
+     * Sets the default start level into which newly installed bundles
+     * will be placed; this method implements functionality for the Start
+     * Level service.
+     * @param startLevel The new default start level for newly installed
+     *        bundles.
+     * @throws java.lang.IllegalArgumentException If the specified start
+     *         level is not greater than zero.
+     * @throws java.security.SecurityException If the caller does not
+     *         have <tt>AdminPermission</tt>.
+    **/
+    void setInitialBundleStartLevel(int startLevel)
+    {
+        if (startLevel <= 0)
+        {
+            throw new IllegalArgumentException(
+                "Initial start level must be greater than zero.");
+        }
+
+        m_configMutableMap.put(
+            FelixConstants.BUNDLE_STARTLEVEL_PROP, Integer.toString(startLevel));
+    }
+
+    /**
+     * Returns the start level for the specified bundle; this method
+     * implements functionality for the Start Level service.
+     * @param bundle The bundle to examine.
+     * @return The start level of the specified bundle.
+     * @throws java.lang.IllegalArgumentException If the specified
+     *          bundle has been uninstalled.
+    **/
+    int getBundleStartLevel(Bundle bundle)
+    {
+        if (bundle.getState() == Bundle.UNINSTALLED)
+        {
+            throw new IllegalArgumentException("Bundle is uninstalled.");
+        }
+
+        return ((BundleImpl) bundle).getStartLevel(getInitialBundleStartLevel());
+    }
+
+    /**
+     * Sets the start level of the specified bundle; this method
+     * implements functionality for the Start Level service.
+     * @param bundle The bundle whose start level is to be modified.
+     * @param startLevel The new start level of the specified bundle.
+     * @throws java.lang.IllegalArgumentException If the specified
+     *          bundle is the system bundle or if the bundle has been
+     *          uninstalled.
+     * @throws java.security.SecurityException If the caller does not
+     *          have <tt>AdminPermission</tt>.
+    **/
+    void setBundleStartLevel(Bundle bundle, int startLevel)
+    {
+        // Acquire bundle lock.
+        BundleImpl impl = (BundleImpl) bundle;
+        try
+        {
+            acquireBundleLock(impl,
+                Bundle.INSTALLED | Bundle.RESOLVED | Bundle.ACTIVE
+                | Bundle.STARTING | Bundle.STOPPING);
+        }
+        catch (IllegalStateException ex)
+        {
+            fireFrameworkEvent(FrameworkEvent.ERROR, impl, ex);
+            m_logger.log(impl,
+                Logger.LOG_ERROR,
+                "Error locking " + impl._getLocation(), ex);
+            return;
+        }
+
+        Throwable rethrow = null;
+
+        try
+        {
+            if (startLevel >= 1)
+            {
+                // NOTE: The start level was persistently recorded inside
+                // the start level impl because the spec requires it to be
+                // done synchronously.
+
+                try
+                {
+                    // Start the bundle if necessary.
+                    // We don't need to be concerned about the target
+                    // start level here, since this method is only executed
+                    // by the start level thread, so no start level change
+                    // can be occurring concurrently.
+                    if (((impl.getPersistentState() == Bundle.ACTIVE)
+                        || (impl.getPersistentState() == Bundle.STARTING))
+                        && (startLevel <= m_activeStartLevel))
+                    {
+// TODO: LAZY - Not sure if this is the best way...
+                        int options = Bundle.START_TRANSIENT;
+                        options = (impl.getPersistentState() == Bundle.STARTING)
+                            ? options | Bundle.START_ACTIVATION_POLICY
+                            : options;
+                        startBundle(impl, options);
+                    }
+                    // Stop the bundle if necessary.
+                    // We don't need to be concerned about the target
+                    // start level here, since this method is only executed
+                    // by the start level thread, so no start level change
+                    // can be occurring concurrently.
+                    else if (((impl.getState() == Bundle.ACTIVE)
+                        || (impl.getState() == Bundle.STARTING))
+                        && (startLevel > m_activeStartLevel))
+                    {
+                        stopBundle(impl, false);
+                    }
+                }
+                catch (Throwable th)
+                {
+                    rethrow = th;
+                    m_logger.log(
+                        impl,
+                        Logger.LOG_ERROR, "Error starting/stopping bundle.", th);
+                }
+            }
+            else
+            {
+                m_logger.log(
+                    impl,
+                    Logger.LOG_WARNING, "Bundle start level must be greater than zero.");
+            }
+        }
+        finally
+        {
+            // Always release bundle lock.
+            releaseBundleLock(impl);
+        }
+
+        if (rethrow != null)
+        {
+            fireFrameworkEvent(FrameworkEvent.ERROR, bundle, rethrow);
+        }
+    }
+
+    /**
+     * Returns whether a bundle is persistently started; this is an
+     * method implementation for the Start Level service.
+     * @param bundle The bundle to examine.
+     * @return <tt>true</tt> if the bundle is marked as persistently
+     *          started, <tt>false</tt> otherwise.
+     * @throws java.lang.IllegalArgumentException If the specified
+     *          bundle has been uninstalled.
+    **/
+    boolean isBundlePersistentlyStarted(Bundle bundle)
+    {
+        if (bundle.getState() == Bundle.UNINSTALLED)
+        {
+            throw new IllegalArgumentException("Bundle is uninstalled.");
+        }
+
+        return (((BundleImpl) bundle).getPersistentState() == Bundle.ACTIVE)
+            || (((BundleImpl) bundle).getPersistentState() == Bundle.STARTING);
+    }
+
+    /**
+     * Returns whether the bundle is using its declared activation policy;
+     * this is an method implementation for the Start Level service.
+     * @param bundle The bundle to examine.
+     * @return <tt>true</tt> if the bundle is using its declared activation
+     *         policy, <tt>false</tt> otherwise.
+     * @throws java.lang.IllegalArgumentException If the specified
+     *          bundle has been uninstalled.
+    **/
+    boolean isBundleActivationPolicyUsed(Bundle bundle)
+    {
+        if (bundle.getState() == Bundle.UNINSTALLED)
+        {
+            throw new IllegalArgumentException("Bundle is uninstalled.");
+        }
+
+        return ((BundleImpl) bundle).isDeclaredActivationPolicyUsed();
+    }
+
+    //
+    // Implementation of Bundle interface methods.
+    //
+
+    /**
+     * Get bundle headers and resolve any localized strings from resource bundles.
+     * @param bundle
+     * @param locale
+     * @return localized bundle headers dictionary.
+    **/
+    Dictionary getBundleHeaders(BundleImpl bundle, String locale)
+    {
+        return new MapToDictionary(bundle.getCurrentLocalizedHeader(locale));
+    }
+
+    /**
+     * Implementation for Bundle.getResource().
+    **/
+    URL getBundleResource(BundleImpl bundle, String name)
+    {
+        if (bundle.getState() == Bundle.UNINSTALLED)
+        {
+            throw new IllegalStateException("The bundle is uninstalled.");
+        }
+        else if (Util.isFragment(bundle.adapt(BundleRevision.class)))
+        {
+            return null;
+        }
+        else if (bundle.getState() == Bundle.INSTALLED)
+        {
+            try
+            {
+                resolveBundleRevision(bundle.adapt(BundleRevision.class));
+            }
+            catch (Exception ex)
+            {
+                // Ignore.
+            }
+        }
+
+        // If the bundle revision isn't resolved, then just search
+        // locally, otherwise delegate.
+        if (bundle.adapt(BundleRevision.class).getWiring() == null)
+        {
+            return ((BundleRevisionImpl) bundle.adapt(BundleRevision.class))
+                .getResourceLocal(name);
+        }
+        else
+        {
+            return ((BundleWiringImpl) bundle.adapt(BundleRevision.class).getWiring())
+                .getResourceByDelegation(name);
+        }
+    }
+
+    /**
+     * Implementation for Bundle.getResources().
+    **/
+    Enumeration getBundleResources(BundleImpl bundle, String name)
+    {
+        if (bundle.getState() == Bundle.UNINSTALLED)
+        {
+            throw new IllegalStateException("The bundle is uninstalled.");
+        }
+        else if (Util.isFragment(bundle.adapt(BundleRevision.class)))
+        {
+            return null;
+        }
+        else if (bundle.getState() == Bundle.INSTALLED)
+        {
+            try
+            {
+                resolveBundleRevision(bundle.adapt(BundleRevision.class));
+            }
+            catch (Exception ex)
+            {
+                // Ignore.
+            }
+        }
+
+        if (bundle.adapt(BundleRevision.class).getWiring() == null)
+        {
+            return ((BundleRevisionImpl) bundle.adapt(BundleRevision.class))
+                .getResourcesLocal(name);
+        }
+        else
+        {
+            return ((BundleWiringImpl) bundle.adapt(BundleRevision.class).getWiring())
+                .getResourcesByDelegation(name);
+        }
+    }
+
+    /**
+     * Implementation for Bundle.getEntry().
+    **/
+    URL getBundleEntry(BundleImpl bundle, String name)
+    {
+        if (bundle.getState() == Bundle.UNINSTALLED)
+        {
+            throw new IllegalStateException("The bundle is uninstalled.");
+        }
+
+        URL url = ((BundleRevisionImpl) bundle.adapt(BundleRevision.class)).getEntry(name);
+
+        // Some JAR files do not contain directory entries, so if
+        // the entry wasn't found and is a directory, scan the entries
+        // to see if we should synthesize an entry for it.
+        if ((url == null) && name.endsWith("/") && !name.equals("/"))
+        {
+            // Use the entry filter enumeration to search the bundle content
+            // recursively for matching entries and return URLs to them.
+            Enumeration enumeration =
+                new EntryFilterEnumeration(
+                    bundle.adapt(BundleRevision.class), false, name, "*", true, true);
+            // If the enumeration has elements, then that means we need
+            // to synthesize the directory entry.
+            if (enumeration.hasMoreElements())
+            {
+                URL entryURL = (URL) enumeration.nextElement();
+                try
+                {
+                    url = new URL(entryURL, ((name.charAt(0) == '/') ? name : "/" + name));
+                }
+                catch (MalformedURLException ex)
+                {
+                    url = null;
+                }
+            }
+        }
+        return url;
+    }
+
+    /**
+     * Implementation for Bundle.getEntryPaths().
+    **/
+    Enumeration getBundleEntryPaths(BundleImpl bundle, String path)
+    {
+        if (bundle.getState() == Bundle.UNINSTALLED)
+        {
+            throw new IllegalStateException("The bundle is uninstalled.");
+        }
+
+        // Get the entry enumeration from the revision content and
+        // create a wrapper enumeration to filter it.
+        Enumeration enumeration =
+            new EntryFilterEnumeration(
+                bundle.adapt(BundleRevision.class), false, path, "*", false, false);
+
+        // Return the enumeration if it has elements.
+        return (!enumeration.hasMoreElements()) ? null : enumeration;
+    }
+
+    /**
+     * Implementation for Bundle.findEntries().
+    **/
+    Enumeration findBundleEntries(
+            BundleImpl bundle, String path, String filePattern, boolean recurse)
+    {
+        if (bundle.getState() == Bundle.UNINSTALLED)
+        {
+            throw new IllegalStateException("The bundle is uninstalled.");
+        }
+        else if (!Util.isFragment(bundle.adapt(BundleRevision.class)) && bundle.getState() == Bundle.INSTALLED)
+        {
+            try
+            {
+                resolveBundleRevision(bundle.adapt(BundleRevision.class));
+            }
+            catch (Exception ex)
+            {
+                // Ignore.
+            }
+        }
+        return findBundleEntries(
+                bundle.adapt(BundleRevision.class), path, filePattern, recurse);
+    }
+
+    /**
+     * Implementation for BundleWiring.findEntries().
+     **/
+    Enumeration findBundleEntries(
+        BundleRevision revision, String path, String filePattern, boolean recurse)
+    {
+        // Get the entry enumeration from the revision content and
+        // create a wrapper enumeration to filter it.
+        Enumeration enumeration =
+            new EntryFilterEnumeration(revision, true, path, filePattern, recurse, true);
+
+        // Return the enumeration if it has elements.
+        return (!enumeration.hasMoreElements()) ? null : enumeration;
+    }
+
+    ServiceReference[] getBundleRegisteredServices(BundleImpl bundle)
+    {
+        if (bundle.getState() == Bundle.UNINSTALLED)
+        {
+            throw new IllegalStateException("The bundle is uninstalled.");
+        }
+
+        // Filter list of registered service references.
+        ServiceReference[] refs = m_registry.getRegisteredServices(bundle);
+
+        return refs;
+    }
+
+    ServiceReference[] getBundleServicesInUse(Bundle bundle)
+    {
+        // Filter list of "in use" service references.
+        ServiceReference[] refs = m_registry.getServicesInUse(bundle);
+
+        return refs;
+    }
+
+    boolean bundleHasPermission(BundleImpl bundle, Object obj)
+    {
+        if (bundle.getState() == Bundle.UNINSTALLED)
+        {
+            throw new IllegalStateException("The bundle is uninstalled.");
+        }
+
+        if (System.getSecurityManager() != null)
+        {
+            try
+            {
+                return (obj instanceof java.security.Permission)
+                    ? impliesBundlePermission(
+                    (BundleProtectionDomain)
+                    bundle.getProtectionDomain(),
+                    (java.security.Permission) obj, true)
+                    : false;
+            }
+            catch (Exception ex)
+            {
+                m_logger.log(
+                    bundle,
+                    Logger.LOG_WARNING,
+                    "Exception while evaluating the permission.",
+                    ex);
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Implementation for Bundle.loadClass().
+    **/
+    Class loadBundleClass(BundleImpl bundle, String name) throws ClassNotFoundException
+    {
+        if (bundle.getState() == Bundle.UNINSTALLED)
+        {
+            throw new IllegalStateException("Bundle is uninstalled");
+        }
+        else if (Util.isFragment(bundle.adapt(BundleRevision.class)))
+        {
+            throw new ClassNotFoundException("Fragments cannot load classes.");
+        }
+        else if (bundle.getState() == Bundle.INSTALLED)
+        {
+            try
+            {
+                resolveBundleRevision(bundle.adapt(BundleRevision.class));
+            }
+            catch (BundleException ex)
+            {
+                // The spec says we must fire a framework error.
+                fireFrameworkEvent(FrameworkEvent.ERROR, bundle, ex);
+                // Then throw a class not found exception.
+                throw new ClassNotFoundException(name, ex);
+            }
+        }
+
+        // We do not call getClassLoader().loadClass() for arrays because
+        // it does not correctly handle array types, which is necessary in
+        // cases like deserialization using a wrapper class loader.
+        if ((name != null) && (name.length() > 0) && (name.charAt(0) == '['))
+        {
+            return Class.forName(name, false,
+                ((BundleWiringImpl) bundle.adapt(BundleWiring.class)).getClassLoader());
+        }
+
+        return ((BundleWiringImpl)
+            bundle.adapt(BundleWiring.class)).getClassLoader().loadClass(name);
+    }
+
+    /**
+     * Implementation for Bundle.start().
+    **/
+    void startBundle(BundleImpl bundle, int options) throws BundleException
+    {
+        // CONCURRENCY NOTE:
+        // We will first acquire the bundle lock for the specific bundle
+        // as long as the bundle is INSTALLED, RESOLVED, or ACTIVE. If this
+        // bundle is not yet resolved, then it will be resolved too. In
+        // that case, the global lock will be acquired to make sure no
+        // bundles can be installed or uninstalled during the resolve.
+
+        int eventType;
+        boolean isTransient = (options & Bundle.START_TRANSIENT) != 0;
+
+        // Acquire bundle lock.
+        try
+        {
+            acquireBundleLock(bundle,
+                Bundle.INSTALLED | Bundle.RESOLVED | Bundle.STARTING | Bundle.ACTIVE | Bundle.STOPPING);
+        }
+        catch (IllegalStateException ex)
+        {
+            if (bundle.getState() == Bundle.UNINSTALLED)
+            {
+                throw new IllegalStateException("Cannot start an uninstalled bundle.");
+            }
+            else
+            {
+                throw new BundleException(
+                    "Bundle " + bundle
+                    + " cannot be started, since it is either starting or stopping.");
+            }
+        }
+
+        // Record whether the bundle is using its declared activation policy.
+        boolean wasDeferred = bundle.isDeclaredActivationPolicyUsed()
+            && (((BundleRevisionImpl) bundle.adapt(BundleRevision.class))
+                .getDeclaredActivationPolicy() == BundleRevisionImpl.LAZY_ACTIVATION);
+        bundle.setDeclaredActivationPolicyUsed(
+            (options & Bundle.START_ACTIVATION_POLICY) != 0);
+
+        BundleException rethrow = null;
+        try
+        {
+            // The spec doesn't say whether it is possible to start an extension
+            // We just do nothing
+            if (bundle.isExtension())
+            {
+                return;
+            }
+
+            // As per the OSGi spec, fragment bundles can not be started and must
+            // throw a BundleException when there is an attempt to start one.
+            if (Util.isFragment(bundle.adapt(BundleRevision.class)))
+            {
+                throw new BundleException("Fragment bundles can not be started.");
+            }
+
+            // Set and save the bundle's persistent state to active
+            // if we are supposed to record state change.
+            if (!isTransient)
+            {
+                if ((options & Bundle.START_ACTIVATION_POLICY) != 0)
+                {
+                    bundle.setPersistentStateStarting();
+                }
+                else
+                {
+                    bundle.setPersistentStateActive();
+                }
+            }
+
+            // Check to see if the bundle's start level is greater than the
+            // the framework's target start level and if so just return. For
+            // transient starts we compare their start level to the current
+            // active start level, since we never process transient starts
+            // asynchronously (i.e., they are either satisfied and process
+            // synchronously here or they throw an exception).
+            int bundleLevel = bundle.getStartLevel(getInitialBundleStartLevel());
+            if (isTransient && (bundleLevel > m_activeStartLevel))
+            {
+                // Throw an exception for transient starts.
+                throw new BundleException(
+                    "Cannot start bundle " + bundle + " because its start level is "
+                    + bundleLevel
+                    + ", which is greater than the framework's start level of "
+                    + m_activeStartLevel + ".");
+            }
+            else if (bundleLevel > m_targetStartLevel)
+            {
+                // Ignore persistent starts that are not satisfied.
+                return;
+            }
+
+            // Check to see if there is a start level change in progress and if
+            // so queue this bundle to the start level bundle queue for the start
+            // level thread and return, except for transient starts which are
+            // queued but processed synchronously.
+            // Note: Don't queue starts from the start level thread, otherwise
+            // we'd never get anything started.
+            if (!Thread.currentThread().getName().equals(FrameworkStartLevelImpl.THREAD_NAME))
+            {
+                synchronized (m_startLevelBundles)
+                {
+                    // Since we have the start level queue lock, we know the
+                    // active start level cannot change. For transient starts
+                    // we now need to double check and make sure we can still
+                    // start them since technically the active start level could
+                    // have changed.
+                    if (isTransient && (bundleLevel > m_activeStartLevel))
+                    {
+                        throw new BundleException(
+                            "Cannot start bundle " + bundle + " because its start level is "
+                            + bundleLevel
+                            + ", which is greater than the framework's start level of "
+                            + m_activeStartLevel + ".");
+                    }
+
+                    // If the start level bundle queue is not empty, then we know
+                    // there is a start level operation ongoing. We know that the
+                    // bundle being started is satisfied by the target start level
+                    // otherwise we wouldn't be here. In most cases we simply want
+                    // to queue the bundle; however, if the bundle level is lower
+                    // than the current active start level, then we want to handle
+                    // it synchronously. This is because the start level thread
+                    // ignores bundles that it should have already processed.
+                    // So queue the bundle if its bundle start level is greater
+                    // or equal to the active start level and then simply return
+                    // since it will be processed asynchronously.
+                    if (!m_startLevelBundles.isEmpty()
+                        && (bundleLevel >= m_activeStartLevel))
+                    {
+                        // Only add the bundle to the start level bundles
+                        // being process if it is not already there.
+                        boolean found = false;
+                        for (StartLevelTuple tuple : m_startLevelBundles)
+                        {
+                            if (tuple.m_bundle == bundle)
+                            {
+                                found = true;
+                            }
+                        }
+
+                        if (!found)
+                        {
+                            m_startLevelBundles.add(new StartLevelTuple(bundle, bundleLevel));
+                        }
+
+                        // Note that although we queued the transiently started
+                        // bundle, we don't return here because we handle all
+                        // transient bundles synchronously. The reason why we
+                        // queue it anyway is for the case where the start level
+                        // is lowering, since the transiently started bundle may
+                        // have already been processed by the start level thread
+                        // and we will start it briefly here synchronously, but
+                        // we want the start level thread to process it again
+                        // so it gets another chance to stop it. This is not an
+                        // issue when the start level is raising because the
+                        // start level thread ignores non-persistently started
+                        // bundles or if it is also persistently started it will
+                        // be a no-op.
+                        if (!isTransient)
+                        {
+                            return;
+                        }
+                    }
+                }
+            }
+
+            switch (bundle.getState())
+            {
+                case Bundle.UNINSTALLED:
+                    throw new IllegalStateException("Cannot start an uninstalled bundle.");
+                case Bundle.STARTING:
+                    if (!wasDeferred)
+                    {
+                        throw new BundleException(
+                            "Bundle " + bundle
+                            + " cannot be started, since it is starting.");
+                    }
+                    break;
+                case Bundle.STOPPING:
+                    throw new BundleException(
+                        "Bundle " + bundle
+                        + " cannot be started, since it is stopping.");
+                case Bundle.ACTIVE:
+                    return;
+                case Bundle.INSTALLED:
+                    resolveBundleRevision(bundle.adapt(BundleRevision.class));
+                    // No break.
+                case Bundle.RESOLVED:
+                    // Set the bundle's context.
+                    bundle.setBundleContext(new BundleContextImpl(m_logger, this, bundle));
+                    // At this point, no matter if the bundle's activation policy is
+                    // eager or deferred, we need to set the bundle's state to STARTING.
+                    // We don't fire a BundleEvent here for this state change, since
+                    // STARTING events are only fired if we are invoking the activator,
+                    // which we may not do if activation is deferred.
+                    setBundleStateAndNotify(bundle, Bundle.STARTING);
+                    break;
+            }
+
+            // If the bundle's activation policy is eager or activation has already
+            // been triggered, then activate the bundle immediately.
+            if (!bundle.isDeclaredActivationPolicyUsed()
+                || (((BundleRevisionImpl) bundle.adapt(BundleRevision.class))
+                    .getDeclaredActivationPolicy() != BundleRevisionImpl.LAZY_ACTIVATION)
+                || ((BundleClassLoader) bundle.adapt(BundleWiring.class).getClassLoader())
+                    .isActivationTriggered())
+            {
+                // Record the event type for the final event and activate.
+                eventType = BundleEvent.STARTED;
+                // Note that the STARTING event is thrown in the activateBundle() method.
+                try
+                {
+                    activateBundle(bundle, false);
+                }
+                catch (BundleException ex)
+                {
+                    rethrow = ex;
+                }
+            }
+            // Otherwise, defer bundle activation.
+            else
+            {
+                // Record the event type for the final event.
+                eventType = BundleEvent.LAZY_ACTIVATION;
+            }
+
+            // We still need to fire the STARTED event, but we will do
+            // it later so we can release the bundle lock.
+        }
+        finally
+        {
+            // Release bundle lock.
+            releaseBundleLock(bundle);
+        }
+
+        // If there was no exception, then we should fire the STARTED
+        // or LAZY_ACTIVATION event here without holding the lock. Otherwise,
+        // fire STOPPED and rethrow exception.
+        if (rethrow == null)
+        {
+            fireBundleEvent(eventType, bundle);
+        }
+        else
+        {
+            fireBundleEvent(BundleEvent.STOPPED, bundle);
+            throw rethrow;
+        }
+    }
+
+    void activateBundle(BundleImpl bundle, boolean fireEvent) throws BundleException
+    {
+        // CONCURRENCY NOTE:
+        // We will first acquire the bundle lock for the specific bundle
+        // as long as the bundle is STARTING or ACTIVE, which is necessary
+        // because we may change the bundle state.
+
+        // Acquire bundle lock.
+        try
+        {
+            acquireBundleLock(bundle, Bundle.STARTING | Bundle.ACTIVE);
+        }
+        catch (IllegalStateException ex)
+        {
+            throw new IllegalStateException(
+                "Activation only occurs for bundles in STARTING state.");
+        }
+
+        try
+        {
+            // If the bundle is already active or its start level is not met,
+            // simply return. Generally, the bundle start level should always
+            // be less than or equal to the active start level since the bundle
+            // must be in the STARTING state to activate it. One potential corner
+            // case is if the bundle is being lazily activated at the same time
+            // there is a start level change going on to lower the start level.
+            // In that case, we test here and avoid activating the bundle since
+            // it will be stopped by the start level thread.
+            if ((bundle.getState() == Bundle.ACTIVE) ||
+                (bundle.getStartLevel(getInitialBundleStartLevel()) > m_targetStartLevel))
+            {
+                return;
+            }
+
+            // Fire STARTING event to signify call to bundle activator.
+            fireBundleEvent(BundleEvent.STARTING, bundle);
+
+            try
+            {
+                // Set the bundle's activator.
+                bundle.setActivator(createBundleActivator(bundle));
+
+                // Activate the bundle if it has an activator.
+                if (bundle.getActivator() != null)
+                {
+                    m_secureAction.startActivator(
+                        bundle.getActivator(), bundle._getBundleContext());
+                }
+
+                setBundleStateAndNotify(bundle, Bundle.ACTIVE);
+
+                // We still need to fire the STARTED event, but we will do
+                // it later so we can release the bundle lock.
+            }
+            catch (Throwable th)
+            {
+                // Spec says we must fire STOPPING event.
+                fireBundleEvent(BundleEvent.STOPPING, bundle);
+
+                // If there was an error starting the bundle,
+                // then reset its state to RESOLVED.
+                setBundleStateAndNotify(bundle, Bundle.RESOLVED);
+
+                // Clean up the bundle activator
+                bundle.setActivator(null);
+
+                // Clean up the bundle context.
+                // We invalidate this first to make sure it cannot be used
+                // after stopping the activator.
+                BundleContextImpl bci = (BundleContextImpl) bundle._getBundleContext();
+                bci.invalidate();
+                bundle.setBundleContext(null);
+
+                // Unregister any services offered by this bundle.
+                m_registry.unregisterServices(bundle);
+
+                // Release any services being used by this bundle.
+                m_registry.ungetServices(bundle);
+
+                // Remove any listeners registered by this bundle.
+                m_dispatcher.removeListeners(bci);
+
+                // The spec says to expect BundleException or
+                // SecurityException, so rethrow these exceptions.
+                if (th instanceof BundleException)
+                {
+                    throw (BundleException) th;
+                }
+                else if ((System.getSecurityManager() != null) &&
+                    (th instanceof java.security.PrivilegedActionException))
+                {
+                    th = ((java.security.PrivilegedActionException) th).getException();
+                }
+
+                // Rethrow all other exceptions as a BundleException.
+                throw new BundleException(
+                    "Activator start error in bundle " + bundle + ".",
+                    BundleException.ACTIVATOR_ERROR, th);
+            }
+        }
+        finally
+        {
+            // Release bundle lock.
+            releaseBundleLock(bundle);
+        }
+
+        // If there was no exception, then we should fire the STARTED
+        // event here without holding the lock if specified.
+        // TODO: LAZY - It would be nice to figure out how to do this without
+        //       duplicating code; this method is called from two different
+        //       places -- one fires the event itself the other one needs it.
+        if (fireEvent)
+        {
+            fireBundleEvent(BundleEvent.STARTED, bundle);
+        }
+    }
+
+    void updateBundle(BundleImpl bundle, InputStream is)
+        throws BundleException
+    {
+        // Acquire bundle lock.
+        try
+        {
+            acquireBundleLock(bundle, Bundle.INSTALLED | Bundle.RESOLVED | Bundle.ACTIVE);
+        }
+        catch (IllegalStateException ex)
+        {
+            if (bundle.getState() == Bundle.UNINSTALLED)
+            {
+                throw new IllegalStateException("Cannot update an uninstalled bundle.");
+            }
+            else
+            {
+                throw new BundleException(
+                    "Bundle " + bundle
+                    + " cannot be update, since it is either starting or stopping.");
+            }
+        }
+
+        // We must release the lock and close the input stream, so do both
+        // in a finally block.
+        try
+        {
+            // Variable to indicate whether bundle is active or not.
+            Throwable rethrow = null;
+
+            final int oldState = bundle.getState();
+
+            // First get the update-URL from our header.
+            String updateLocation = (String)
+                ((BundleRevisionImpl) bundle.adapt(BundleRevision.class))
+                    .getHeaders().get(Constants.BUNDLE_UPDATELOCATION);
+
+            // If no update location specified, use original location.
+            if (updateLocation == null)
+            {
+                updateLocation = bundle._getLocation();
+            }
+
+            // Stop the bundle if it is active, but do not change its
+            // persistent state.
+            if (oldState == Bundle.ACTIVE)
+            {
+                stopBundle(bundle, false);
+            }
+
+            try
+            {
+                // Revising the bundle creates a new revision, which modifies
+                // the global state, so we need to acquire the global lock
+                // before revising.
+                boolean locked = acquireGlobalLock();
+                if (!locked)
+                {
+                    throw new BundleException(
+                        "Cannot acquire global lock to update the bundle.");
+                }
+                try
+                {
+                    // Try to revise.
+                    boolean wasExtension = bundle.isExtension();
+                    bundle.revise(updateLocation, is);
+
+                    // Verify bundle revision.
+                    try
+                    {
+                        Object sm = System.getSecurityManager();
+
+                        if (sm != null)
+                        {
+                            ((SecurityManager) sm).checkPermission(
+                                new AdminPermission(bundle, AdminPermission.LIFECYCLE));
+                        }
+
+                        // If this is an update from a normal to an extension bundle
+                        // then attach the extension
+                        if (!wasExtension && bundle.isExtension())
+                        {
+                            m_extensionManager.addExtensionBundle(this, bundle);
+// TODO: REFACTOR - Perhaps we could move this into extension manager.
+                            m_resolver.addRevision(m_extensionManager.getRevision());
+// TODO: REFACTOR - Not clear why this is here. We should look at all of these steps more closely.
+                            setBundleStateAndNotify(bundle, Bundle.RESOLVED);
+                        }
+                        else if (wasExtension)
+                        {
+                            setBundleStateAndNotify(bundle, Bundle.INSTALLED);
+                        }
+                    }
+                    catch (Throwable ex)
+                    {
+                        try
+                        {
+                            bundle.rollbackRevise();
+                        }
+                        catch (Exception busted)
+                        {
+                            m_logger.log(
+                                bundle, Logger.LOG_ERROR, "Unable to rollback.", busted);
+                        }
+
+                        throw ex;
+                    }
+                }
+                finally
+                {
+                    // Always release the global lock.
+                    releaseGlobalLock();
+                }
+            }
+            catch (Throwable ex)
+            {
+                m_logger.log(
+                    bundle, Logger.LOG_ERROR, "Unable to update the bundle.", ex);
+                rethrow = ex;
+            }
+
+            // Set new state, mark as needing a refresh, and fire updated event
+            // if successful.
+            if (rethrow == null)
+            {
+                bundle.setLastModified(System.currentTimeMillis());
+
+                if (!bundle.isExtension())
+                {
+                    setBundleStateAndNotify(bundle, Bundle.INSTALLED);
+                }
+                else
+                {
+                    m_extensionManager.startExtensionBundle(this, bundle);
+                }
+
+                fireBundleEvent(BundleEvent.UNRESOLVED, bundle);
+
+                fireBundleEvent(BundleEvent.UPDATED, bundle);
+
+                // Acquire global lock to check if we should auto-refresh.
+                boolean locked = acquireGlobalLock();
+                // If we did not get the global lock, then do not try to
+                // auto-refresh.
+                if (locked)
+                {
+                    try
+                    {
+                        if (!m_dependencies.hasDependents(bundle)
+                            && !bundle.isExtension())
+                        {
+                            try
+                            {
+                                List<Bundle> list = new ArrayList<Bundle>(1);
+                                list.add(bundle);
+                                refreshPackages(list, null);
+                            }
+                            catch (Exception ex)
+                            {
+                                m_logger.log(bundle,
+                                    Logger.LOG_ERROR,
+                                    "Unable to immediately purge the bundle revisions.", ex);
+                            }
+                        }
+                    }
+                    finally
+                    {
+                        // Always release the global lock.
+                        releaseGlobalLock();
+                    }
+                }
+            }
+
+            // If the old state was active, but the new revision is a fragment,
+            // then mark the persistent state to inactive.
+            if ((oldState == Bundle.ACTIVE)
+                && Util.isFragment(bundle.adapt(BundleRevision.class)))
+            {
+                bundle.setPersistentStateInactive();
+                m_logger.log(bundle, Logger.LOG_WARNING,
+                    "Previously active bundle was updated to a fragment, resetting state to inactive: "
+                    + bundle);
+            }
+            // Otherwise, restart the bundle if it was previously active,
+            // but do not change its persistent state.
+            else if (oldState == Bundle.ACTIVE)
+            {
+                startBundle(bundle, Bundle.START_TRANSIENT);
+            }
+
+            // If update failed, rethrow exception.
+            if (rethrow != null)
+            {
+                if (rethrow instanceof AccessControlException)
+                {
+                    throw (AccessControlException) rethrow;
+                }
+                else if (rethrow instanceof BundleException)
+                {
+                    throw (BundleException) rethrow;
+                }
+                else
+                {
+                    throw new BundleException(
+                        "Update of bundle " + bundle + " failed.", rethrow);
+                }
+            }
+        }
+        finally
+        {
+            // Close the input stream.
+            try
+            {
+                if (is != null) is.close();
+            }
+            catch (Exception ex)
+            {
+                m_logger.log(bundle, Logger.LOG_ERROR, "Unable to close input stream.", ex);
+            }
+
+            // Release bundle lock.
+            releaseBundleLock(bundle);
+        }
+    }
+
+    void stopBundle(BundleImpl bundle, boolean record)
+        throws BundleException
+    {
+        // Acquire bundle lock.
+        try
+        {
+            acquireBundleLock(bundle,
+                Bundle.INSTALLED | Bundle.RESOLVED | Bundle.STARTING | Bundle.ACTIVE | Bundle.STOPPING);
+        }
+        catch (IllegalStateException ex)
+        {
+            if (bundle.getState() == Bundle.UNINSTALLED)
+            {
+                throw new IllegalStateException("Cannot stop an uninstalled bundle.");
+            }
+            else
+            {
+                throw new BundleException(
+                    "Bundle " + bundle
+                    + " cannot be stopped since it is already stopping.");
+            }
+        }
+
+        try
+        {
+            Throwable rethrow = null;
+
+            // Set the bundle's persistent state to inactive if necessary.
+            if (record)
+            {
+                bundle.setPersistentStateInactive();
+            }
+
+            // If the bundle is not persistently started, then we
+            // need to reset the activation policy flag, since it
+            // does not persist across persistent stops or transient
+            // stops.
+            if (!isBundlePersistentlyStarted(bundle))
+            {
+                bundle.setDeclaredActivationPolicyUsed(false);
+            }
+
+            // As per the OSGi spec, fragment bundles can not be stopped and must
+            // throw a BundleException when there is an attempt to stop one.
+            if (Util.isFragment(bundle.adapt(BundleRevision.class)))
+            {
+                throw new BundleException("Fragment bundles can not be stopped: " + bundle);
+            }
+
+            boolean wasActive = false;
+            switch (bundle.getState())
+            {
+                case Bundle.UNINSTALLED:
+                    throw new IllegalStateException("Cannot stop an uninstalled bundle.");
+                case Bundle.STARTING:
+                    if (bundle.isDeclaredActivationPolicyUsed()
+                        && ((BundleRevisionImpl) bundle.adapt(BundleRevision.class))
+                            .getDeclaredActivationPolicy() != BundleRevisionImpl.LAZY_ACTIVATION)
+                    {
+                        throw new BundleException(
+                            "Stopping a starting or stopping bundle is currently not supported.");
+                    }
+                    break;
+                case Bundle.STOPPING:
+                    throw new BundleException(
+                        "Stopping a starting or stopping bundle is currently not supported.");
+                case Bundle.INSTALLED:
+                case Bundle.RESOLVED:
+                    return;
+                case Bundle.ACTIVE:
+                    wasActive = true;
+                    break;
+            }
+
+            // At this point, no matter if the bundle's activation policy is
+            // eager or deferred, we need to set the bundle's state to STOPPING
+            // and fire the STOPPING event.
+            setBundleStateAndNotify(bundle, Bundle.STOPPING);
+            fireBundleEvent(BundleEvent.STOPPING, bundle);
+
+            // If the bundle was active, then invoke the activator stop() method
+            // or if we are stopping the system bundle.
+            if ((wasActive) || (bundle.getBundleId() == 0))
+            {
+                try
+                {
+                    if (bundle.getActivator() != null)
+                    {
+                        m_secureAction.stopActivator(bundle.getActivator(), bundle._getBundleContext());
+                    }
+                }
+                catch (Throwable th)
+                {
+                    m_logger.log(bundle, Logger.LOG_ERROR, "Error stopping bundle.", th);
+                    rethrow = th;
+                }
+            }
+
+            // Do not clean up after the system bundle since it will
+            // clean up after itself.
+            if (bundle.getBundleId() != 0)
+            {
+                // Clean up the bundle activator.
+                bundle.setActivator(null);
+
+                // Clean up the bundle context.
+                // We invalidate this first to make sure it cannot be used
+                // after stopping the activator.
+                BundleContextImpl bci = (BundleContextImpl) bundle._getBundleContext();
+                bci.invalidate();
+                bundle.setBundleContext(null);
+
+                // Unregister any services offered by this bundle.
+                m_registry.unregisterServices(bundle);
+
+                // Release any services being used by this bundle.
+                m_registry.ungetServices(bundle);
+
+                // The spec says that we must remove all event
+                // listeners for a bundle when it is stopped.
+                m_dispatcher.removeListeners(bci);
+
+                setBundleStateAndNotify(bundle, Bundle.RESOLVED);
+
+                // We still need to fire the STOPPED event, but we will do
+                // it later so we can release the bundle lock.
+            }
+
+            // Throw activator error if there was one.
+            if (rethrow != null)
+            {
+                // The spec says to expect BundleException or
+                // SecurityException, so rethrow these exceptions.
+                if (rethrow instanceof BundleException)
+                {
+                    throw (BundleException) rethrow;
+                }
+                else if ((System.getSecurityManager() != null) &&
+                    (rethrow instanceof java.security.PrivilegedActionException))
+                {
+                    rethrow = ((java.security.PrivilegedActionException) rethrow).getException();
+                }
+
+                // Rethrow all other exceptions as a BundleException.
+                throw new BundleException(
+                    "Activator stop error in bundle " + bundle + ".", rethrow);
+            }
+        }
+        finally
+        {
+            // Always release bundle lock.
+            releaseBundleLock(bundle);
+        }
+
+        // If there was no exception, then we should fire the STOPPED event
+        // here without holding the lock.
+        fireBundleEvent(BundleEvent.STOPPED, bundle);
+    }
+
+    void uninstallBundle(BundleImpl bundle) throws BundleException
+    {
+        // Acquire bundle lock.
+        try
+        {
+            acquireBundleLock(bundle,
+                Bundle.INSTALLED | Bundle.RESOLVED | Bundle.STARTING | Bundle.ACTIVE | Bundle.STOPPING);
+        }
+        catch (IllegalStateException ex)
+        {
+            if (bundle.getState() == Bundle.UNINSTALLED)
+            {
+                throw new IllegalStateException("Cannot uninstall an uninstalled bundle.");
+            }
+            else
+            {
+                throw new BundleException(
+                    "Bundle " + bundle
+                    + " cannot be uninstalled since it is stopping.");
+            }
+        }
+
+        try
+        {
+            // The spec says that uninstall should always succeed, so
+            // catch an exception here if stop() doesn't succeed and
+            // rethrow it at the end.
+            if (!bundle.isExtension() && (bundle.getState() == Bundle.ACTIVE))
+            {
+                try
+                {
+                    stopBundle(bundle, true);
+                }
+                catch (BundleException ex)
+                {
+                    fireFrameworkEvent(FrameworkEvent.ERROR, bundle, ex);
+                }
+            }
+
+            // Remove the bundle from the installed map.
+            BundleImpl target = null;
+            // Acquire global lock.
+            boolean locked = acquireGlobalLock();
+            if (!locked)
+            {
+                // If the calling thread holds bundle locks, then we might not
+                // be able to get the global lock.
+                throw new IllegalStateException(
+                    "Unable to acquire global lock to remove bundle.");
+            }
+            try
+            {
+                // Use a copy-on-write approach to remove the bundle
+                // from the installed maps.
+                Map[] maps = new Map[] {
+                    new HashMap<String, BundleImpl>(m_installedBundles[LOCATION_MAP_IDX]),
+                    new TreeMap<Long, BundleImpl>(m_installedBundles[IDENTIFIER_MAP_IDX])
+                };
+                target = (BundleImpl) maps[LOCATION_MAP_IDX].remove(bundle._getLocation());
+                if (target != null)
+                {
+                    maps[IDENTIFIER_MAP_IDX].remove(new Long(target.getBundleId()));
+                    m_installedBundles = maps;
+
+                    // Set the bundle's persistent state to uninstalled.
+                    bundle.setPersistentStateUninstalled();
+
+                    // Put bundle in uninstalled bundle array.
+                    rememberUninstalledBundle(bundle);
+                }
+            }
+            finally
+            {
+                releaseGlobalLock();
+            }
+
+            if (target == null)
+            {
+                m_logger.log(bundle,
+                    Logger.LOG_ERROR, "Unable to remove bundle from installed map!");
+            }
+
+            setBundleStateAndNotify(bundle, Bundle.INSTALLED);
+
+            // Unfortunately, fire UNRESOLVED event while holding the lock,
+            // since we still need to change the bundle state.
+            fireBundleEvent(BundleEvent.UNRESOLVED, bundle);
+
+            // Set state to uninstalled.
+            setBundleStateAndNotify(bundle, Bundle.UNINSTALLED);
+            bundle.setLastModified(System.currentTimeMillis());
+        }
+        finally
+        {
+            // Always release bundle lock.
+            releaseBundleLock(bundle);
+        }
+
+        // Fire UNINSTALLED event without holding the lock.
+        fireBundleEvent(BundleEvent.UNINSTALLED, bundle);
+
+        // Acquire global lock to check if we should auto-refresh.
+        boolean locked = acquireGlobalLock();
+        if (locked)
+        {
+            try
+            {
+                // If the bundle is not used by anyone, then garbage
+                // collect it now.
+                if (!m_dependencies.hasDependents(bundle))
+                {
+                    try
+                    {
+                        List<Bundle> list = Collections.singletonList((Bundle) bundle);
+                        refreshPackages(list, null);
+                    }
+                    catch (Exception ex)
+                    {
+                        m_logger.log(bundle,
+                            Logger.LOG_ERROR,
+                            "Unable to immediately garbage collect the bundle.", ex);
+                    }
+                }
+            }
+            finally
+            {
+                // Always release the global lock.
+                releaseGlobalLock();
+            }
+        }
+    }
+
+    //
+    // Implementation of BundleContext interface methods.
+    //
+
+    /**
+     * Implementation for BundleContext.getProperty(). Returns
+     * environment property associated with the framework.
+     *
+     * @param key The name of the property to retrieve.
+     * @return The value of the specified property or null.
+    **/
+    String getProperty(String key)
+    {
+        // First, check the config properties.
+        String val = (String) m_configMap.get(key);
+        // If not found, then try the system properties.
+        return (val == null) ? System.getProperty(key) : val;
+    }
+
+    private Bundle reloadBundle(BundleArchive ba)
+        throws BundleException
+    {
+        BundleImpl bundle = null;
+
+        // Try to purge old revisions before installing;
+        // this is done just in case a "refresh" didn't
+        // occur last session...this would only be due to
+        // an error or system crash.
+        try
+        {
+            if (ba.isRemovalPending())
+            {
+                ba.purge();
+            }
+        }
+        catch (Exception ex)
+        {
+            m_logger.log(
+                Logger.LOG_ERROR,
+                "Could not purge bundle.", ex);
+        }
+
+        try
+        {
+            // Acquire the global lock to create the bundle,
+            // since this impacts the global state.
+            boolean locked = acquireGlobalLock();
+            if (!locked)
+            {
+                throw new BundleException(
+                    "Unable to acquire the global lock to install the bundle.");
+            }
+            try
+            {
+                bundle = new BundleImpl(this, null, ba);
+
+                // Extensions are handled as a special case.
+                if (bundle.isExtension())
+                {
+                    m_extensionManager.addExtensionBundle(this, bundle);
+                    m_resolver.addRevision(m_extensionManager.getRevision());
+                }
+
+                // Use a copy-on-write approach to add the bundle
+                // to the installed maps.
+                Map[] maps = new Map[] {
+                    new HashMap<String, BundleImpl>(m_installedBundles[LOCATION_MAP_IDX]),
+                    new TreeMap<Long, BundleImpl>(m_installedBundles[IDENTIFIER_MAP_IDX])
+                };
+                maps[LOCATION_MAP_IDX].put(bundle._getLocation(), bundle);
+                maps[IDENTIFIER_MAP_IDX].put(new Long(bundle.getBundleId()), bundle);
+                m_installedBundles = maps;
+            }
+            finally
+            {
+                // Always release the global lock.
+                releaseGlobalLock();
+            }
+        }
+        catch (Throwable ex)
+        {
+            if (ex instanceof BundleException)
+            {
+                throw (BundleException) ex;
+            }
+            else if (ex instanceof AccessControlException)
+            {
+                throw (AccessControlException) ex;
+            }
+            else
+            {
+                throw new BundleException("Could not create bundle object.", ex);
+            }
+        }
+
+        if (bundle.isExtension())
+        {
+            m_extensionManager.startExtensionBundle(this, bundle);
+        }
+
+        return bundle;
+    }
+
+    Bundle installBundle(
+        Bundle origin, String location, InputStream is)
+        throws BundleException
+    {
+        BundleArchive ba = null;
+        BundleImpl existing, bundle = null;
+
+        // Acquire an install lock.
+        acquireInstallLock(location);
+
+        try
+        {
+            // Check to see if the framework is still running;
+            if ((getState() == Bundle.STOPPING) ||
+                (getState() == Bundle.UNINSTALLED))
+            {
+                throw new BundleException("The framework has been shutdown.");
+            }
+
+            // If bundle location is already installed, then
+            // return it as required by the OSGi specification.
+            existing = (BundleImpl) getBundle(location);
+            if (existing == null)
+            {
+                // First generate an identifier for it.
+                long id = getNextId();
+
+                try
+                {
+                    // Add the bundle to the cache.
+                    ba = m_cache.create(id, getInitialBundleStartLevel(), location, is);
+                }
+                catch (Exception ex)
+                {
+                    throw new BundleException(
+                        "Unable to cache bundle: " + location, ex);
+                }
+                finally
+                {
+                    try
+                    {
+                        if (is != null) is.close();
+                    }
+                    catch (IOException ex)
+                    {
+                        m_logger.log(
+                            Logger.LOG_ERROR,
+                            "Unable to close input stream.", ex);
+                    }
+                }
+
+                try
+                {
+                    // Acquire the global lock to create the bundle,
+                    // since this impacts the global state.
+                    boolean locked = acquireGlobalLock();
+                    if (!locked)
+                    {
+                        throw new BundleException(
+                            "Unable to acquire the global lock to install the bundle.");
+                    }
+                    try
+                    {
+                        bundle = new BundleImpl(this, origin, ba);
+                    }
+                    finally
+                    {
+                        // Always release the global lock.
+                        releaseGlobalLock();
+                    }
+
+                    if (!bundle.isExtension())
+                    {
+                        Object sm = System.getSecurityManager();
+                        if (sm != null)
+                        {
+                            ((SecurityManager) sm).checkPermission(
+                                new AdminPermission(bundle, AdminPermission.LIFECYCLE));
+                        }
+                    }
+                    else
+                    {
+                        m_extensionManager.addExtensionBundle(this, bundle);
+                        m_resolver.addRevision(m_extensionManager.getRevision());
+                    }
+                }
+                catch (Throwable ex)
+                {
+                    // Remove bundle from the cache.
+                    try
+                    {
+                        if (bundle != null)
+                        {
+                            bundle.closeAndDelete();
+                        }
+                        else if (ba != null)
+                        {
+                            ba.closeAndDelete();
+                        }
+                    }
+                    catch (Exception ex1)
+                    {
+                        m_logger.log(bundle,
+                            Logger.LOG_ERROR,
+                            "Could not remove from cache.", ex1);
+                    }
+                    if (ex instanceof BundleException)
+                    {
+                        throw (BundleException) ex;
+                    }
+                    else if (ex instanceof AccessControlException)
+                    {
+                        throw (AccessControlException) ex;
+                    }
+                    else
+                    {
+                        throw new BundleException("Could not create bundle object.", ex);
+                    }
+                }
+
+                // Acquire global lock.
+                boolean locked = acquireGlobalLock();
+                if (!locked)
+                {
+                    // If the calling thread holds bundle locks, then we might not
+                    // be able to get the global lock.
+                    throw new IllegalStateException(
+                        "Unable to acquire global lock to add bundle.");
+                }
+                try
+                {
+                    // Use a copy-on-write approach to add the bundle
+                    // to the installed maps.
+                    Map[] maps = new Map[] {
+                        new HashMap<String, BundleImpl>(m_installedBundles[LOCATION_MAP_IDX]),
+                        new TreeMap<Long, BundleImpl>(m_installedBundles[IDENTIFIER_MAP_IDX])
+                    };
+                    maps[LOCATION_MAP_IDX].put(location, bundle);
+                    maps[IDENTIFIER_MAP_IDX].put(new Long(bundle.getBundleId()), bundle);
+                    m_installedBundles = maps;
+                }
+                finally
+                {
+                    releaseGlobalLock();
+                }
+
+                if (bundle.isExtension())
+                {
+                    m_extensionManager.startExtensionBundle(this, bundle);
+                }
+            }
+        }
+        finally
+        {
+            // Always release install lock.
+            releaseInstallLock(location);
+
+            // Always try to close the input stream.
+            try
+            {
+                if (is != null) is.close();
+            }
+            catch (IOException ex)
+            {
+                m_logger.log(bundle,
+                    Logger.LOG_ERROR,
+                    "Unable to close input stream.", ex);
+                // Not much else we can do.
+            }
+        }
+
+        if (existing != null)
+        {
+            Set<ServiceReference<org.osgi.framework.hooks.bundle.FindHook>> hooks =
+                getHooks(org.osgi.framework.hooks.bundle.FindHook.class);
+            if (!hooks.isEmpty())
+            {
+                Collection<Bundle> bundles = new ArrayList<Bundle>(1);
+                bundles.add(existing);
+                bundles = new ShrinkableCollection<Bundle>(bundles);
+                for (ServiceReference<org.osgi.framework.hooks.bundle.FindHook> hook : hooks)
+                {
+                    org.osgi.framework.hooks.bundle.FindHook fh = getService(this, hook);
+                    if (fh != null)
+                    {
+                        try
+                        {
+                            m_secureAction.invokeBundleFindHook(
+                                fh, origin.getBundleContext(), bundles);
+                        }
+                        catch (Throwable th)
+                        {
+                            m_logger.doLog(
+                                hook.getBundle(),
+                                hook,
+                                Logger.LOG_WARNING,
+                                "Problem invoking bundle hook.",
+                                th);
+                        }
+                    }
+                }
+                if (bundles.isEmpty())
+                {
+                    throw new BundleException(
+                        "Bundle installation rejected by hook.",
+                        BundleException.REJECTED_BY_HOOK);
+                }
+            }
+        }
+        else
+        {
+            // Fire bundle event.
+            fireBundleEvent(BundleEvent.INSTALLED, bundle, origin);
+        }
+
+        // Return new bundle.
+        return (existing != null) ? existing : bundle;
+    }
+
+    /**
+     * Retrieves a bundle from its location.
+     *
+     * @param location The location of the bundle to retrieve.
+     * @return The bundle associated with the location or null if there
+     *         is no bundle associated with the location.
+    **/
+    Bundle getBundle(String location)
+    {
+        return (Bundle) m_installedBundles[LOCATION_MAP_IDX].get(location);
+    }
+
+    /**
+     * Implementation for BundleContext.getBundle(). Retrieves a
+     * bundle from its identifier.
+     *
+     * @param id The identifier of the bundle to retrieve.
+     * @return The bundle associated with the identifier or null if there
+     *         is no bundle associated with the identifier.
+    **/
+    Bundle getBundle(BundleContext bc, long id)
+    {
+        BundleImpl bundle = (BundleImpl)
+            m_installedBundles[IDENTIFIER_MAP_IDX].get(new Long(id));
+        if (bundle != null)
+        {
+            List<BundleImpl> uninstalledBundles = m_uninstalledBundles;
+            for (int i = 0;
+                (bundle == null)
+                    && (uninstalledBundles != null)
+                    && (i < uninstalledBundles.size());
+                i++)
+            {
+                if (uninstalledBundles.get(i).getBundleId() == id)
+                {
+                    bundle = uninstalledBundles.get(i);
+                }
+            }
+        }
+
+        Set<ServiceReference<org.osgi.framework.hooks.bundle.FindHook>> hooks =
+            getHooks(org.osgi.framework.hooks.bundle.FindHook.class);
+        if (!hooks.isEmpty() && (bundle != null))
+        {
+            Collection<Bundle> bundles = new ArrayList<Bundle>(1);
+            bundles.add(bundle);
+            bundles = new ShrinkableCollection<Bundle>(bundles);
+            for (ServiceReference<org.osgi.framework.hooks.bundle.FindHook> hook : hooks)
+            {
+                org.osgi.framework.hooks.bundle.FindHook fh = getService(this, hook);
+                if (fh != null)
+                {
+                    try
+                    {
+                        m_secureAction.invokeBundleFindHook(fh, bc, bundles);
+                    }
+                    catch (Throwable th)
+                    {
+                        m_logger.doLog(
+                            hook.getBundle(),
+                            hook,
+                            Logger.LOG_WARNING,
+                            "Problem invoking bundle hook.",
+                            th);
+                    }
+                }
+            }
+            bundle = (bundles.isEmpty()) ? null : bundle;
+        }
+        return bundle;
+    }
+
+    /**
+     * Retrieves a bundle by its identifier and avoids bundles hooks.
+     *
+     * @return The bundle associated with the identifier or null.
+    **/
+    Bundle getBundle(long id)
+    {
+        BundleImpl bundle = (BundleImpl)
+            m_installedBundles[IDENTIFIER_MAP_IDX].get(new Long(id));
+        if (bundle != null)
+        {
+            return bundle;
+        }
+
+        List<BundleImpl> uninstalledBundles = m_uninstalledBundles;
+        for (int i = 0;
+            (uninstalledBundles != null) && (i < uninstalledBundles.size());
+            i++)
+        {
+            if (uninstalledBundles.get(i).getBundleId() == id)
+            {
+                return uninstalledBundles.get(i);
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Implementation for BundleContext.getBundles(). Retrieves
+     * all installed bundles.
+     *
+     * @return An array containing all installed bundles or null if
+     *         there are no installed bundles.
+    **/
+    Bundle[] getBundles(BundleContext bc)
+    {
+        Collection<Bundle> bundles = m_installedBundles[IDENTIFIER_MAP_IDX].values();
+        Set<ServiceReference<org.osgi.framework.hooks.bundle.FindHook>> hooks =
+            getHooks(org.osgi.framework.hooks.bundle.FindHook.class);
+        if (!hooks.isEmpty())
+        {
+            bundles = new ShrinkableCollection<Bundle>(new ArrayList(bundles));
+            for (ServiceReference<org.osgi.framework.hooks.bundle.FindHook> hook : hooks)
+            {
+                org.osgi.framework.hooks.bundle.FindHook fh = getService(this, hook);
+                if (fh != null)
+                {
+                    try
+                    {
+                        m_secureAction.invokeBundleFindHook(fh, bc, bundles);
+                    }
+                    catch (Throwable th)
+                    {
+                        m_logger.doLog(
+                            hook.getBundle(),
+                            hook,
+                            Logger.LOG_WARNING,
+                            "Problem invoking bundle hook.",
+                            th);
+                    }
+                }
+            }
+        }
+        return (Bundle[]) bundles.toArray(new Bundle[bundles.size()]);
+    }
+
+    /**
+     * Retrieves all installed bundles and avoids bundles hooks.
+     *
+     * @return An array containing all installed bundles or null if
+     *         there are no installed bundles.
+    **/
+    Bundle[] getBundles()
+    {
+        Collection<Bundle> bundles = m_installedBundles[IDENTIFIER_MAP_IDX].values();
+        return (Bundle[]) bundles.toArray(new Bundle[bundles.size()]);
+    }
+
+    void addBundleListener(BundleImpl bundle, BundleListener l)
+    {
+        m_dispatcher.addListener(
+            bundle._getBundleContext(), BundleListener.class, l, null);
+    }
+
+    void removeBundleListener(BundleImpl bundle, BundleListener l)
+    {
+        m_dispatcher.removeListener(
+            bundle._getBundleContext(), BundleListener.class, l);
+    }
+
+    /**
+     * Implementation for BundleContext.addServiceListener().
+     * Adds service listener to the listener list so that is
+     * can listen for <code>ServiceEvent</code>s.
+     *
+     * @param bundle The bundle that registered the listener.
+     * @param l The service listener to add to the listener list.
+     * @param f The filter for the listener; may be null.
+    **/
+    void addServiceListener(BundleImpl bundle, ServiceListener l, String f)
+        throws InvalidSyntaxException
+    {
+        Filter oldFilter;
+        Filter newFilter = (f == null) ? null : FrameworkUtil.createFilter(f);
+
+        oldFilter = m_dispatcher.addListener(
+            bundle._getBundleContext(), ServiceListener.class, l, newFilter);
+
+        // Invoke ListenerHook.removed() if filter updated.
+        Set<ServiceReference<org.osgi.framework.hooks.service.ListenerHook>> listenerHooks =
+            m_registry.getHooks(org.osgi.framework.hooks.service.ListenerHook.class);
+        if (oldFilter != null)
+        {
+            final Collection removed = Collections.singleton(
+                new ListenerInfo(bundle, bundle._getBundleContext(),
+                    ServiceListener.class, l, oldFilter, null, true));
+            for (ServiceReference<org.osgi.framework.hooks.service.ListenerHook> sr : listenerHooks)
+            {
+                org.osgi.framework.hooks.service.ListenerHook lh = getService(this, sr);
+                if (lh != null)
+                {
+                    try
+                    {
+                        m_secureAction.invokeServiceListenerHookRemoved(lh, removed);
+                    }
+                    catch (Throwable th)
+                    {
+                        m_logger.log(sr, Logger.LOG_WARNING,
+                            "Problem invoking service registry hook", th);
+                    }
+                    finally
+                    {
+                        m_registry.ungetService(this, sr);
+                    }
+                }
+            }
+        }
+
+        // Invoke the ListenerHook.added() on all hooks.
+        final Collection added = Collections.singleton(
+            new ListenerInfo(bundle, bundle._getBundleContext(),
+                ServiceListener.class, l, newFilter, null, false));
+        for (ServiceReference<org.osgi.framework.hooks.service.ListenerHook> sr : listenerHooks)
+        {
+            org.osgi.framework.hooks.service.ListenerHook lh = getService(this, sr);
+            if (lh != null)
+            {
+                try
+                {
+                    m_secureAction.invokeServiceListenerHookAdded(lh, added);
+                }
+                catch (Throwable th)
+                {
+                    m_logger.log(sr, Logger.LOG_WARNING,
+                        "Problem invoking service registry hook", th);
+                }
+                finally
+                {
+                    m_registry.ungetService(this, sr);
+                }
+            }
+        }
+    }
+
+    /**
+     * Implementation for BundleContext.removeServiceListener().
+     * Removes service listeners from the listener list.
+     *
+     * @param bundle The context bundle of the listener
+     * @param l The service listener to remove from the listener list.
+    **/
+    void removeServiceListener(BundleImpl bundle, ServiceListener l)
+    {
+        org.osgi.framework.hooks.service.ListenerHook.ListenerInfo listener =
+            m_dispatcher.removeListener(
+                bundle._getBundleContext(), ServiceListener.class, l);
+
+        if (listener != null)
+        {
+            // Invoke the ListenerHook.removed() on all hooks.
+            Set<ServiceReference<org.osgi.framework.hooks.service.ListenerHook>> listenerHooks =
+                m_registry.getHooks(org.osgi.framework.hooks.service.ListenerHook.class);
+            Collection removed = Collections.singleton(listener);
+            for (ServiceReference<org.osgi.framework.hooks.service.ListenerHook> sr : listenerHooks)
+            {
+                org.osgi.framework.hooks.service.ListenerHook lh = getService(this, sr);
+                if (lh != null)
+                {
+                    try
+                    {
+                        m_secureAction.invokeServiceListenerHookRemoved(lh, removed);
+                    }
+                    catch (Throwable th)
+                    {
+                        m_logger.log(sr, Logger.LOG_WARNING,
+                            "Problem invoking service registry hook", th);
+                    }
+                    finally
+                    {
+                        m_registry.ungetService(this, sr);
+                    }
+                }
+            }
+        }
+    }
+
+    void addFrameworkListener(BundleImpl bundle, FrameworkListener l)
+    {
+        m_dispatcher.addListener(
+            bundle._getBundleContext(), FrameworkListener.class, l, null);
+    }
+
+    void removeFrameworkListener(BundleImpl bundle, FrameworkListener l)
+    {
+        m_dispatcher.removeListener(
+            bundle._getBundleContext(), FrameworkListener.class, l);
+    }
+
+    /**
+     * Implementation for BundleContext.registerService(). Registers
+     * a service for the specified bundle bundle.
+     *
+     * @param classNames A string array containing the names of the classes
+     *                under which the new service is available.
+     * @param svcObj The service object or <code>ServiceFactory</code>.
+     * @param dict A dictionary of properties that further describe the
+     *             service or null.
+     * @return A <code>ServiceRegistration</code> object or null.
+    **/
+    ServiceRegistration registerService(
+        BundleContextImpl context, String[] classNames, Object svcObj, Dictionary dict)
+    {
+        if (classNames == null)
+        {
+            throw new NullPointerException("Service class names cannot be null.");
+        }
+        else if (svcObj == null)
+        {
+            throw new IllegalArgumentException("Service object cannot be null.");
+        }
+
+        ServiceRegistration reg = null;
+
+        // Check to make sure that the service object is
+        // an instance of all service classes; ignore if
+        // service object is a service factory.
+        if (!(svcObj instanceof ServiceFactory))
+        {
+            for (int i = 0; i < classNames.length; i++)
+            {
+                Class clazz = Util.loadClassUsingClass(svcObj.getClass(), classNames[i], m_secureAction);
+                if (clazz == null)
+                {
+                    throw new IllegalArgumentException(
+                        "Cannot cast service: " + classNames[i]);
+                }
+                else if (!clazz.isAssignableFrom(svcObj.getClass()))
+                {
+                    throw new IllegalArgumentException(
+                        "Service object is not an instance of \""
+                        + classNames[i] + "\".");
+                }
+            }
+        }
+
+        reg = m_registry.registerService(context, classNames, svcObj, dict);
+
+        // Check to see if this a listener hook; if so, then we need
+        // to invoke the callback with all existing service listeners.
+        if (ServiceRegistry.isHook(
+            classNames, org.osgi.framework.hooks.service.ListenerHook.class, svcObj))
+        {
+            org.osgi.framework.hooks.service.ListenerHook lh =
+                (org.osgi.framework.hooks.service.ListenerHook)
+                    getService(this, reg.getReference());
+            if (lh != null)
+            {
+                try
+                {
+                    m_secureAction.invokeServiceListenerHookAdded(
+                        lh, m_dispatcher.getAllServiceListeners());
+                }
+                catch (Throwable th)
+                {
+                    m_logger.log(reg.getReference(), Logger.LOG_WARNING,
+                        "Problem invoking service registry hook", th);
+                }
+                finally
+                {
+                    m_registry.ungetService(this, reg.getReference());
+                }
+            }
+        }
+
+        // Fire service event.
+        fireServiceEvent(new ServiceEvent(ServiceEvent.REGISTERED, reg.getReference()), null);
+
+        return reg;
+    }
+
+    /**
+     * Retrieves an array of {@link ServiceReference} objects based on calling bundle,
+     * service class name, and filter expression.  Optionally checks for isAssignable to
+     * make sure that the service can be cast to the
+     * @param bundle Calling Bundle
+     * @param className Service Classname or <code>null</code> for all
+     * @param expr Filter Criteria or <code>null</code>
+     * @return Array of ServiceReference objects that meet the criteria
+     * @throws InvalidSyntaxException
+     */
+    ServiceReference[] getServiceReferences(
+        final BundleImpl bundle, final String className,
+        final String expr, final boolean checkAssignable)
+        throws InvalidSyntaxException
+    {
+        // Define filter if expression is not null.
+        SimpleFilter filter = null;
+        if (expr != null)
+        {
+            try
+            {
+                filter = SimpleFilter.parse(expr);
+            }
+            catch (Exception ex)
+            {
+                throw new InvalidSyntaxException(ex.getMessage(), expr);
+            }
+        }
+
+        // Ask the service registry for all matching service references.
+        final List refList = m_registry.getServiceReferences(className, filter);
+
+        // Filter on assignable references
+        if (checkAssignable)
+        {
+            for (int refIdx = 0; (refList != null) && (refIdx < refList.size()); refIdx++)
+            {
+                // Get the current service reference.
+                ServiceReference ref = (ServiceReference) refList.get(refIdx);
+
+                // Now check for castability.
+                if (!Util.isServiceAssignable(bundle, ref))
+                {
+                    refList.remove(refIdx);
+                    refIdx--;
+                }
+            }
+        }
+
+        // activate findhooks
+        Set<ServiceReference<org.osgi.framework.hooks.service.FindHook>> findHooks =
+            m_registry.getHooks(org.osgi.framework.hooks.service.FindHook.class);
+        for (ServiceReference<org.osgi.framework.hooks.service.FindHook> sr : findHooks)
+        {
+            org.osgi.framework.hooks.service.FindHook fh = getService(this, sr);
+            if (fh != null)
+            {
+                try
+                {
+                    m_secureAction.invokeServiceFindHook(
+                        fh,
+                        bundle._getBundleContext(),
+                        className,
+                        expr,
+                        !checkAssignable,
+                        new ShrinkableCollection(refList));
+                }
+                catch (Throwable th)
+                {
+                    m_logger.log(sr, Logger.LOG_WARNING,
+                        "Problem invoking service registry hook", th);
+                }
+                finally
+                {
+                    m_registry.ungetService(this, sr);
+                }
+            }
+        }
+
+        if (refList.size() > 0)
+        {
+            return (ServiceReference[]) refList.toArray(new ServiceReference[refList.size()]);
+        }
+
+        return null;
+    }
+
+    /**
+     * Retrieves Array of {@link ServiceReference} objects based on calling bundle, service class name,
+     * optional filter expression, and optionally filters further on the version.
+     * If running under a {@link SecurityManager}, checks that the calling bundle has permissions to
+     * see the service references and removes references that aren't.
+     * @param bundle Calling Bundle
+     * @param className Service Classname or <code>null</code> for all
+     * @param expr Filter Criteria or <code>null</code>
+     * @param checkAssignable <code>true</code> to check for isAssignable, <code>false</code> to return all versions
+     * @return Array of ServiceReference objects that meet the criteria
+     * @throws InvalidSyntaxException
+     */
+    ServiceReference[] getAllowedServiceReferences(
+        BundleImpl bundle, String className, String expr, boolean checkAssignable)
+        throws InvalidSyntaxException
+    {
+        ServiceReference[] refs = getServiceReferences(bundle, className, expr, checkAssignable);
+
+        Object sm = System.getSecurityManager();
+
+        if ((sm == null) || (refs == null))
+        {
+            return refs;
+        }
+
+        List result = new ArrayList();
+
+        for (int i = 0; i < refs.length; i++)
+        {
+            try
+            {
+                ((SecurityManager) sm).checkPermission(new ServicePermission(refs[i], ServicePermission.GET));
+                result.add(refs[i]);
+            }
+            catch (Exception ex)
+            {
+                // Ignore, since we are just testing permission.
+            }
+        }
+
+        if (result.isEmpty())
+        {
+            return null;
+        }
+
+        return (ServiceReference[]) result.toArray(new ServiceReference[result.size()]);
+
+    }
+
+    <S> S getService(Bundle bundle, ServiceReference<S> ref)
+    {
+        try
+        {
+            return (S) m_registry.getService(bundle, ref);
+        }
+        catch (ServiceException ex)
+        {
+            fireFrameworkEvent(FrameworkEvent.ERROR, ref.getBundle(), ex);
+        }
+
+        return null;
+    }
+
+    boolean ungetService(Bundle bundle, ServiceReference ref)
+    {
+        return m_registry.ungetService(bundle, ref);
+    }
+
+    File getDataFile(BundleImpl bundle, String s)
+    {
+        if (bundle.getState() == Bundle.UNINSTALLED)
+        {
+            throw new IllegalStateException("Bundle has been uninstalled");
+        }
+        else if (Util.isFragment(adapt(BundleRevision.class)))
+        {
+            return null;
+        }
+        try
+        {
+            if (bundle == this)
+            {
+                return m_cache.getSystemBundleDataFile(s);
+            }
+
+            return bundle.getArchive().getDataFile(s);
+        }
+        catch (Exception ex)
+        {
+            m_logger.log(bundle, Logger.LOG_ERROR, ex.getMessage());
+            return null;
+        }
+    }
+
+    //
+    // Hook service management methods.
+    //
+
+    boolean isHookBlackListed(ServiceReference sr)
+    {
+        return m_registry.isHookBlackListed(sr);
+    }
+
+    void blackListHook(ServiceReference sr)
+    {
+        m_registry.blackListHook(sr);
+    }
+
+    public <S> Set<ServiceReference<S>> getHooks(Class<S> hookClass)
+    {
+        return m_registry.getHooks(hookClass);
+    }
+
+    //
+    // PackageAdmin related methods.
+    //
+
+    private final Map<Class, Boolean> m_systemBundleClassCache =
+        new WeakHashMap<Class, Boolean>();
+
+    /**
+     * This method returns the bundle associated with the specified class if
+     * the class was loaded from a bundle from this framework instance. If the
+     * class was not loaded from a bundle or was loaded by a bundle in another
+     * framework instance, then <tt>null</tt> is returned.
+     *
+     * @param clazz the class for which to find its associated bundle.
+     * @return the bundle associated with the specified class or <tt>null</tt>
+     *         if the class was not loaded by a bundle or its associated
+     *         bundle belongs to a different framework instance.
+    **/
+    Bundle getBundle(Class clazz)
+    {
+        // If the class comes from bundle class loader, then return
+        // associated bundle if it is from this framework instance.
+        if (clazz.getClassLoader() instanceof BundleReference)
+        {
+            // Only return the bundle if it is from this framework.
+            BundleReference br = (BundleReference) clazz.getClassLoader();
+            return ((br.getBundle() instanceof BundleImpl)
+                && (((BundleImpl) br.getBundle()).getFramework() == this))
+                    ? br.getBundle() : null;
+        }
+        // Otherwise check if the class conceptually comes from the
+        // system bundle. Ignore implicit boot delegation.
+        if (!clazz.getName().startsWith("java."))
+        {
+            Boolean fromSystemBundle;
+            synchronized (m_systemBundleClassCache)
+            {
+                fromSystemBundle = m_systemBundleClassCache.get(clazz);
+            }
+            if (fromSystemBundle == null)
+            {
+                Class sbClass = null;
+                try
+                {
+                    sbClass = ((BundleWiringImpl) m_extensionManager
+                        .getRevision().getWiring()).getClassByDelegation(clazz.getName());
+                }
+                catch (ClassNotFoundException ex)
+                {
+                    // Ignore, treat as false.
+                }
+                synchronized (m_systemBundleClassCache)
+                {
+                    if (sbClass == clazz)
+                    {
+                        fromSystemBundle = Boolean.TRUE;
+                    }
+                    else
+                    {
+                        fromSystemBundle = Boolean.FALSE;
+                    }
+                    m_systemBundleClassCache.put(clazz, fromSystemBundle);
+                }
+            }
+            return fromSystemBundle.booleanValue() ? this : null;
+        }
+        return null;
+    }
+
+    /**
+     * Returns the exported packages associated with the specified
+     * package name. This is used by the PackageAdmin service
+     * implementation.
+     *
+     * @param pkgName The name of the exported package to find.
+     * @return The exported package or null if no matching package was found.
+    **/
+    ExportedPackage[] getExportedPackages(String pkgName)
+    {
+        // First, get all exporters of the package.
+        Map<String, Object> attrs = Collections.singletonMap(
+            BundleRevision.PACKAGE_NAMESPACE, (Object) pkgName);
+        BundleRequirementImpl req = new BundleRequirementImpl(
+            null,
+            BundleRevision.PACKAGE_NAMESPACE,
+            Collections.EMPTY_MAP,
+            attrs);
+        List<BundleCapability> exports = m_resolver.findProviders(req, false);
+
+        // We only want resolved capabilities.
+        for (Iterator<BundleCapability> it = exports.iterator(); it.hasNext(); )
+        {
+            if (it.next().getRevision().getWiring() == null)
+            {
+                it.remove();
+            }
+        }
+
+        if (exports != null)
+        {
+            List pkgs = new ArrayList();
+
+            for (Iterator<BundleCapability> it = exports.iterator(); it.hasNext(); )
+            {
+                // Get the bundle associated with the current exporting revision.
+                Bundle bundle = it.next().getRevision().getBundle();
+
+                // We need to find the version of the exported package, but this
+                // is tricky since there may be multiple versions of the package
+                // offered by a given bundle, since multiple revisions of the
+                // bundle JAR file may exist if the bundle was updated without
+                // refreshing the framework. In this case, each revision of the
+                // bundle JAR file is represented as a revision ordered from
+                // newest to oldest. We assume that the first revision found to
+                // be exporting the package is the provider of the package,
+                // which makes sense since it must have been resolved first.
+                List<BundleRevision> revisions =
+                    bundle.adapt(BundleRevisions.class).getRevisions();
+                for (int i = revisions.size() - 1; i >= 0; i--)
+                {
+                    BundleRevision br = revisions.get(i);
+                    List<BundleCapability> caps = (br.getWiring() == null)
+                        ? br.getDeclaredCapabilities(null)
+                        : br.getWiring().getCapabilities(null);
+                    for (BundleCapability cap : caps)
+                    {
+                        if (cap.getNamespace().equals(req.getNamespace())
+                            && CapabilitySet.matches(cap, req.getFilter()))
+                        {
+                            pkgs.add(
+                                new ExportedPackageImpl(
+                                    this, (BundleImpl) bundle, br, cap));
+                        }
+                    }
+                }
+            }
+
+            return (pkgs.isEmpty())
+                ? null
+                : (ExportedPackage[]) pkgs.toArray(new ExportedPackage[pkgs.size()]);
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns an array of all actively exported packages from the specified
+     * bundle or if the specified bundle is <tt>null</tt> an array
+     * containing all actively exported packages by all bundles.
+     *
+     * @param b The bundle whose exported packages are to be retrieved
+     *        or <tt>null</tt> if the exported packages of all bundles are
+     *        to be retrieved.
+     * @return An array of exported packages.
+    **/
+    ExportedPackage[] getExportedPackages(Bundle b)
+    {
+        List list = new ArrayList();
+
+        // If a bundle is specified, then return its
+        // exported packages.
+        if (b != null)
+        {
+            BundleImpl bundle = (BundleImpl) b;
+            getExportedPackages(bundle, list);
+        }
+        // Otherwise return all exported packages.
+        else
+        {
+            // To create a list of all exported packages, we must look
+            // in the installed and uninstalled sets of bundles.
+            boolean locked = acquireGlobalLock();
+            if (!locked)
+            {
+                // If the calling thread holds bundle locks, then we might not
+                // be able to get the global lock.
+                throw new IllegalStateException(
+                    "Unable to acquire global lock to retrieve exported packages.");
+            }
+            try
+            {
+                // First get exported packages from uninstalled bundles.
+                for (int bundleIdx = 0;
+                    (m_uninstalledBundles != null) && (bundleIdx < m_uninstalledBundles.size());
+                    bundleIdx++)
+                {
+                    BundleImpl bundle = m_uninstalledBundles.get(bundleIdx);
+                    getExportedPackages(bundle, list);
+                }
+
+                // Now get exported packages from installed bundles.
+                Bundle[] bundles = getBundles();
+                for (int bundleIdx = 0; bundleIdx < bundles.length; bundleIdx++)
+                {
+                    BundleImpl bundle = (BundleImpl) bundles[bundleIdx];
+                    getExportedPackages(bundle, list);
+                }
+            }
+            finally
+            {
+                releaseGlobalLock();
+            }
+        }
+
+        return (list.isEmpty())
+            ? null
+            : (ExportedPackage[]) list.toArray(new ExportedPackage[list.size()]);
+    }
+
+    /**
+     * Adds any current active exported packages from the specified bundle
+     * to the passed in list.
+     * @param bundle The bundle from which to retrieve exported packages.
+     * @param list The list to which the exported packages are added
+    **/
+    private void getExportedPackages(Bundle bundle, List list)
+    {
+        // Since a bundle may have many revisions associated with it,
+        // one for each revision in the cache, search each revision
+        // to get all exports.
+        for (BundleRevision br : bundle.adapt(BundleRevisions.class).getRevisions())
+        {
+            List<BundleCapability> caps = (br.getWiring() == null)
+                ? br.getDeclaredCapabilities(null)
+                : br.getWiring().getCapabilities(null);
+            if ((caps != null) && (caps.size() > 0))
+            {
+                for (BundleCapability cap : caps)
+                {
+                    if (cap.getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE))
+                    {
+                        String pkgName = (String)
+                            cap.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE);
+                        list.add(new ExportedPackageImpl(
+                            this, (BundleImpl) bundle, br, cap));
+                    }
+                }
+            }
+        }
+    }
+
+    // Needed by ExportedPackageImpl.
+    Set<Bundle> getImportingBundles(BundleImpl exporter, BundleCapability cap)
+    {
+        return m_dependencies.getImportingBundles(exporter, cap);
+    }
+
+    // Needed by RequiredBundleImpl.
+    Set<Bundle> getRequiringBundles(BundleImpl bundle)
+    {
+        return m_dependencies.getRequiringBundles(bundle);
+    }
+
+    boolean resolveBundles(Collection<Bundle> targets)
+    {
+        // Acquire global lock.
+        boolean locked = acquireGlobalLock();
+        if (!locked)
+        {
+            m_logger.log(
+                Logger.LOG_WARNING,
+                "Unable to acquire global lock to perform resolve.",
+                null);
+            return false;
+        }
+
+        try
+        {
+            // Remember original targets.
+            Collection<Bundle> originalTargets = targets;
+
+            // Determine set of bundles to be resolved, which is either the
+            // specified bundles or all bundles if null.
+            if (targets == null)
+            {
+                // Add all bundles to the list.
+                targets = m_installedBundles[LOCATION_MAP_IDX].values();
+            }
+
+            // Now resolve each target bundle.
+            boolean result = true;
+
+            // If there are targets, then resolve each one.
+            if (!targets.isEmpty())
+            {
+                // Get bundle revisions for bundles in INSTALLED state.
+                Set<BundleRevision> revisions =
+                    new HashSet<BundleRevision>(targets.size());
+                for (Bundle b : targets)
+                {
+                    if (b.getState() != Bundle.UNINSTALLED)
+                    {
+                        revisions.add(b.adapt(BundleRevision.class));
+                    }
+                }
+                // If we had to filter any of the original targets, then
+                // the return result will be false regardless.
+                if ((originalTargets != null) && (originalTargets.size() != revisions.size()))
+                {
+                    result = false;
+                }
+                try
+                {
+                    m_resolver.resolve(Collections.EMPTY_SET, revisions);
+                    if (result)
+                    {
+                        for (BundleRevision br : revisions)
+                        {
+                            if (br.getWiring() == null)
+                            {
+                                result = false;
+                                break;
+                            }
+                        }
+                    }
+                }
+                catch (ResolveException ex)
+                {
+                    result = false;
+                }
+                catch (BundleException ex)
+                {
+                    result = false;
+                }
+            }
+
+            return result;
+        }
+        finally
+        {
+            // Always release the global lock.
+            releaseGlobalLock();
+        }
+    }
+
+    private void resolveBundleRevision(BundleRevision revision) throws BundleException
+    {
+        try
+        {
+            m_resolver.resolve(Collections.singleton(revision), Collections.EMPTY_SET);
+        }
+        catch (ResolveException ex)
+        {
+            if (ex.getRevision() != null)
+            {
+                Bundle b = ex.getRevision().getBundle();
+                throw new BundleException(
+                    "Unresolved constraint in bundle "
+                    + b + ": " + ex.getMessage(), BundleException.RESOLVE_ERROR);
+            }
+            else
+            {
+                throw new BundleException(ex.getMessage(), BundleException.RESOLVE_ERROR);
+            }
+        }
+    }
+
+    void refreshPackages(Collection<Bundle> targets, FrameworkListener[] listeners)
+    {
+        // Acquire global lock.
+        boolean locked = acquireGlobalLock();
+        if (!locked)
+        {
+            // If the thread calling holds bundle locks, then we might not
+            // be able to get the global lock. However, in practice this
+            // should not happen since the calls to this method have either
+            // already acquired the global lock or it is PackageAdmin which
+            // doesn't hold bundle locks.
+            throw new IllegalStateException(
+                "Unable to acquire global lock for refresh.");
+        }
+
+        // Determine set of bundles to refresh, which is all transitive
+        // dependencies of specified set or all transitive dependencies
+        // of all bundles if null is specified.
+        Collection<Bundle> newTargets = targets;
+        if (newTargets == null)
+        {
+            List<Bundle> list = new ArrayList<Bundle>();
+
+            // First add all uninstalled bundles.
+            for (int i = 0;
+                (m_uninstalledBundles != null) && (i < m_uninstalledBundles.size());
+                i++)
+            {
+                list.add(m_uninstalledBundles.get(i));
+            }
+
+            // Then add all updated bundles.
+            Iterator iter = m_installedBundles[LOCATION_MAP_IDX].values().iterator();
+            while (iter.hasNext())
+            {
+                BundleImpl bundle = (BundleImpl) iter.next();
+                if (bundle.isRemovalPending())
+                {
+                    list.add(bundle);
+                }
+            }
+
+            if (!list.isEmpty())
+            {
+                newTargets = list;
+            }
+        }
+
+        // If there are targets, then find all dependencies for each one.
+        Set<Bundle> bundles = null;
+        if (newTargets != null)
+        {
+            // Create map of bundles that import the packages
+            // from the target bundles.
+            bundles = new HashSet<Bundle>();
+            for (Bundle target : newTargets)
+            {
+                // If anyone passes in a null bundle, then just
+                // ignore it.
+                if (target != null)
+                {
+                    // Add the current target bundle to the map of
+                    // bundles to be refreshed.
+                    bundles.add(target);
+                    // Add all importing bundles to map.
+                    populateDependentGraph((BundleImpl) target, bundles);
+                }
+            }
+        }
+
+        // Now refresh each bundle.
+        try
+        {
+            boolean restart = false;
+
+            Bundle systemBundle = this;
+
+            // We need to restart the framework if either an extension bundle is
+            // refreshed or the system bundle is refreshed and any extension bundle
+            // has been updated or uninstalled.
+            if (bundles != null)
+            {
+                for (Bundle b : bundles)
+                {
+                    if ((systemBundle == b) || ((BundleImpl) b).isExtension())
+                    {
+                        restart = true;
+                        break;
+                    }
+                }
+
+                // If we need to restart the framework, then no reason to
+                // do a refresh.
+                if (!restart)
+                {
+                    // Now we actually need to refresh the affected bundles.
+                    // At this point the collection contains every bundle that has
+                    // been updated and/or removed as well as all bundles that import
+                    // packages from these bundles.
+
+                    // Create refresh helpers for each bundle.
+                    List<RefreshHelper> helpers = new ArrayList<RefreshHelper>(bundles.size());
+                    for (Bundle b : bundles)
+                    {
+                        // Remove any targeted bundles from the uninstalled bundles
+                        // array, since they will be removed from the system after
+                        // the refresh.
+                        // TODO: FRAMEWORK - Is this correct?
+                        forgetUninstalledBundle((BundleImpl) b);
+
+                        // Create refresh helper for bundle.
+                        helpers.add(new RefreshHelper(b));
+                    }
+
+                    // Stop all refreshing bundles.
+                    for (RefreshHelper helper : helpers)
+                    {
+                        if (helper != null)
+                        {
+                            helper.stop();
+                        }
+                    }
+
+                    // Refresh or remove all refreshing bundles first.
+                    for (RefreshHelper helper : helpers)
+                    {
+                        if (helper != null)
+                        {
+                            helper.refreshOrRemove();
+                        }
+                    }
+
+                    // Restart all refreshed bundles that were previously running.
+                    for (RefreshHelper helper : helpers)
+                    {
+                        if (helper != null)
+                        {
+                            helper.restart();
+                        }
+                    }
+                }
+                else
+                {
+                    try
+                    {
+                        update();
+                    }
+                    catch (BundleException ex)
+                    {
+                        m_logger.log(Logger.LOG_ERROR, "Framework restart error.", ex);
+                    }
+                }
+            }
+        }
+        finally
+        {
+            // Always release the global lock.
+            releaseGlobalLock();
+        }
+
+        fireFrameworkEvent(FrameworkEvent.PACKAGES_REFRESHED, this, null);
+
+        if (listeners != null)
+        {
+            FrameworkEvent event = new FrameworkEvent(
+                FrameworkEvent.PACKAGES_REFRESHED, this, null);
+            for (FrameworkListener l : listeners)
+            {
+                try
+                {
+                    l.frameworkEvent(event);
+                }
+                catch (Throwable th)
+                {
+                    m_logger.log(Logger.LOG_ERROR,
+                        "Framework listener delivery error.", th);
+                }
+            }
+        }
+    }
+
+    Collection<Bundle> getDependencyClosure(Collection<Bundle> targets)
+    {
+        // Acquire global lock.
+        boolean locked = acquireGlobalLock();
+        if (!locked)
+        {
+            // If the thread calling holds bundle locks, then we might not
+            // be able to get the global lock. However, in practice this
+            // should not happen since the calls to this method have either
+            // already acquired the global lock or it is PackageAdmin which
+            // doesn't hold bundle locks.
+            throw new IllegalStateException(
+                "Unable to acquire global lock for refresh.");
+        }
+
+        try
+        {
+            // If there are targets, then find all dependencies for each one.
+            Set<Bundle> bundles = Collections.EMPTY_SET;
+            if (targets != null)
+            {
+                // Create map of bundles that import the packages
+                // from the target bundles.
+                bundles = new HashSet<Bundle>();
+                for (Bundle target : targets)
+                {
+                    // Add the current target bundle to the map of
+                    // bundles to be refreshed.
+                    bundles.add(target);
+                    // Add all importing bundles to map.
+                    populateDependentGraph((BundleImpl) target, bundles);
+                }
+            }
+            return bundles;
+        }
+        finally
+        {
+            // Always release the global lock.
+            releaseGlobalLock();
+        }
+    }
+
+    // Calls to this method must have the global lock.
+    private void populateDependentGraph(BundleImpl exporter, Set<Bundle> set)
+    {
+        // Get all dependent bundles of this bundle.
+        Set<Bundle> dependents = m_dependencies.getDependentBundles(exporter);
+
+        if (dependents != null)
+        {
+            for (Bundle b : dependents)
+            {
+                // Avoid cycles if the bundle is already in set.
+                if (!set.contains(b))
+                {
+                    // Add each dependent bundle to set.
+                    set.add(b);
+                    // Now recurse into each bundle to get its dependents.
+                    populateDependentGraph((BundleImpl) b, set);
+                }
+            }
+        }
+    }
+
+    Collection<Bundle> getRemovalPendingBundles()
+    {
+        // Acquire global lock.
+        boolean locked = acquireGlobalLock();
+        if (!locked)
+        {
+            // If the thread calling holds bundle locks, then we might not
+            // be able to get the global lock. However, in practice this
+            // should not happen since the calls to this method have either
+            // already acquired the global lock or it is PackageAdmin which
+            // doesn't hold bundle locks.
+            throw new IllegalStateException(
+                "Unable to acquire global lock for refresh.");
+        }
+
+        try
+        {
+            List<Bundle> bundles = new ArrayList<Bundle>();
+            if (m_uninstalledBundles != null)
+            {
+                for (Bundle b : m_uninstalledBundles)
+                {
+                    bundles.add(b);
+                }
+            }
+            for (Bundle b : getBundles())
+            {
+                if (((BundleImpl) b).isRemovalPending())
+                {
+                    bundles.add(b);
+                }
+            }
+            return bundles;
+        }
+        finally
+        {
+            // Always release the global lock.
+            releaseGlobalLock();
+        }
+    }
+
+    //
+    // Miscellaneous private methods.
+    //
+
+    private volatile SecurityProvider m_securityProvider;
+
+    SecurityProvider getSecurityProvider()
+    {
+        return m_securityProvider;
+    }
+
+    void setSecurityProvider(SecurityProvider securityProvider)
+    {
+        m_securityProvider = securityProvider;
+    }
+
+    Object getSignerMatcher(BundleImpl bundle, int signersType)
+    {
+        if ((bundle != this) && (m_securityProvider != null))
+        {
+            return m_securityProvider.getSignerMatcher(bundle, signersType);
+        }
+        return new HashMap();
+    }
+
+    boolean impliesBundlePermission(BundleProtectionDomain bundleProtectionDomain, Permission permission, boolean direct)
+    {
+        if (m_securityProvider != null)
+        {
+            return m_securityProvider.hasBundlePermission(bundleProtectionDomain, permission, direct);
+        }
+        else
+        {
+            Bundle source = bundleProtectionDomain.getBundle();
+
+            return (m_securityDefaultPolicy && (source == null || source.getBundleId() != 0)) ?
+                bundleProtectionDomain.superImplies(permission) : true;
+        }
+    }
+
+    private BundleActivator createBundleActivator(Bundle impl)
+        throws Exception
+    {
+        // CONCURRENCY NOTE:
+        // This method is called indirectly from startBundle() (via _startBundle()),
+        // which has the bundle lock, so there is no need to do any locking here.
+
+        // Get the activator class from the header map.
+        BundleActivator activator = null;
+        Map headerMap = ((BundleRevisionImpl) impl.adapt(BundleRevision.class)).getHeaders();
+        String className = (String) headerMap.get(Constants.BUNDLE_ACTIVATOR);
+        // Try to instantiate activator class if present.
+        if (className != null)
+        {
+            className = className.trim();
+            Class clazz;
+            try
+            {
+                clazz = ((BundleWiringImpl)
+                    impl.adapt(BundleRevision.class).getWiring()).getClassByDelegation(className);
+            }
+            catch (ClassNotFoundException ex)
+            {
+                throw new BundleException("Not found: " + className, ex);
+            }
+            activator = (BundleActivator) clazz.newInstance();
+        }
+
+        return activator;
+    }
+
+    private void refreshBundle(BundleImpl bundle) throws Exception
+    {
+        // Acquire bundle lock.
+        try
+        {
+            acquireBundleLock(bundle, Bundle.INSTALLED | Bundle.RESOLVED);
+        }
+        catch (IllegalStateException ex)
+        {
+            throw new BundleException(
+                "Bundle state has changed unexpectedly during refresh.");
+        }
+
+        try
+        {
+            // See if we need to fire UNRESOLVED event.
+            boolean fire = (bundle.getState() != Bundle.INSTALLED);
+            // Remove dependencies.
+            m_dependencies.removeDependencies(bundle);
+            // Reset the bundle object.
+            ((BundleImpl) bundle).refresh();
+            // Fire UNRESOLVED event if necessary
+            // and notify state change..
+            if (fire)
+            {
+                setBundleStateAndNotify(bundle, Bundle.INSTALLED);
+                fireBundleEvent(BundleEvent.UNRESOLVED, bundle);
+            }
+        }
+        catch (Exception ex)
+        {
+            fireFrameworkEvent(FrameworkEvent.ERROR, bundle, ex);
+        }
+        finally
+        {
+            // Always release the bundle lock.
+            releaseBundleLock(bundle);
+        }
+    }
+
+    //
+    // Event-related methods.
+    //
+
+    /**
+     * Fires bundle events.
+    **/
+    void fireFrameworkEvent(
+        int type, Bundle bundle, Throwable throwable)
+    {
+        m_dispatcher.fireFrameworkEvent(new FrameworkEvent(type, bundle, throwable));
+    }
+
+    /**
+     * Fires bundle events.
+     *
+     * @param type The type of bundle event to fire.
+     * @param bundle The bundle associated with the event.
+    **/
+    void fireBundleEvent(int type, Bundle bundle)
+    {
+        m_dispatcher.fireBundleEvent(new BundleEvent(type, bundle), this);
+    }
+
+    void fireBundleEvent(int type, Bundle bundle, Bundle origin)
+    {
+        m_dispatcher.fireBundleEvent(new BundleEvent(type, bundle, origin), this);
+    }
+
+    /**
+     * Fires service events.
+     *
+     * @param event The service event to fire.
+     * @param reg The service registration associated with the service object.
+    **/
+    private void fireServiceEvent(ServiceEvent event, Dictionary oldProps)
+    {
+        m_dispatcher.fireServiceEvent(event, oldProps, this);
+    }
+
+    //
+    // Property related methods.
+    //
+
+    private void initializeFrameworkProperties()
+    {
+        // Standard OSGi properties.
+        m_configMutableMap.put(
+            FelixConstants.FRAMEWORK_VERSION,
+            FelixConstants.FRAMEWORK_VERSION_VALUE);
+        m_configMutableMap.put(
+            FelixConstants.FRAMEWORK_VENDOR,
+            FelixConstants.FRAMEWORK_VENDOR_VALUE);
+        m_configMutableMap.put(
+            FelixConstants.FRAMEWORK_LANGUAGE,
+            System.getProperty("user.language"));
+        m_configMutableMap.put(
+            FelixConstants.FRAMEWORK_OS_VERSION,
+            System.getProperty("os.version"));
+        m_configMutableMap.put(
+            FelixConstants.SUPPORTS_FRAMEWORK_EXTENSION,
+            "true");
+        m_configMutableMap.put(
+            FelixConstants.SUPPORTS_FRAMEWORK_FRAGMENT,
+            "true");
+        m_configMutableMap.put(
+            FelixConstants.SUPPORTS_FRAMEWORK_REQUIREBUNDLE,
+            "true");
+        m_configMutableMap.put(
+            FelixConstants.SUPPORTS_BOOTCLASSPATH_EXTENSION,
+            "false");
+
+        String s = null;
+        s = R4LibraryClause.normalizeOSName(System.getProperty("os.name"));
+        m_configMutableMap.put(FelixConstants.FRAMEWORK_OS_NAME, s);
+        s = R4LibraryClause.normalizeProcessor(System.getProperty("os.arch"));
+        m_configMutableMap.put(FelixConstants.FRAMEWORK_PROCESSOR, s);
+        m_configMutableMap.put(
+            FelixConstants.FELIX_VERSION_PROPERTY, getFrameworkVersion());
+
+        // Set supported execution environments to default value,
+        // if not explicitly configured.
+        if (!getConfig().containsKey(Constants.FRAMEWORK_EXECUTIONENVIRONMENT))
+        {
+            s = Util.getDefaultProperty(
+                m_logger, Constants.FRAMEWORK_EXECUTIONENVIRONMENT);
+            if (s != null)
+            {
+                m_configMutableMap.put(
+                    Constants.FRAMEWORK_EXECUTIONENVIRONMENT, s);
+            }
+        }
+    }
+
+    /**
+     * Read the framework version from the property file.
+     * @return the framework version as a string.
+    **/
+    private static String getFrameworkVersion()
+    {
+        // The framework version property.
+        Properties props = new Properties();
+        InputStream in = Felix.class.getResourceAsStream("Felix.properties");
+        if (in != null)
+        {
+            try
+            {
+                props.load(in);
+            }
+            catch (IOException ex)
+            {
+                ex.printStackTrace();
+            }
+            finally
+            {
+                try
+                {
+                    in.close();
+                }
+                catch (IOException ex)
+                {
+                    // Not much we can do.
+                }
+            }
+        }
+
+        // Maven uses a '-' to separate the version qualifier,
+        // while OSGi uses a '.', so we need to convert to a '.'
+        StringBuffer sb =
+            new StringBuffer(
+                props.getProperty(
+                    FelixConstants.FELIX_VERSION_PROPERTY, "0.0.0"));
+        if (sb.toString().indexOf("-") >= 0)
+        {
+            sb.setCharAt(sb.toString().indexOf("-"), '.');
+        }
+        String toRet = sb.toString();
+        if (toRet.indexOf("${pom") >= 0)
+        {
+            return "0.0.0";
+        }
+        else
+        {
+            return toRet;
+        }
+    }
+
+    //
+    // Private utility methods.
+    //
+
+    /**
+     * Generated the next valid bundle identifier.
+    **/
+    private long loadNextId()
+    {
+        synchronized (m_nextIdLock)
+        {
+            // Read persisted next bundle identifier.
+            InputStream is = null;
+            BufferedReader br = null;
+            try
+            {
+                File file = m_cache.getSystemBundleDataFile("bundle.id");
+                is = m_secureAction.getFileInputStream(file);
+                br = new BufferedReader(new InputStreamReader(is));
+                return Long.parseLong(br.readLine());
+            }
+            catch (FileNotFoundException ex)
+            {
+                // Ignore this case because we assume that this is the
+                // initial startup of the framework and therefore the
+                // file does not exist yet.
+            }
+            catch (Exception ex)
+            {
+                m_logger.log(
+                    Logger.LOG_WARNING,
+                    "Unable to initialize next bundle identifier from persistent storage.",
+                    ex);
+            }
+            finally
+            {
+                try
+                {
+                    if (br != null) br.close();
+                    if (is != null) is.close();
+                }
+                catch (Exception ex)
+                {
+                    m_logger.log(
+                        Logger.LOG_WARNING,
+                        "Unable to close next bundle identifier file.",
+                        ex);
+                }
+            }
+        }
+
+        return -1;
+    }
+
+    private long getNextId()
+    {
+        synchronized (m_nextIdLock)
+        {
+            // Save the current id.
+            long id = m_nextId;
+
+            // Increment the next id.
+            m_nextId++;
+
+            // Write the bundle state.
+            OutputStream os = null;
+            BufferedWriter bw = null;
+            try
+            {
+                File file = m_cache.getSystemBundleDataFile("bundle.id");
+                os = m_secureAction.getFileOutputStream(file);
+                bw = new BufferedWriter(new OutputStreamWriter(os));
+                String s = Long.toString(m_nextId);
+                bw.write(s, 0, s.length());
+            }
+            catch (Exception ex)
+            {
+                m_logger.log(
+                    Logger.LOG_WARNING,
+                    "Unable to save next bundle identifier to persistent storage.",
+                    ex);
+            }
+            finally
+            {
+                try
+                {
+                    if (bw != null) bw.close();
+                    if (os != null) os.close();
+                }
+                catch (Exception ex)
+                {
+                    m_logger.log(
+                        Logger.LOG_WARNING,
+                        "Unable to close next bundle identifier file.",
+                        ex);
+                }
+            }
+
+            return id;
+        }
+    }
+
+    //
+    // Miscellaneous inner classes.
+    //
+
+    class SystemBundleActivator implements BundleActivator
+    {
+        public void start(BundleContext context) throws Exception
+        {
+            // Add the bundle activator for the url handler service.
+            m_activatorList.add(0, new URLHandlersActivator(m_configMap, Felix.this));
+
+            // Start all activators.
+            for (int i = 0; i < m_activatorList.size(); i++)
+            {
+                Felix.m_secureAction.startActivator(
+                    (BundleActivator) m_activatorList.get(i), context);
+            }
+        }
+
+        public void stop(BundleContext context)
+        {
+            // The state of the framework should be STOPPING, so
+            // acquire the bundle lock to verify it.
+            acquireBundleLock(Felix.this, Bundle.STOPPING);
+            releaseBundleLock(Felix.this);
+
+            // Use the start level service to set the start level to zero
+            // in order to stop all bundles in the framework. Since framework
+            // shutdown happens on its own thread, we can wait for the start
+            // level service to finish before proceeding by calling the
+            // non-spec setStartLevelAndWait() method.
+            m_fwkStartLevel.setStartLevelAndWait(0);
+
+            // Stop framework wiring thread.
+            m_fwkWiring.stop();
+            // Stop framework start level thread.
+            m_fwkStartLevel.stop();
+
+            // Shutdown event dispatching queue.
+            m_dispatcher.stopDispatching();
+
+            // Since there may be updated and uninstalled bundles that
+            // have not been refreshed, we will take care of refreshing
+            // them during shutdown.
+
+            // Refresh all updated bundles.
+            Bundle[] bundles = getBundles();
+            for (int i = 0; i < bundles.length; i++)
+            {
+                BundleImpl bundle = (BundleImpl) bundles[i];
+                if (bundle.isRemovalPending())
+                {
+                    try
+                    {
+                        refreshBundle(bundle);
+                    }
+                    catch (Exception ex)
+                    {
+                        fireFrameworkEvent(FrameworkEvent.ERROR, bundle, ex);
+                        m_logger.log(bundle, Logger.LOG_ERROR, "Unable to purge bundle "
+                            + bundle._getLocation(), ex);
+                    }
+                }
+            }
+
+            // Delete uninstalled bundles.
+            for (int i = 0;
+                (m_uninstalledBundles != null) && (i < m_uninstalledBundles.size());
+                i++)
+            {
+                try
+                {
+                    m_uninstalledBundles.get(i).closeAndDelete();
+                }
+                catch (Exception ex)
+                {
+                    m_logger.log(m_uninstalledBundles.get(i),
+                        Logger.LOG_ERROR,
+                        "Unable to remove "
+                        + m_uninstalledBundles.get(i)._getLocation(), ex);
+                }
+            }
+
+            // Dispose of the bundles to close their associated contents.
+            bundles = getBundles();
+            for (int i = 0; i < bundles.length; i++)
+            {
+                ((BundleImpl) bundles[i]).close();
+            }
+
+            // Stop all system bundle activators.
+            for (int i = 0; i < m_activatorList.size(); i++)
+            {
+                try
+                {
+                    Felix.m_secureAction.stopActivator((BundleActivator)
+                        m_activatorList.get(i), _getBundleContext());
+                }
+                catch (Throwable throwable)
+                {
+                    m_logger.log(
+                        Logger.LOG_WARNING,
+                        "Exception stopping a system bundle activator.",
+                        throwable);
+                }
+            }
+
+            if (m_securityManager != null)
+            {
+                System.setSecurityManager(null);
+                m_securityManager = null;
+            }
+
+            m_dependencies.removeDependents(adapt(BundleRevision.class));
+
+            if (m_extensionManager != null)
+            {
+                m_extensionManager.removeExtensions(Felix.this);
+            }
+
+            // Dispose of the bundle cache.
+            m_cache.release();
+            m_cache = null;
+
+            // Set the framework state to resolved.
+            acquireBundleLock(Felix.this, Bundle.STOPPING);
+            try
+            {
+                // Clean up the bundle context.
+                ((BundleContextImpl) _getBundleContext()).invalidate();
+                setBundleContext(null);
+
+                // Set the framework state to resolved and open
+                // the shutdown gate.
+                setBundleStateAndNotify(Felix.this, Bundle.RESOLVED);
+                m_shutdownGate.open();
+                m_shutdownGate = null;
+            }
+            finally
+            {
+                releaseBundleLock(Felix.this);
+            }
+        }
+    }
+
+    /**
+     * Simple class that is used in <tt>refreshPackages()</tt> to embody
+     * the refresh logic in order to keep the code clean. This class is
+     * not static because it needs access to framework event firing methods.
+    **/
+    private class RefreshHelper
+    {
+        private BundleImpl m_bundle = null;
+        private int m_oldState = Bundle.INSTALLED;
+
+        public RefreshHelper(Bundle bundle)
+        {
+            m_bundle = (BundleImpl) bundle;
+        }
+
+        public void stop()
+        {
+            acquireBundleLock(m_bundle,
+                    Bundle.INSTALLED | Bundle.RESOLVED | Bundle.STARTING |
+                    Bundle.ACTIVE | Bundle.STOPPING | Bundle.UNINSTALLED);
+            try
+            {
+                m_oldState = m_bundle.getState();
+                if (m_oldState != Bundle.UNINSTALLED)
+                {
+                    if (!Util.isFragment(m_bundle.adapt(BundleRevision.class)))
+                    {
+                        stopBundle(m_bundle, false);
+                    }
+                }
+            }
+            catch (Throwable ex)
+            {
+                fireFrameworkEvent(FrameworkEvent.ERROR, m_bundle, ex);
+            }
+            finally
+            {
+                releaseBundleLock(m_bundle);
+            }
+        }
+
+        public void refreshOrRemove()
+        {
+            try
+            {
+                // Delete or refresh the bundle depending on its
+                // current state.
+                if (m_bundle.getState() == Bundle.UNINSTALLED)
+                {
+                    // Remove dependencies.
+                    m_dependencies.removeDependencies(m_bundle);
+                    m_bundle.closeAndDelete();
+                    m_bundle = null;
+                }
+                else
+                {
+                    // This removes all old bundle revisions from memory and
+                    // from disk. It only maintains the newest revision in the
+                    // bundle cache.
+                    refreshBundle(m_bundle);
+                }
+            }
+            catch (Throwable ex)
+            {
+                fireFrameworkEvent(FrameworkEvent.ERROR, m_bundle, ex);
+            }
+        }
+
+        public void restart()
+        {
+            if ((m_bundle != null) && (m_oldState == Bundle.ACTIVE))
+            {
+                try
+                {
+// TODO: LAZY - Not sure if this is the best way...
+                    int options = Bundle.START_TRANSIENT;
+                    options = (m_bundle.getPersistentState() == Bundle.STARTING)
+                        ? options | Bundle.START_ACTIVATION_POLICY
+                        : options;
+                    startBundle(m_bundle, options);
+                }
+                catch (Throwable ex)
+                {
+                    fireFrameworkEvent(FrameworkEvent.ERROR, m_bundle, ex);
+                }
+            }
+        }
+    }
+
+    // Compares bundles by start level. Within a start level,
+    // bundles are sorted by bundle ID.
+    private static class StartLevelTuple implements Comparable<StartLevelTuple>
+    {
+        private final BundleImpl m_bundle;
+        private int m_level;
+
+        StartLevelTuple(BundleImpl bundle, int level)
+        {
+            m_bundle = bundle;
+            m_level = level;
+        }
+
+        public int compareTo(StartLevelTuple t)
+        {
+            int result = 1;
+
+            if (m_level < t.m_level)
+            {
+                result = -1;
+            }
+            else if (m_level > t.m_level)
+            {
+                result = 1;
+            }
+            else if (m_bundle.getBundleId() < t.m_bundle.getBundleId())
+            {
+                result = -1;
+            }
+            else if (m_bundle.getBundleId() == t.m_bundle.getBundleId())
+            {
+                result = 0;
+            }
+
+            return result;
+        }
+    }
+
+    //
+    // Locking related methods.
+    //
+
+    private void rememberUninstalledBundle(BundleImpl bundle)
+    {
+        boolean locked = acquireGlobalLock();
+        if (!locked)
+        {
+            // If the calling thread holds bundle locks, then we might not
+            // be able to get the global lock.
+            throw new IllegalStateException(
+                "Unable to acquire global lock to record uninstalled bundle.");
+        }
+        try
+        {
+            // Verify that the bundle is not already in the array.
+            for (int i = 0;
+                (m_uninstalledBundles != null) && (i < m_uninstalledBundles.size());
+                i++)
+            {
+                if (m_uninstalledBundles.get(i) == bundle)
+                {
+                    return;
+                }
+            }
+
+            // Use a copy-on-write approach to add the bundle
+            // to the uninstalled list.
+            List<BundleImpl> uninstalledBundles = new ArrayList(m_uninstalledBundles);
+            uninstalledBundles.add(bundle);
+            m_uninstalledBundles = uninstalledBundles;
+        }
+        finally
+        {
+            releaseGlobalLock();
+        }
+    }
+
+    private void forgetUninstalledBundle(BundleImpl bundle)
+    {
+        boolean locked = acquireGlobalLock();
+        if (!locked)
+        {
+            // If the calling thread holds bundle locks, then we might not
+            // be able to get the global lock.
+            throw new IllegalStateException(
+                "Unable to acquire global lock to release uninstalled bundle.");
+        }
+        try
+        {
+            if (m_uninstalledBundles == null)
+            {
+                return;
+            }
+
+            // Use a copy-on-write approach to remove the bundle
+            // from the uninstalled list.
+            List<BundleImpl> uninstalledBundles = new ArrayList(m_uninstalledBundles);
+            uninstalledBundles.remove(bundle);
+            m_uninstalledBundles = uninstalledBundles;
+        }
+        finally
+        {
+            releaseGlobalLock();
+        }
+    }
+
+    void acquireInstallLock(String location)
+        throws BundleException
+    {
+        synchronized (m_installRequestLock_Priority1)
+        {
+            while (m_installRequestMap.get(location) != null)
+            {
+                try
+                {
+                    m_installRequestLock_Priority1.wait();
+                }
+                catch (InterruptedException ex)
+                {
+                    throw new BundleException("Unable to install, thread interrupted.");
+                }
+            }
+
+            m_installRequestMap.put(location, location);
+        }
+    }
+
+    void releaseInstallLock(String location)
+    {
+        synchronized (m_installRequestLock_Priority1)
+        {
+            m_installRequestMap.remove(location);
+            m_installRequestLock_Priority1.notifyAll();
+        }
+    }
+
+    void setBundleStateAndNotify(BundleImpl bundle, int state)
+    {
+        synchronized (m_bundleLock)
+        {
+            bundle.__setState(state);
+            m_bundleLock.notifyAll();
+        }
+    }
+
+    /**
+     * This method acquires the lock for the specified bundle as long as the
+     * bundle is in one of the specified states. If it is not, an exception
+     * is thrown. Bundle state changes will be monitored to avoid deadlocks.
+     * @param bundle The bundle to lock.
+     * @param desiredStates Logically OR'ed desired bundle states.
+     * @throws java.lang.IllegalStateException If the bundle is not in one of the
+     *         specified desired states.
+    **/
+    void acquireBundleLock(BundleImpl bundle, int desiredStates)
+        throws IllegalStateException
+    {
+        synchronized (m_bundleLock)
+        {
+            // Wait if the desired bundle is already locked by someone else
+            // or if any thread has the global lock, unless the current thread
+            // holds the global lock or the bundle lock already.
+            while (!bundle.isLockable() ||
+                ((m_globalLockThread != null)
+                    && (m_globalLockThread != Thread.currentThread())
+                    && (bundle.getLockingThread() != Thread.currentThread())))
+            {
+                // Check to make sure the bundle is in a desired state.
+                // If so, keep waiting. If not, throw an exception.
+                if ((desiredStates & bundle.getState()) == 0)
+                {
+                    throw new IllegalStateException("Bundle in unexpected state.");
+                }
+                // If the calling thread already owns the global lock, then make
+                // sure no other thread is trying to promote a bundle lock to a
+                // global lock. If so, interrupt the other thread to avoid deadlock.
+                else if (m_globalLockThread == Thread.currentThread()
+                    && (bundle.getLockingThread() != null)
+                    && m_globalLockWaitersList.contains(bundle.getLockingThread()))
+                {
+                    bundle.getLockingThread().interrupt();
+                }
+
+                try
+                {
+                    m_bundleLock.wait();
+                }
+                catch (InterruptedException ex)
+                {
+                    throw new IllegalStateException("Unable to acquire bundle lock, thread interrupted.");
+                }
+            }
+
+            // Now that we can acquire the bundle lock, let's check to make sure
+            // it is in a desired state; if not, throw an exception and do not
+            // lock it.
+            if ((desiredStates & bundle.getState()) == 0)
+            {
+                throw new IllegalStateException("Bundle in unexpected state.");
+            }
+
+            // Acquire the bundle lock.
+            bundle.lock();
+        }
+    }
+
+    /**
+     * Releases the bundle's lock.
+     * @param bundle The bundle whose lock is to be released.
+     * @throws java.lang.IllegalStateException If the calling thread does not
+     *         own the bundle lock.
+    **/
+    void releaseBundleLock(BundleImpl bundle)
+    {
+        synchronized (m_bundleLock)
+        {
+            // Unlock the bundle.
+            bundle.unlock();
+            // If the thread no longer holds the bundle lock,
+            // then remove it from the held lock map.
+            if (bundle.getLockingThread() == null)
+            {
+                m_bundleLock.notifyAll();
+            }
+        }
+    }
+
+    /**
+     * Attempts to acquire the global lock. Will also promote a bundle lock
+     * to the global lock, if the calling thread already holds a bundle lock.
+     * Since it is possible to deadlock when trying to acquire the global lock
+     * while holding a bundle lock, this method may fail if a potential deadlock
+     * is detected. If the calling thread does not hold a bundle lock, then it
+     * will wait indefinitely to acquire the global.
+     * @return <tt>true</tt> if the global lock was successfully acquired,
+     *         <tt>false</tt> otherwise.
+    **/
+    boolean acquireGlobalLock()
+    {
+        synchronized (m_bundleLock)
+        {
+            // Wait as long as some other thread holds the global lock
+            // and the current thread is not interrupted.
+            boolean interrupted = false;
+            while (!interrupted
+                && (m_globalLockThread != null)
+                && (m_globalLockThread != Thread.currentThread()))
+            {
+                // Add calling thread to global lock waiters list.
+                m_globalLockWaitersList.add(Thread.currentThread());
+                // We need to wake up all waiting threads so we can
+                // recheck for potential deadlock in acquireBundleLock()
+                // if this thread was holding a bundle lock and is now
+                // trying to promote it to a global lock.
+                m_bundleLock.notifyAll();
+                // Now wait for the global lock.
+                try
+                {
+                    m_bundleLock.wait();
+                }
+                catch (InterruptedException ex)
+                {
+                    interrupted = true;
+                }
+                // At this point we are either interrupted or will get the
+                // global lock, so remove the thread from the waiters list.
+                m_globalLockWaitersList.remove(Thread.currentThread());
+            }
+
+            // Check to see if we were interrupted, which means someone
+            // with the global lock wants our bundle lock, so we should
+            // fail gracefully.
+            if (!interrupted)
+            {
+                // Increment the current thread's global lock count.
+                m_globalLockCount++;
+                m_globalLockThread = Thread.currentThread();
+            }
+
+            // Note: If the thread was interrupted, there is no reason to notify
+            // anyone, since the thread was likely interrupted to force it to give
+            // up a bundle lock it is holding. When it does give up the bundle
+            // lock, it will do a notifyAll() in there.
+
+            return !interrupted;
+        }
+    }
+
+    /**
+     * Releases the global lock.
+     * @throws java.lang.IllegalStateException If the calling thread does not
+     *         own the global lock.
+    **/
+    void releaseGlobalLock()
+    {
+        synchronized (m_bundleLock)
+        {
+            // Decrement the current thread's global lock count;
+            if (m_globalLockThread == Thread.currentThread())
+            {
+                m_globalLockCount--;
+                if (m_globalLockCount == 0)
+                {
+                    m_globalLockThread = null;
+                    m_bundleLock.notifyAll();
+                }
+            }
+            else
+            {
+                throw new IllegalStateException(
+                    "The current thread doesn't own the global lock.");
+            }
+        }
+    }
+
+    private volatile URLHandlersActivator m_urlHandlersActivator;
+
+    void setURLHandlersActivator(URLHandlersActivator urlHandlersActivator)
+    {
+        m_urlHandlersActivator = urlHandlersActivator;
+    }
+
+    Object getStreamHandlerService(String protocol)
+    {
+        return m_urlHandlersActivator.getStreamHandlerService(protocol);
+    }
+
+    Object getContentHandlerService(String mimeType)
+    {
+        return m_urlHandlersActivator.getContentHandlerService(mimeType);
+    }
+}
diff --git a/src/main/java/org/apache/felix/framework/FilterImpl.java b/src/main/java/org/apache/felix/framework/FilterImpl.java
new file mode 100644
index 0000000..666533f
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/FilterImpl.java
@@ -0,0 +1,258 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import org.apache.felix.framework.ServiceRegistrationImpl.ServiceReferenceImpl;
+import org.apache.felix.framework.capabilityset.CapabilitySet;
+import org.apache.felix.framework.capabilityset.SimpleFilter;
+import org.apache.felix.framework.util.StringMap;
+import org.apache.felix.framework.wiring.BundleCapabilityImpl;
+import org.osgi.framework.Filter;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.wiring.BundleRevision;
+
+public class FilterImpl implements Filter
+{
+    private final SimpleFilter m_filter;
+
+    public FilterImpl(String filterStr) throws InvalidSyntaxException
+    {
+        try
+        {
+            m_filter = SimpleFilter.parse(filterStr);
+        }
+        catch (Throwable th)
+        {
+            throw new InvalidSyntaxException(th.getMessage(), filterStr);
+        }
+    }
+
+    public boolean match(ServiceReference sr)
+    {
+        if (sr instanceof ServiceReferenceImpl)
+        {
+            return CapabilitySet.matches((ServiceReferenceImpl) sr, m_filter);
+        }
+        else
+        {
+            return CapabilitySet.matches(new WrapperCapability(sr), m_filter);
+        }
+    }
+
+    public boolean match(Dictionary<String, ? > dctnr)
+    {
+        return CapabilitySet.matches(new WrapperCapability(dctnr, false), m_filter);
+    }
+
+    public boolean matchCase(Dictionary<String, ? > dctnr)
+    {
+        return CapabilitySet.matches(new WrapperCapability(dctnr, true), m_filter);
+    }
+
+    public boolean matches(Map<String, ?> map)
+    {
+        return CapabilitySet.matches(new WrapperCapability(map), m_filter);
+    }
+
+    public boolean equals(Object o)
+    {
+        return toString().equals(o.toString());
+    }
+
+    public int hashCode()
+    {
+        return toString().hashCode();
+    }
+
+    public String toString()
+    {
+        return m_filter.toString();
+    }
+
+    static class WrapperCapability extends BundleCapabilityImpl
+    {
+        private final Map m_map;
+
+        public WrapperCapability(Map map)
+        {
+            super(null, null, Collections.EMPTY_MAP, Collections.EMPTY_MAP);
+            m_map = (map == null) ? Collections.EMPTY_MAP : map;
+        }
+
+        public WrapperCapability(Dictionary dict, boolean caseSensitive)
+        {
+            super(null, null, Collections.EMPTY_MAP, Collections.EMPTY_MAP);
+            m_map = new DictionaryToMap(dict, caseSensitive);
+        }
+
+        public WrapperCapability(ServiceReference sr)
+        {
+            super(null, null, Collections.EMPTY_MAP, Collections.EMPTY_MAP);
+            m_map = new StringMap();
+            for (String key : sr.getPropertyKeys())
+            {
+                m_map.put(key, sr.getProperty(key));
+            }
+        }
+
+        @Override
+        public BundleRevision getRevision()
+        {
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        @Override
+        public String getNamespace()
+        {
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        @Override
+        public Map<String, String> getDirectives()
+        {
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        @Override
+        public Map<String, Object> getAttributes()
+        {
+            return m_map;
+        }
+
+        @Override
+        public List<String> getUses()
+        {
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+    }
+
+    private static class DictionaryToMap implements Map
+    {
+        private final Map m_map;
+        private final Dictionary m_dict;
+
+        public DictionaryToMap(Dictionary dict, boolean caseSensitive)
+        {
+            if (!caseSensitive)
+            {
+                m_dict = null;
+                m_map = new StringMap();
+                if (dict != null)
+                {
+                    Enumeration keys = dict.keys();
+                    while (keys.hasMoreElements())
+                    {
+                        Object key = keys.nextElement();
+                        if (m_map.get(key) == null)
+                        {
+                            m_map.put(key, dict.get(key));
+                        }
+                        else
+                        {
+                            throw new IllegalArgumentException(
+                                "Duplicate attribute: " + key.toString());
+                        }
+                    }
+                }
+            }
+            else
+            {
+                m_dict = dict;
+                m_map = null;
+            }
+        }
+
+        public int size()
+        {
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        public boolean isEmpty()
+        {
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        public boolean containsKey(Object o)
+        {
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        public boolean containsValue(Object o)
+        {
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        public Object get(Object o)
+        {
+            if (m_dict != null)
+            {
+                return m_dict.get(o);
+            }
+            else if (m_map != null)
+            {
+                return m_map.get(o);
+            }
+            return null;
+        }
+
+        public Object put(Object k, Object v)
+        {
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        public Object remove(Object o)
+        {
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        public void putAll(Map map)
+        {
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        public void clear()
+        {
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        public Set<Object> keySet()
+        {
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        public Collection<Object> values()
+        {
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        public Set<Entry<Object, Object>> entrySet()
+        {
+            return Collections.EMPTY_SET;
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/felix/framework/FrameworkFactory.java b/src/main/java/org/apache/felix/framework/FrameworkFactory.java
new file mode 100644
index 0000000..ce377ad
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/FrameworkFactory.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework;
+
+import java.util.Map;
+import org.osgi.framework.launch.Framework;
+
+public class FrameworkFactory implements org.osgi.framework.launch.FrameworkFactory
+{
+    public Framework newFramework(Map configuration)
+    {
+        return new Felix(configuration);
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/felix/framework/FrameworkStartLevelImpl.java b/src/main/java/org/apache/felix/framework/FrameworkStartLevelImpl.java
new file mode 100644
index 0000000..e6509ce
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/FrameworkStartLevelImpl.java
@@ -0,0 +1,344 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.osgi.framework.AdminPermission;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.FrameworkListener;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.framework.startlevel.BundleStartLevel;
+import org.osgi.framework.startlevel.FrameworkStartLevel;
+import org.osgi.service.startlevel.StartLevel;
+
+class FrameworkStartLevelImpl implements FrameworkStartLevel, Runnable
+{
+    static final String THREAD_NAME = "FelixStartLevel";
+
+    private static final int BUNDLE_IDX = 0;
+    private static final int STARTLEVEL_IDX = 1;
+
+    private final Felix m_felix;
+    private final ServiceRegistry m_registry;
+    private final List m_requests = new ArrayList();
+    private final List<FrameworkListener[]> m_requestListeners
+        = new ArrayList<FrameworkListener[]>();
+    private ServiceRegistration<StartLevel> m_slReg;
+    private Thread m_thread = null;
+
+    FrameworkStartLevelImpl(Felix felix, ServiceRegistry registry)
+    {
+        m_felix = felix;
+        m_registry = registry;
+    }
+
+    void start()
+    {
+        m_slReg = m_registry.registerService(m_felix._getBundleContext(),
+                new String[] { StartLevel.class.getName() },
+                new StartLevelImpl(m_felix),
+                null);
+    }
+
+    // Should only be called hold requestList lock.
+    private void startThread()
+    {
+        // Start a thread to perform asynchronous package refreshes.
+        if (m_thread == null)
+        {
+            m_thread = new Thread(this, THREAD_NAME);
+            m_thread.setDaemon(true);
+            m_thread.start();
+        }
+    }
+
+    /**
+     * Stops the FelixStartLevel thread on system shutdown. Shutting down the
+     * thread explicitly is required in the embedded case, where Felix may be
+     * stopped without the Java VM being stopped. In this case the
+     * FelixStartLevel thread must be stopped explicitly.
+     * <p>
+     * This method is called by the
+     * {@link StartLevelActivator#stop(BundleContext)} method.
+     */
+    void stop()
+    {
+        synchronized (m_requests)
+        {
+            if (m_thread != null)
+            {
+                // Null thread variable to signal to the thread that
+                // we want it to exit.
+                m_thread = null;
+
+                // Wake up the thread, if it is currently in the wait() state
+                // for more work.
+                m_requests.notifyAll();
+            }
+        }
+    }
+
+    public Bundle getBundle()
+    {
+        return m_felix;
+    }
+
+    public int getStartLevel()
+    {
+        return m_felix.getActiveStartLevel();
+    }
+
+    public void setStartLevel(int startlevel, FrameworkListener... listeners)
+    {
+        Object sm = System.getSecurityManager();
+
+        if (sm != null)
+        {
+            ((SecurityManager) sm).checkPermission(
+                new AdminPermission(m_felix, AdminPermission.STARTLEVEL));
+        }
+
+        if (startlevel <= 0)
+        {
+            throw new IllegalArgumentException(
+                "Start level must be greater than zero.");
+        }
+
+        synchronized (m_requests)
+        {
+            // Start thread if necessary.
+            startThread();
+            // Queue request.
+            m_requestListeners.add(listeners);
+            m_requests.add(new Integer(startlevel));
+            m_requests.notifyAll();
+        }
+    }
+
+    /**
+     * This method is currently only called by the by the thread that calls
+     * the Felix.start() method and the shutdown thread when the
+     * framework is shutting down.
+     * @param startlevel
+    **/
+    /* package */ void setStartLevelAndWait(int startlevel)
+    {
+        Object request = new Integer(startlevel);
+        synchronized (request)
+        {
+            synchronized (m_requests)
+            {
+                // Start thread if necessary.
+                startThread();
+                // Queue request.
+                m_requestListeners.add(null);
+                m_requests.add(request);
+                m_requests.notifyAll();
+            }
+
+            try
+            {
+                request.wait();
+            }
+            catch (InterruptedException ex)
+            {
+                // Log it and ignore since it won't cause much of an issue.
+                m_felix.getLogger().log(
+                    Logger.LOG_WARNING,
+                    "Wait for start level change during shutdown interrupted.",
+                    ex);
+            }
+        }
+    }
+
+    public int getInitialBundleStartLevel()
+    {
+        return m_felix.getInitialBundleStartLevel();
+    }
+
+    public void setInitialBundleStartLevel(int startlevel)
+    {
+        Object sm = System.getSecurityManager();
+
+        if (sm != null)
+        {
+            ((SecurityManager) sm).checkPermission(
+                new AdminPermission(m_felix, AdminPermission.STARTLEVEL));
+        }
+        m_felix.setInitialBundleStartLevel(startlevel);
+    }
+
+    BundleStartLevel createBundleStartLevel(BundleImpl bundle)
+    {
+        return new BundleStartLevelImpl(bundle);
+    }
+
+    class BundleStartLevelImpl implements BundleStartLevel
+    {
+        private BundleImpl m_bundle;
+
+        private BundleStartLevelImpl(BundleImpl bundle)
+        {
+            m_bundle = bundle;
+        }
+
+        public Bundle getBundle()
+        {
+            return m_bundle;
+        }
+
+        public int getStartLevel()
+        {
+            return m_felix.getBundleStartLevel(m_bundle);
+        }
+
+        public void setStartLevel(int startlevel)
+        {
+            Object sm = System.getSecurityManager();
+
+            if (sm != null)
+            {
+                ((SecurityManager) sm).checkPermission(
+                    new AdminPermission(m_bundle, AdminPermission.EXECUTE));
+            }
+
+            if (m_bundle.getBundleId() == 0)
+            {
+                throw new IllegalArgumentException(
+                    "Cannot change system bundle start level.");
+            }
+            else if (startlevel <= 0)
+            {
+                throw new IllegalArgumentException(
+                    "Start level must be greater than zero.");
+            }
+            synchronized (m_requests)
+            {
+                // Start thread if necessary.
+                startThread();
+                // Synchronously persists the start level.
+                ((BundleImpl) m_bundle).setStartLevel(startlevel);
+                // Queue request.
+                m_requestListeners.add(null);
+                m_requests.add(new Object[] { m_bundle, new Integer(startlevel) });
+                m_requests.notifyAll();
+            }
+        }
+
+        public boolean isPersistentlyStarted()
+        {
+            return m_felix.isBundlePersistentlyStarted(m_bundle);
+        }
+
+        public boolean isActivationPolicyUsed()
+        {
+            return m_felix.isBundleActivationPolicyUsed(m_bundle);
+        }
+    }
+
+    public void run()
+    {
+        // This thread loops forever, thus it should
+        // be a daemon thread.
+        Object previousRequest = null;
+        while (true)
+        {
+            Object request = null;
+            FrameworkListener[] listeners = null;
+            synchronized (m_requests)
+            {
+                // Wait for a request.
+                while (m_requests.isEmpty())
+                {
+                    // Terminate the thread if requested to do so (see stop()).
+                    if (m_thread == null)
+                    {
+                        return;
+                    }
+
+                    try
+                    {
+                        m_requests.wait();
+                    }
+                    catch (InterruptedException ex)
+                    {
+                        // Ignore.
+                    }
+                }
+
+                // Get the requested start level.
+                request = m_requests.remove(0);
+                listeners = m_requestListeners.remove(0);
+            }
+
+            // If the request object is an Integer, then the request
+            // is to set the framework start level. If the request is
+            // an Object array, then the request is to set the start
+            // level for a bundle.
+            // NOTE: We don't catch any exceptions here, because
+            // the invoked methods shield us from exceptions by
+            // catching Throwables when they invoke callbacks.
+            if (request instanceof Integer)
+            {
+                // Set the new framework start level.
+                try
+                {
+                    m_felix.setActiveStartLevel(((Integer) request).intValue(), listeners);
+                }
+                catch (IllegalStateException ise)
+                {
+                    // Thrown if global lock cannot be acquired, in which case
+                    // just retry (unless we already did)
+                    if (previousRequest == request)
+                    {
+                        m_felix.getLogger().log(Logger.LOG_ERROR,
+                            "Unexpected problem setting active start level to " + request, ise);
+                    }
+                    else
+                    {
+                        synchronized (m_requests)
+                        {
+                            m_requests.add(0, request);
+                            previousRequest = request;
+                        }
+                    }
+                }
+                catch (Exception ex)
+                {
+                    m_felix.getLogger().log(Logger.LOG_ERROR,
+                        "Unexpected problem setting active start level to " + request, ex);
+                }
+            }
+            else
+            {
+                Bundle bundle = (Bundle) ((Object[]) request)[BUNDLE_IDX];
+                int startlevel = ((Integer) ((Object[]) request)[STARTLEVEL_IDX]).intValue();
+                m_felix.setBundleStartLevel(bundle, startlevel);
+            }
+
+            // Notify any waiting thread that this request is done.
+            synchronized (request)
+            {
+                request.notifyAll();
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/felix/framework/FrameworkWiringImpl.java b/src/main/java/org/apache/felix/framework/FrameworkWiringImpl.java
new file mode 100644
index 0000000..8ad803c
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/FrameworkWiringImpl.java
@@ -0,0 +1,188 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import org.osgi.framework.AdminPermission;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.FrameworkListener;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.framework.wiring.FrameworkWiring;
+import org.osgi.service.packageadmin.PackageAdmin;
+
+class FrameworkWiringImpl implements FrameworkWiring, Runnable
+{
+    private final Felix m_felix;
+    private final ServiceRegistry m_registry;
+    private final List<Collection<Bundle>> m_requests = new ArrayList();
+    private final List<FrameworkListener[]> m_requestListeners
+        = new ArrayList<FrameworkListener[]>();
+    private ServiceRegistration<PackageAdmin> m_paReg;
+    private Thread m_thread = null;
+
+
+    public FrameworkWiringImpl(Felix felix, ServiceRegistry registry)
+    {
+        m_felix = felix;
+        m_registry = registry;
+    }
+
+    void start()
+    {
+        m_paReg = m_registry.registerService(m_felix._getBundleContext(),
+                new String[] { PackageAdmin.class.getName() },
+                new PackageAdminImpl(m_felix),
+                null);
+    }
+
+    /**
+     * Stops the FelixFrameworkWiring thread on system shutdown. Shutting down the
+     * thread explicitly is required in the embedded case, where Felix may be
+     * stopped without the Java VM being stopped. In this case the
+     * FelixFrameworkWiring thread must be stopped explicitly.
+     * <p>
+     * This method is called by the
+     * {@link PackageAdminActivator#stop(BundleContext)} method.
+     */
+    void stop()
+    {
+        synchronized (m_requests)
+        {
+            if (m_thread != null)
+            {
+                // Null thread variable to signal to the thread that
+                // we want it to exit.
+                m_thread = null;
+
+                // Wake up the thread, if it is currently in the wait() state
+                // for more work.
+                m_requests.notifyAll();
+            }
+        }
+    }
+
+    public Bundle getBundle()
+    {
+        return m_felix;
+    }
+
+    public void refreshBundles(Collection<Bundle> bundles, FrameworkListener... listeners)
+    {
+        Object sm = System.getSecurityManager();
+
+        if (sm != null)
+        {
+            ((SecurityManager) sm).checkPermission(
+                new AdminPermission(m_felix, AdminPermission.RESOLVE));
+        }
+        synchronized (m_requests)
+        {
+            // Start a thread to perform asynchronous package refreshes.
+            if (m_thread == null)
+            {
+                m_thread = new Thread(this, "FelixFrameworkWiring");
+                m_thread.setDaemon(true);
+                m_thread.start();
+            }
+
+            // Queue request and notify thread.
+            m_requests.add(bundles);
+            m_requestListeners.add(listeners);
+            m_requests.notifyAll();
+        }
+    }
+
+    public boolean resolveBundles(Collection<Bundle> bundles)
+    {
+        Object sm = System.getSecurityManager();
+
+        if (sm != null)
+        {
+            ((SecurityManager) sm).checkPermission(
+                new AdminPermission(m_felix, AdminPermission.RESOLVE));
+        }
+
+        return m_felix.resolveBundles(bundles);
+    }
+
+    public Collection<Bundle> getRemovalPendingBundles()
+    {
+        return m_felix.getRemovalPendingBundles();
+    }
+
+    public Collection<Bundle> getDependencyClosure(Collection<Bundle> targets)
+    {
+        return m_felix.getDependencyClosure(targets);
+    }
+
+    /**
+     * The OSGi specification states that package refreshes happen
+     * asynchronously; this is the run() method for the package
+     * refreshing thread.
+    **/
+    public void run()
+    {
+        // This thread loops forever, thus it should
+        // be a daemon thread.
+        while (true)
+        {
+            Collection<Bundle> bundles = null;
+            FrameworkListener[] listeners = null;
+            synchronized (m_requests)
+            {
+                // Wait for a refresh request.
+                while (m_requests.isEmpty())
+                {
+                    // Terminate the thread if requested to do so (see stop()).
+                    if (m_thread == null)
+                    {
+                        return;
+                    }
+
+                    try
+                    {
+                        m_requests.wait();
+                    }
+                    catch (InterruptedException ex)
+                    {
+                    }
+                }
+
+                // Get the bundles parameter for the current refresh request.
+                bundles = m_requests.get(0);
+                listeners = m_requestListeners.get(0);
+            }
+
+            // Perform refresh.
+            // NOTE: We don't catch any exceptions here, because
+            // the invoked method shields us from exceptions by
+            // catching Throwables when its invokes callbacks.
+            m_felix.refreshPackages(bundles, listeners);
+
+            // Remove the first request since it is now completed.
+            synchronized (m_requests)
+            {
+                m_requests.remove(0);
+                m_requestListeners.remove(0);
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/felix/framework/Logger.java b/src/main/java/org/apache/felix/framework/Logger.java
new file mode 100644
index 0000000..6e05d4f
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/Logger.java
@@ -0,0 +1,324 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import org.osgi.framework.*;
+
+/**
+ * <p>
+ * This class mimics the standard OSGi <tt>LogService</tt> interface. An
+ * instance of this class is used by the framework for all logging. By default
+ * this class logs messages to standard out. The log level can be set to
+ * control the amount of logging performed, where a higher number results in
+ * more logging. A log level of zero turns off logging completely.
+ * </p>
+ * <p>
+ * The log levels match those specified in the OSGi Log Service (i.e., 1 = error,
+ * 2 = warning, 3 = information, and 4 = debug). The default value is 1.
+ * </p>
+ * <p>
+ * This class also uses the System Bundle's context to track log services
+ * and will use the highest ranking log service, if present, as a back end
+ * instead of printing to standard out. The class uses reflection to invoking
+ * the log service's method to avoid a dependency on the log interface.
+ * </p>
+**/
+public class Logger implements ServiceListener
+{
+    public static final int LOG_ERROR = 1;
+    public static final int LOG_WARNING = 2;
+    public static final int LOG_INFO = 3;
+    public static final int LOG_DEBUG = 4;
+
+    private int m_logLevel = 1;
+    private BundleContext m_context = null;
+
+    private final static int LOGGER_OBJECT_IDX = 0;
+    private final static int LOGGER_METHOD_IDX = 1;
+    private ServiceReference m_logRef = null;
+    private Object[] m_logger = null;
+
+    public Logger()
+    {
+    }
+
+    public final synchronized void setLogLevel(int i)
+    {
+        m_logLevel = i;
+    }
+
+    public final synchronized int getLogLevel()
+    {
+        return m_logLevel;
+    }
+
+    protected void setSystemBundleContext(BundleContext context)
+    {
+        // TODO: Find a way to log to a log service inside the framework.
+        // The issue is that we log messages while holding framework
+        // internal locks -- hence, when a log service calls back into 
+        // the framework (e.g., by loading a class) we might deadlock. 
+        // One instance of this problem is tracked in FELIX-536.
+        // For now we just disable logging to log services inside the
+        // framework. 
+
+        // m_context = context;
+        // startListeningForLogService();
+    }
+
+    public final void log(int level, String msg)
+    {
+        _log(null, null, level, msg, null);
+    }
+
+    public final void log(int level, String msg, Throwable throwable)
+    {
+        _log(null, null, level, msg, throwable);
+    }
+
+    public final void log(ServiceReference sr, int level, String msg)
+    {
+        _log(null, sr, level, msg, null);
+    }
+
+    public final void log(ServiceReference sr, int level, String msg, Throwable throwable)
+    {
+        _log(null, sr, level, msg, throwable);
+    }
+
+    public final void log(Bundle bundle, int level, String msg)
+    {
+        _log(bundle, null, level, msg, null);
+    }
+
+    public final void log(Bundle bundle, int level, String msg, Throwable throwable)
+    {
+        _log(bundle, null, level, msg, throwable);
+    }
+
+    protected void doLog(
+        Bundle bundle, ServiceReference sr, int level,
+        String msg, Throwable throwable)
+    {
+        String s = "";
+        if (sr != null)
+        {
+            s = s + "SvcRef "  + sr + " ";
+        }
+        else if (bundle != null)
+        {
+            s = s + "Bundle " + bundle.toString() + " ";
+        }
+        s = s + msg;
+        if (throwable != null)
+        {
+            s = s + " (" + throwable + ")";
+        }
+        switch (level)
+        {
+            case LOG_DEBUG:
+                System.out.println("DEBUG: " + s);
+                break;
+            case LOG_ERROR:
+                System.out.println("ERROR: " + s);
+                if (throwable != null)
+                {
+                    if ((throwable instanceof BundleException) &&
+                        (((BundleException) throwable).getNestedException() != null))
+                    {
+                        throwable = ((BundleException) throwable).getNestedException();
+                    }
+                    throwable.printStackTrace();
+                }
+                break;
+            case LOG_INFO:
+                System.out.println("INFO: " + s);
+                break;
+            case LOG_WARNING:
+                System.out.println("WARNING: " + s);
+                break;
+            default:
+                System.out.println("UNKNOWN[" + level + "]: " + s);
+        }
+    }
+
+    private void _log(
+        Bundle bundle, ServiceReference sr, int level,
+        String msg, Throwable throwable)
+    {
+        // Save our own copy just in case it changes. We could try to do
+        // more conservative locking here, but let's be optimistic.
+        Object[] logger = m_logger;
+
+        if (m_logLevel >= level)
+        {
+            // Use the log service if available.
+            if (logger != null)
+            {
+                _logReflectively(logger, sr, level, msg, throwable);
+            }
+            // Otherwise, default logging action.
+            else
+            {
+                doLog(bundle, sr, level, msg, throwable);
+            }
+        }
+    }
+
+    private void _logReflectively(
+        Object[] logger, ServiceReference sr, int level, String msg, Throwable throwable)
+    {
+        if (logger != null)
+        {
+            Object[] params = {
+                sr, new Integer(level), msg, throwable
+            };
+            try
+            {
+                ((Method) logger[LOGGER_METHOD_IDX]).invoke(logger[LOGGER_OBJECT_IDX], params);
+            }
+            catch (InvocationTargetException ex)
+            {
+                System.err.println("Logger: " + ex);
+            }
+            catch (IllegalAccessException ex)
+            {
+                System.err.println("Logger: " + ex);
+            }
+        }
+    }
+
+    /**
+     * This method is called when the system bundle context is set;
+     * it simply adds a service listener so that the system bundle can track
+     * log services to be used as the back end of the logging mechanism. It also
+     * attempts to get an existing log service, if present, but in general
+     * there will never be a log service present since the system bundle is
+     * started before every other bundle.
+    **/
+    private synchronized void startListeningForLogService()
+    {
+        // Add a service listener for log services.
+        try
+        {
+            m_context.addServiceListener(
+                this, "(objectClass=org.osgi.service.log.LogService)");
+        }
+        catch (InvalidSyntaxException ex) {
+            // This will never happen since the filter is hard coded.
+        }
+        // Try to get an existing log service.
+        m_logRef = m_context.getServiceReference("org.osgi.service.log.LogService");
+        // Get the service object if available and set it in the logger.
+        if (m_logRef != null)
+        {
+            setLogger(m_context.getService(m_logRef));
+        }
+    }
+
+    /**
+     * This method implements the callback for the ServiceListener interface.
+     * It is public as a byproduct of implementing the interface and should
+     * not be called directly. This method tracks run-time changes to log
+     * service availability. If the log service being used by the framework's
+     * logging mechanism goes away, then this will try to find an alternative.
+     * If a higher ranking log service is registered, then this will switch
+     * to the higher ranking log service.
+    **/
+    public final synchronized void serviceChanged(ServiceEvent event)
+    {
+        // If no logger is in use, then grab this one.
+        if ((event.getType() == ServiceEvent.REGISTERED) && (m_logRef == null))
+        {
+            m_logRef = event.getServiceReference();
+            // Get the service object and set it in the logger.
+            setLogger(m_context.getService(m_logRef));
+        }
+        // If a logger is in use, but this one has a higher ranking, then swap
+        // it for the existing logger.
+        else if ((event.getType() == ServiceEvent.REGISTERED) && (m_logRef != null))
+        {
+            ServiceReference ref =
+                m_context.getServiceReference("org.osgi.service.log.LogService");
+            if (!ref.equals(m_logRef))
+            {
+                m_context.ungetService(m_logRef);
+                m_logRef = ref;
+                setLogger(m_context.getService(m_logRef));
+            }
+
+        }
+        // If the current logger is going away, release it and try to
+        // find another one.
+        else if ((event.getType() == ServiceEvent.UNREGISTERING) &&
+            m_logRef.equals(event.getServiceReference()))
+        {
+            // Unget the service object.
+            m_context.ungetService(m_logRef);
+            // Try to get an existing log service.
+            m_logRef = m_context.getServiceReference(
+                "org.osgi.service.log.LogService");
+            // Get the service object if available and set it in the logger.
+            if (m_logRef != null)
+            {
+                setLogger(m_context.getService(m_logRef));
+            }
+            else
+            {
+                setLogger(null);
+            }
+        }
+    }
+
+    /**
+     * This method sets the new log service object. It also caches the method to
+     * invoke. The service object and method are stored in array to optimistically
+     * eliminate the need to locking when logging.
+    **/
+    private void setLogger(Object logObj)
+    {
+        if (logObj == null)
+        {
+            m_logger = null;
+        }
+        else
+        {
+            Class[] formalParams = {
+                ServiceReference.class,
+                Integer.TYPE,
+                String.class,
+                Throwable.class
+            };
+
+            try
+            {
+                Method logMethod = logObj.getClass().getMethod("log", formalParams);
+                logMethod.setAccessible(true);
+                m_logger = new Object[] { logObj, logMethod };
+            }
+            catch (NoSuchMethodException ex)
+            {
+                System.err.println("Logger: " + ex);
+                m_logger = null;
+            }
+        }
+    }
+}
diff --git a/src/main/java/org/apache/felix/framework/PackageAdminImpl.java b/src/main/java/org/apache/felix/framework/PackageAdminImpl.java
new file mode 100644
index 0000000..3a5b915
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/PackageAdminImpl.java
@@ -0,0 +1,265 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework;
+
+import java.util.*;
+import org.apache.felix.framework.util.Util;
+import org.apache.felix.framework.util.VersionRange;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Version;
+import org.osgi.framework.wiring.BundleRevision;
+import org.osgi.framework.wiring.BundleRevisions;
+import org.osgi.framework.wiring.BundleWire;
+import org.osgi.framework.wiring.FrameworkWiring;
+import org.osgi.service.packageadmin.ExportedPackage;
+import org.osgi.service.packageadmin.PackageAdmin;
+import org.osgi.service.packageadmin.RequiredBundle;
+
+
+public class PackageAdminImpl implements PackageAdmin
+{
+    private static final Comparator COMPARATOR = new Comparator() {
+        public int compare(Object o1, Object o2)
+        {
+            // Reverse arguments to sort in descending order.
+            return ((ExportedPackage) o2).getVersion().compareTo(
+                ((ExportedPackage) o1).getVersion());
+        }
+    };
+
+    private Felix m_felix = null;
+
+    PackageAdminImpl(Felix felix)
+    {
+        m_felix = felix;
+    }
+
+    /**
+     * Returns the bundle associated with this class if the class was
+     * loaded from a bundle, otherwise returns null.
+     *
+     * @param clazz the class for which to determine its associated bundle.
+     * @return the bundle associated with the specified class, otherwise null.
+    **/
+    public Bundle getBundle(Class clazz)
+    {
+        return m_felix.getBundle(clazz);
+    }
+
+    /**
+     * Returns all bundles that have a specified symbolic name and whose
+     * version is in the specified version range. If no version range is
+     * specified, then all bundles with the specified symbolic name are
+     * returned. The array is sorted in descending version order.
+     *
+     * @param symbolicName the target symbolic name.
+     * @param versionRange the target version range.
+     * @return an array of matching bundles sorted in descending version order.
+    **/
+    public Bundle[] getBundles(String symbolicName, String versionRange)
+    {
+        VersionRange vr = (versionRange == null) ? null : VersionRange.parse(versionRange);
+        Bundle[] bundles = m_felix.getBundles();
+        List list = new ArrayList();
+        for (int i = 0; (bundles != null) && (i < bundles.length); i++)
+        {
+            String sym = bundles[i].getSymbolicName();
+            if ((sym != null) && sym.equals(symbolicName))
+            {
+                Version v = bundles[i].adapt(BundleRevision.class).getVersion();
+                if ((vr == null) || vr.isInRange(v))
+                {
+                    list.add(bundles[i]);
+                }
+            }
+        }
+        if (list.isEmpty())
+        {
+            return null;
+        }
+        bundles = (Bundle[]) list.toArray(new Bundle[list.size()]);
+        Arrays.sort(bundles,new Comparator() {
+            public int compare(Object o1, Object o2)
+            {
+                Version v1 = ((Bundle) o1).adapt(BundleRevision.class).getVersion();
+                Version v2 = ((Bundle) o2).adapt(BundleRevision.class).getVersion();
+                // Compare in reverse order to get descending sort.
+                return v2.compareTo(v1);
+            }
+        });
+        return bundles;
+    }
+
+    public int getBundleType(Bundle bundle)
+    {
+        Map headerMap = ((BundleRevisionImpl)
+            bundle.adapt(BundleRevisionImpl.class)).getHeaders();
+        if (headerMap.containsKey(Constants.FRAGMENT_HOST))
+        {
+            return PackageAdmin.BUNDLE_TYPE_FRAGMENT;
+        }
+        return 0;
+    }
+
+    /**
+     * Returns the exported package associated with the specified
+     * package name. If there are more than one version of the package
+     * being exported, then the highest version is returned.
+     *
+     * @param name the name of the exported package to find.
+     * @return the exported package or null if no matching package was found.
+    **/
+    public ExportedPackage getExportedPackage(String name)
+    {
+        // Get all versions of the exported package.
+        ExportedPackage[] pkgs = m_felix.getExportedPackages(name);
+        // If there are no versions exported, then return null.
+        if ((pkgs == null) || (pkgs.length == 0))
+        {
+            return null;
+        }
+        // Sort the exported versions.
+        Arrays.sort(pkgs, COMPARATOR);
+        // Return the highest version.
+        return pkgs[0];
+    }
+
+    public ExportedPackage[] getExportedPackages(String name)
+    {
+        ExportedPackage[] pkgs = m_felix.getExportedPackages(name);
+        return ((pkgs == null) || pkgs.length == 0) ? null : pkgs;
+    }
+
+    /**
+     * Returns the packages exported by the specified bundle.
+     *
+     * @param bundle the bundle whose exported packages are to be returned.
+     * @return an array of packages exported by the bundle or null if the
+     *         bundle does not export any packages.
+    **/
+    public ExportedPackage[] getExportedPackages(Bundle bundle)
+    {
+        ExportedPackage[] pkgs = m_felix.getExportedPackages(bundle);
+        return ((pkgs == null) || pkgs.length == 0) ? null : pkgs;
+    }
+
+    public Bundle[] getFragments(Bundle bundle)
+    {
+        // If the bundle is not a fragment, then return its fragments.
+        if ((getBundleType(bundle) & BUNDLE_TYPE_FRAGMENT) == 0)
+        {
+            List<Bundle> list = new ArrayList<Bundle>();
+            // Iterate through revisions
+            for (BundleRevision revision : bundle.adapt(BundleRevisions.class).getRevisions())
+            {
+                // Get attached fragments.
+                if (revision.getWiring() != null)
+                {
+                    List<BundleRevision> fragments =
+                        Util.getFragments(revision.getWiring());
+                    for (int i = 0; i < fragments.size(); i++)
+                    {
+                        Bundle b = fragments.get(i).getBundle();
+                        if (b != null)
+                        {
+                            list.add(b);
+                        }
+                    }
+                }
+            }
+            // Convert list to an array.
+            return (list.isEmpty())
+                ? null
+                : (Bundle[]) list.toArray(new Bundle[list.size()]);
+        }
+        return null;
+    }
+
+    public Bundle[] getHosts(Bundle bundle)
+    {
+        // If the bundle is a fragment, return its hosts
+        if ((getBundleType(bundle) & BUNDLE_TYPE_FRAGMENT) != 0)
+        {
+            List<Bundle> list = new ArrayList<Bundle>();
+            // Iterate through revisions
+            for (BundleRevision revision : bundle.adapt(BundleRevisions.class).getRevisions())
+            {
+                // Get hosts
+                if (revision.getWiring() != null)
+                {
+                    List<BundleWire> hostWires = revision.getWiring().getRequiredWires(null);
+                    for (int i = 0; (hostWires != null) && (i < hostWires.size()); i++)
+                    {
+                        Bundle b = hostWires.get(i).getProviderWiring().getBundle();
+                        if (b != null)
+                        {
+                            list.add(b);
+                        }
+                    }
+                }
+            }
+            // Convert list to an array.
+            return (list.isEmpty())
+                ? null
+                : (Bundle[]) list.toArray(new Bundle[list.size()]);
+        }
+        return null;
+    }
+
+    public RequiredBundle[] getRequiredBundles(String symbolicName)
+    {
+        List list = new ArrayList();
+        for (Bundle bundle : m_felix.getBundles())
+        {
+            if ((symbolicName == null)
+                || (symbolicName.equals(bundle.getSymbolicName())))
+            {
+                list.add(new RequiredBundleImpl(m_felix, (BundleImpl) bundle));
+            }
+        }
+        return (list.isEmpty())
+            ? null
+            : (RequiredBundle[]) list.toArray(new RequiredBundle[list.size()]);
+    }
+
+    /**
+     * The OSGi specification states that refreshing packages is
+     * asynchronous; this method simply notifies the package admin
+     * thread to do a refresh.
+     * @param bundles array of bundles to refresh or <tt>null</tt> to refresh
+     *                any bundles in need of refreshing.
+    **/
+    public void refreshPackages(Bundle[] bundles)
+        throws SecurityException
+    {
+        List<Bundle> list = (bundles == null)
+            ? null
+            : Arrays.asList(bundles);
+        m_felix.adapt(FrameworkWiring.class).refreshBundles(list);
+    }
+
+    public boolean resolveBundles(Bundle[] bundles)
+    {
+        List<Bundle> list = (bundles == null)
+            ? null
+            : Arrays.asList(bundles);
+        return m_felix.adapt(FrameworkWiring.class).resolveBundles(list);
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/felix/framework/RequiredBundleImpl.java b/src/main/java/org/apache/felix/framework/RequiredBundleImpl.java
new file mode 100644
index 0000000..855223b
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/RequiredBundleImpl.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework;
+
+import java.util.Set;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.Version;
+import org.osgi.service.packageadmin.RequiredBundle;
+
+class RequiredBundleImpl implements RequiredBundle
+{
+    private final Felix m_felix;
+    private final BundleImpl m_bundle;
+    private volatile String m_toString = null;
+    private volatile String m_versionString = null;
+
+    public RequiredBundleImpl(Felix felix, BundleImpl bundle)
+    {
+        m_felix = felix;
+        m_bundle = bundle;
+    }
+
+    public String getSymbolicName()
+    {
+        return m_bundle.getSymbolicName();
+    }
+
+    public Bundle getBundle()
+    {
+        return m_bundle;
+    }
+
+    public Bundle[] getRequiringBundles()
+    {
+        // If the package is stale, then return null per the spec.
+        if (m_bundle.isStale())
+        {
+            return null;
+        }
+        Set<Bundle> set = m_felix.getRequiringBundles(m_bundle);
+        return set.toArray(new Bundle[set.size()]);
+    }
+
+    public Version getVersion()
+    {
+        return m_bundle.getVersion();
+    }
+
+    public boolean isRemovalPending()
+    {
+        return m_bundle.isRemovalPending();
+    }
+
+    public String toString()
+    {
+        if (m_toString == null)
+        {
+            m_toString = m_bundle.getSymbolicName()
+                + "; version=" + m_bundle.getVersion();
+        }
+        return m_toString;
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/felix/framework/ResolveContextImpl.java b/src/main/java/org/apache/felix/framework/ResolveContextImpl.java
new file mode 100644
index 0000000..393dabf
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/ResolveContextImpl.java
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.felix.framework.StatefulResolver.ResolverHookRecord;
+import org.apache.felix.framework.resolver.CandidateComparator;
+import org.apache.felix.framework.resolver.HostedCapability;
+import org.apache.felix.framework.resolver.ResolveContext;
+import org.apache.felix.framework.resolver.ResolveException;
+import org.osgi.framework.wiring.BundleCapability;
+import org.osgi.framework.wiring.BundleRequirement;
+import org.osgi.framework.wiring.BundleRevision;
+import org.osgi.framework.wiring.BundleWiring;
+
+/**
+ *
+ * @author rickhall
+ */
+public class ResolveContextImpl extends ResolveContext
+{
+    private final StatefulResolver m_state;
+    private final Map<BundleRevision, BundleWiring> m_wirings;
+    private final ResolverHookRecord m_resolverHookrecord;
+    private final Collection<BundleRevision> m_mandatory;
+    private final Collection<BundleRevision> m_optional;
+    private final Collection<BundleRevision> m_ondemand;
+
+    ResolveContextImpl(
+        StatefulResolver state, Map<BundleRevision, BundleWiring> wirings,
+        ResolverHookRecord resolverHookRecord, Collection<BundleRevision> mandatory,
+        Collection<BundleRevision> optional, Collection<BundleRevision> ondemand)
+    {
+        m_state = state;
+        m_wirings = wirings;
+        m_resolverHookrecord = resolverHookRecord;
+        m_mandatory = mandatory;
+        m_optional = optional;
+        m_ondemand = ondemand;
+    }
+
+    @Override
+    public Collection<BundleRevision> getMandatoryRevisions()
+    {
+        return new ArrayList<BundleRevision>(m_mandatory);
+    }
+
+    @Override
+    public Collection<BundleRevision> getOptionalRevisions()
+    {
+        return new ArrayList<BundleRevision>(m_optional);
+    }
+
+    public Collection<BundleRevision> getOndemandRevisions()
+    {
+        return new ArrayList<BundleRevision>(m_ondemand);
+    }
+
+    public List<BundleCapability> findProviders(BundleRequirement br, boolean obeyMandatory)
+    {
+        return m_state.findProvidersInternal(m_resolverHookrecord, br, obeyMandatory);
+    }
+
+    public int insertHostedCapability(List<BundleCapability> caps, HostedCapability hc)
+    {
+        int idx = Collections.binarySearch(caps, hc, new CandidateComparator());
+        if (idx < 0)
+        {
+            idx = Math.abs(idx + 1);
+        }
+        caps.add(idx, hc);
+        return idx;
+    }
+
+    public boolean isEffective(BundleRequirement br)
+    {
+        return m_state.isEffective(br);
+    }
+
+    public Map<BundleRevision, BundleWiring> getWirings()
+    {
+        return m_wirings;
+    }
+
+    public void checkNativeLibraries(BundleRevision rev) throws ResolveException
+    {
+        m_state.checkNativeLibraries(rev);
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/felix/framework/ServiceRegistrationImpl.java b/src/main/java/org/apache/felix/framework/ServiceRegistrationImpl.java
new file mode 100644
index 0000000..ce35e18
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/ServiceRegistrationImpl.java
@@ -0,0 +1,732 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework;
+
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.*;
+
+import org.apache.felix.framework.util.MapToDictionary;
+import org.apache.felix.framework.util.StringMap;
+import org.apache.felix.framework.util.Util;
+import org.apache.felix.framework.wiring.BundleCapabilityImpl;
+import org.osgi.framework.*;
+import org.osgi.framework.BundleReference;
+import org.osgi.framework.wiring.BundleCapability;
+import org.osgi.framework.wiring.BundleRevision;
+import org.osgi.framework.wiring.BundleWire;
+
+class ServiceRegistrationImpl implements ServiceRegistration
+{
+    // Service registry.
+    private final ServiceRegistry m_registry;
+    // Bundle providing the service.
+    private final Bundle m_bundle;
+    // Interfaces associated with the service object.
+    private final String[] m_classes;
+    // Service Id associated with the service object.
+    private final Long m_serviceId;
+    // Service object.
+    private volatile Object m_svcObj;
+    // Service factory interface.
+    private volatile ServiceFactory m_factory;
+    // Associated property dictionary.
+    private volatile Map m_propMap = new StringMap();
+    // Re-usable service reference.
+    private final ServiceReferenceImpl m_ref;
+    // Flag indicating that we are unregistering.
+    private volatile boolean m_isUnregistering = false;
+
+    public ServiceRegistrationImpl(
+        ServiceRegistry registry, Bundle bundle,
+        String[] classes, Long serviceId,
+        Object svcObj, Dictionary dict)
+    {
+        m_registry = registry;
+        m_bundle = bundle;
+        m_classes = classes;
+        m_serviceId = serviceId;
+        m_svcObj = svcObj;
+        m_factory = (m_svcObj instanceof ServiceFactory)
+            ? (ServiceFactory) m_svcObj : null;
+
+        initializeProperties(dict);
+
+        // This reference is the "standard" reference for this
+        // service and will always be returned by getReference().
+        m_ref = new ServiceReferenceImpl();
+    }
+
+    protected synchronized boolean isValid()
+    {
+        return (m_svcObj != null);
+    }
+
+    protected synchronized void invalidate()
+    {
+        m_svcObj = null;
+    }
+
+    public synchronized ServiceReference getReference()
+    {
+        // Make sure registration is valid.
+        if (!isValid())
+        {
+            throw new IllegalStateException(
+                "The service registration is no longer valid.");
+        }
+        return m_ref;
+    }
+
+    public void setProperties(Dictionary dict)
+    {
+        Map oldProps;
+        synchronized (this)
+        {
+            // Make sure registration is valid.
+            if (!isValid())
+            {
+                throw new IllegalStateException(
+                    "The service registration is no longer valid.");
+            }
+            // Remember old properties.
+            oldProps = m_propMap;
+            // Set the properties.
+            initializeProperties(dict);
+        }
+        // Tell registry about it.
+        m_registry.servicePropertiesModified(this, new MapToDictionary(oldProps));
+    }
+
+    public void unregister()
+    {
+        synchronized (this)
+        {
+            if (!isValid() || m_isUnregistering)
+            {
+                throw new IllegalStateException("Service already unregistered.");
+            }
+            m_isUnregistering = true;
+        }
+        m_registry.unregisterService(m_bundle, this);
+        synchronized (this)
+        {
+            m_svcObj = null;
+            m_factory = null;
+        }
+    }
+
+    //
+    // Utility methods.
+    //
+
+    /**
+     * This method determines if the class loader of the service object
+     * has access to the specified class.
+     * @param clazz the class to test for reachability.
+     * @return <tt>true</tt> if the specified class is reachable from the
+     *         service object's class loader, <tt>false</tt> otherwise.
+    **/
+    private boolean isClassAccessible(Class clazz)
+    {
+        // We need to see if the class loader of the service object has
+        // access to the specified class; however, we may not have a service
+        // object. If we only have service factory, then we will assume two
+        // different scenarios:
+        // 1. The service factory is provided by the bundle providing the
+        //    service.
+        // 2. The service factory is NOT provided by the bundle providing
+        //    the service.
+        // For case 1, we will use the class loader of the service factory
+        // to find the class. For case 2, we will assume we have an extender
+        // at work here and always return true, since we have no real way of
+        // knowing the wiring of the provider unless we actually get the
+        // service object, which defeats the lazy aspect of service factories.
+
+        // Case 2.
+        if ((m_factory != null)
+            && (Felix.m_secureAction.getClassLoader(m_factory.getClass()) instanceof BundleReference)
+            && !((BundleReference) Felix.m_secureAction.getClassLoader(m_factory.getClass())).getBundle().equals(m_bundle))
+        {
+            try
+            {
+                Class providedClazz = m_bundle.loadClass(clazz.getName());
+                if (providedClazz != null)
+                {
+                    return providedClazz == clazz;
+                }
+            }
+            catch (ClassNotFoundException ex)
+            {
+                // Ignore and try interface class loaders.
+            }
+            return true;
+        }
+
+        // Case 1.
+        Class sourceClass = (m_factory != null) ? m_factory.getClass() : m_svcObj.getClass();
+        return Util.loadClassUsingClass(sourceClass, clazz.getName(), Felix.m_secureAction) == clazz;
+    }
+
+    Object getProperty(String key)
+    {
+        return m_propMap.get(key);
+    }
+
+    private String[] getPropertyKeys()
+    {
+        Set s = m_propMap.keySet();
+        return (String[]) s.toArray(new String[s.size()]);
+    }
+
+    private Bundle[] getUsingBundles()
+    {
+        return m_registry.getUsingBundles(m_ref);
+    }
+
+    /**
+     * This method provides direct access to the associated service object;
+     * it generally should not be used by anyone other than the service registry
+     * itself.
+     * @return The service object associated with the registration.
+    **/
+    Object getService()
+    {
+        return m_svcObj;
+    }
+
+    Object getService(Bundle acqBundle)
+    {
+        // If the service object is a service factory, then
+        // let it create the service object.
+        if (m_factory != null)
+        {
+            Object svcObj = null;
+            try
+            {
+                if (System.getSecurityManager() != null)
+                {
+                    svcObj = AccessController.doPrivileged(
+                        new ServiceFactoryPrivileged(acqBundle, null));
+                }
+                else
+                {
+                    svcObj = getFactoryUnchecked(acqBundle);
+                }
+            }
+            catch (PrivilegedActionException ex)
+            {
+                if (ex.getException() instanceof ServiceException)
+                {
+                    throw (ServiceException) ex.getException();
+                }
+                else
+                {
+                    throw new ServiceException(
+                        "Service factory exception: " + ex.getException().getMessage(),
+                        ServiceException.FACTORY_EXCEPTION, ex.getException());
+                }
+            }
+            return svcObj;
+        }
+        else
+        {
+            return m_svcObj;
+        }
+    }
+
+    void ungetService(Bundle relBundle, Object svcObj)
+    {
+        // If the service object is a service factory, then
+        // let it release the service object.
+        if (m_factory != null)
+        {
+            try
+            {
+                if (System.getSecurityManager() != null)
+                {
+                    AccessController.doPrivileged(
+                        new ServiceFactoryPrivileged(relBundle, svcObj));
+                }
+                else
+                {
+                    ungetFactoryUnchecked(relBundle, svcObj);
+                }
+            }
+            catch (Exception ex)
+            {
+                m_registry.getLogger().log(
+                    m_bundle,
+                    Logger.LOG_ERROR,
+                    "ServiceRegistrationImpl: Error ungetting service.",
+                    ex);
+            }
+        }
+    }
+
+    private void initializeProperties(Dictionary dict)
+    {
+        // Create a case-insensitive map for the properties.
+        Map props = new StringMap();
+
+        if (dict != null)
+        {
+            // Make sure there are no duplicate keys.
+            Enumeration keys = dict.keys();
+            while (keys.hasMoreElements())
+            {
+                Object key = keys.nextElement();
+                if (props.get(key) == null)
+                {
+                    props.put(key, dict.get(key));
+                }
+                else
+                {
+                    throw new IllegalArgumentException("Duplicate service property: " + key);
+                }
+            }
+        }
+
+        // Add the framework assigned properties.
+        props.put(Constants.OBJECTCLASS, m_classes);
+        props.put(Constants.SERVICE_ID, m_serviceId);
+
+        // Update the service property map.
+        m_propMap = props;
+    }
+
+    private Object getFactoryUnchecked(Bundle bundle)
+    {
+        Object svcObj = null;
+        try
+        {
+            svcObj = m_factory.getService(bundle, this);
+        }
+        catch (Throwable th)
+        {
+            throw new ServiceException(
+                "Service factory exception: " + th.getMessage(),
+                ServiceException.FACTORY_EXCEPTION, th);
+        }
+        if (svcObj != null)
+        {
+            for (int i = 0; i < m_classes.length; i++)
+            {
+                Class clazz = Util.loadClassUsingClass(
+                    svcObj.getClass(), m_classes[i], Felix.m_secureAction);
+                if ((clazz == null) || !clazz.isAssignableFrom(svcObj.getClass()))
+                {
+                    if (clazz == null)
+                    {
+                        throw new ServiceException(
+                            "Service cannot be cast due to missing class: " + m_classes[i],
+                            ServiceException.FACTORY_ERROR);
+                    }
+                    else
+                    {
+                        throw new ServiceException(
+                            "Service cannot be cast: " + m_classes[i],
+                            ServiceException.FACTORY_ERROR);
+                    }
+                }
+            }
+        }
+        else
+        {
+            throw new ServiceException(
+                "Service factory returned null.", ServiceException.FACTORY_ERROR);
+        }
+        return svcObj;
+    }
+
+    private void ungetFactoryUnchecked(Bundle bundle, Object svcObj)
+    {
+        m_factory.ungetService(bundle, this, svcObj);
+    }
+
+    /**
+     * This simple class is used to ensure that when a service factory
+     * is called, that no other classes on the call stack interferes
+     * with the permissions of the factory itself.
+    **/
+    private class ServiceFactoryPrivileged implements PrivilegedExceptionAction
+    {
+        private Bundle m_bundle = null;
+        private Object m_svcObj = null;
+
+        public ServiceFactoryPrivileged(Bundle bundle, Object svcObj)
+        {
+            m_bundle = bundle;
+            m_svcObj = svcObj;
+        }
+
+        public Object run() throws Exception
+        {
+            if (m_svcObj == null)
+            {
+                return getFactoryUnchecked(m_bundle);
+            }
+            else
+            {
+                ungetFactoryUnchecked(m_bundle, m_svcObj);
+            }
+            return null;
+        }
+    }
+
+    //
+    // ServiceReference implementation
+    //
+
+    class ServiceReferenceImpl extends BundleCapabilityImpl implements ServiceReference
+    {
+        private final ServiceReferenceMap m_map;
+
+        private ServiceReferenceImpl()
+        {
+            super(null, null, Collections.EMPTY_MAP, Collections.EMPTY_MAP);
+            m_map = new ServiceReferenceMap();
+        }
+
+        ServiceRegistrationImpl getRegistration()
+        {
+            return ServiceRegistrationImpl.this;
+        }
+
+        //
+        // Capability methods.
+        //
+
+        @Override
+        public BundleRevision getRevision()
+        {
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        @Override
+        public String getNamespace()
+        {
+            return "service-reference";
+        }
+
+        @Override
+        public Map<String, String> getDirectives()
+        {
+            return Collections.EMPTY_MAP;
+        }
+
+        @Override
+        public Map<String, Object> getAttributes()
+        {
+            return m_map;
+        }
+
+        @Override
+        public List<String> getUses()
+        {
+            return Collections.EMPTY_LIST;
+        }
+
+        //
+        // ServiceReference methods.
+        //
+
+        public Object getProperty(String s)
+        {
+            return ServiceRegistrationImpl.this.getProperty(s);
+        }
+
+        public String[] getPropertyKeys()
+        {
+            return ServiceRegistrationImpl.this.getPropertyKeys();
+        }
+
+        public Bundle getBundle()
+        {
+            // The spec says that this should return null if
+            // the service is unregistered.
+            return (isValid()) ? m_bundle : null;
+        }
+
+        public Bundle[] getUsingBundles()
+        {
+            return ServiceRegistrationImpl.this.getUsingBundles();
+        }
+
+        public String toString()
+        {
+            String[] ocs = (String[]) getProperty("objectClass");
+            String oc = "[";
+            for(int i = 0; i < ocs.length; i++)
+            {
+                oc = oc + ocs[i];
+                if (i < ocs.length - 1)
+                    oc = oc + ", ";
+            }
+            oc = oc + "]";
+            return oc;
+        }
+
+        public boolean isAssignableTo(Bundle requester, String className)
+        {
+            // Always return true if the requester is the same as the provider.
+            if (requester == m_bundle)
+            {
+                return true;
+            }
+
+            // Boolean flag.
+            boolean allow = true;
+            // Get the package.
+            String pkgName =
+                Util.getClassPackage(className);
+            // Get package wiring from service requester.
+            BundleRevision requesterRevision = requester.adapt(BundleRevision.class);
+            BundleWire requesterWire = Util.getWire(requesterRevision, pkgName);
+            BundleCapability requesterCap = Util.getPackageCapability(requesterRevision, pkgName);
+            // Get package wiring from service provider.
+            BundleRevision providerRevision = m_bundle.adapt(BundleRevision.class);
+            BundleWire providerWire = Util.getWire(providerRevision, pkgName);
+            BundleCapability providerCap = Util.getPackageCapability(providerRevision, pkgName);
+
+            // There are four situations that may occur here:
+            //   1. Neither the requester, nor provider have wires for the package.
+            //   2. The requester does not have a wire for the package.
+            //   3. The provider does not have a wire for the package.
+            //   4. Both the requester and provider have a wire for the package.
+            // For case 1, if the requester does not have access to the class at
+            // all, we assume it is using reflection and do not filter. If the
+            // requester does have access to the class, then we make sure it is
+            // the same class as the service. For case 2, we do not filter if the
+            // requester is the exporter of the package to which the provider of
+            // the service is wired. Otherwise, as in case 1, if the requester
+            // does not have access to the class at all, we do not filter, but if
+            // it does have access we check if it is the same class accessible to
+            // the providing revision. For case 3, the provider will not have a wire
+            // if it is exporting the package, so we determine if the requester
+            // is wired to it or somehow using the same class. For case 4, we
+            // simply compare the exporting revisions from the package wiring to
+            // determine if we need to filter the service reference.
+
+            // Case 1: Both requester and provider have no wire.
+            if ((requesterWire == null) && (providerWire == null))
+            {
+                // If requester has no access then true, otherwise service
+                // registration must have same class as requester.
+                try
+                {
+                    Class requestClass =
+                        ((BundleWiringImpl) requesterRevision.getWiring())
+                            .getClassByDelegation(className);
+                    allow = getRegistration().isClassAccessible(requestClass);
+                }
+                catch (Exception ex)
+                {
+                    // Requester has no access to the class, so allow it, since
+                    // we assume the requester is using reflection.
+                    allow = true;
+                }
+            }
+            // Case 2: Requester has no wire, but provider does.
+            else if ((requesterWire == null) && (providerWire != null))
+            {
+                // If the requester exports the package, then the provider must
+                // be wired to it.
+                if (requesterCap != null)
+                {
+                    allow = providerWire.getProviderWiring().getRevision().equals(requesterRevision);
+                }
+                // Otherwise, check if the requester has access to the class and,
+                // if so, if it is the same class as the provider.
+                else
+                {
+                    try
+                    {
+                        // Try to load class from requester.
+                        Class requestClass =((BundleWiringImpl)
+                            requesterRevision.getWiring()).getClassByDelegation(className);
+                        try
+                        {
+                            // If requester has access to the class, verify it is the
+                            // same class as the provider.
+                            allow = (((BundleWiringImpl)
+                                providerRevision.getWiring())
+                                    .getClassByDelegation(className) == requestClass);
+                        }
+                        catch (Exception ex)
+                        {
+                            allow = false;
+                        }
+                    }
+                    catch (Exception ex)
+                    {
+                        // Requester has no access to the class, so allow it, since
+                        // we assume the requester is using reflection.
+                        allow = true;
+                    }
+                }
+            }
+            // Case 3: Requester has a wire, but provider does not.
+            else if ((requesterWire != null) && (providerWire == null))
+            {
+                // If the provider exports the package, then the requester must
+                // be wired to it.
+                if (providerCap != null)
+                {
+                    allow = requesterWire.getProviderWiring().getRevision().equals(providerRevision);
+                }
+                // If the provider is not the exporter of the requester's package,
+                // then try to use the service registration to see if the requester's
+                // class is accessible.
+                else
+                {
+                    try
+                    {
+                        // Load the class from the requesting bundle.
+                        Class requestClass = ((BundleWiringImpl)
+                            requesterRevision.getWiring())
+                                .getClassByDelegation(className);
+                        // Get the service registration and ask it to check
+                        // if the service object is assignable to the requesting
+                        // bundle's class.
+                        allow = getRegistration().isClassAccessible(requestClass);
+                    }
+                    catch (Exception ex)
+                    {
+                        // Filter to be safe.
+                        allow = false;
+                    }
+                }
+            }
+            // Case 4: Both requester and provider have a wire.
+            else
+            {
+                // Include service reference if the wires have the
+                // same source revision.
+                allow = providerWire.getProviderWiring().getRevision()
+                    .equals(requesterWire.getProviderWiring().getRevision());
+            }
+
+            return allow;
+        }
+
+        public int compareTo(Object reference)
+        {
+            ServiceReference other = (ServiceReference) reference;
+
+            Long id = (Long) getProperty(Constants.SERVICE_ID);
+            Long otherId = (Long) other.getProperty(Constants.SERVICE_ID);
+
+            if (id.equals(otherId))
+            {
+                return 0; // same service
+            }
+
+            Object rankObj = getProperty(Constants.SERVICE_RANKING);
+            Object otherRankObj = other.getProperty(Constants.SERVICE_RANKING);
+
+            // If no rank, then spec says it defaults to zero.
+            rankObj = (rankObj == null) ? new Integer(0) : rankObj;
+            otherRankObj = (otherRankObj == null) ? new Integer(0) : otherRankObj;
+
+            // If rank is not Integer, then spec says it defaults to zero.
+            Integer rank = (rankObj instanceof Integer)
+                ? (Integer) rankObj : new Integer(0);
+            Integer otherRank = (otherRankObj instanceof Integer)
+                ? (Integer) otherRankObj : new Integer(0);
+
+            // Sort by rank in ascending order.
+            if (rank.compareTo(otherRank) < 0)
+            {
+                return -1; // lower rank
+            }
+            else if (rank.compareTo(otherRank) > 0)
+            {
+                return 1; // higher rank
+            }
+
+            // If ranks are equal, then sort by service id in descending order.
+            return (id.compareTo(otherId) < 0) ? 1 : -1;
+        }
+    }
+
+    private class ServiceReferenceMap implements Map
+    {
+        public int size()
+        {
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        public boolean isEmpty()
+        {
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        public boolean containsKey(Object o)
+        {
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        public boolean containsValue(Object o)
+        {
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        public Object get(Object o)
+        {
+            return ServiceRegistrationImpl.this.getProperty((String) o);
+        }
+
+        public Object put(Object k, Object v)
+        {
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        public Object remove(Object o)
+        {
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        public void putAll(Map map)
+        {
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        public void clear()
+        {
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        public Set<Object> keySet()
+        {
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        public Collection<Object> values()
+        {
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        public Set<Entry<Object, Object>> entrySet()
+        {
+            return Collections.EMPTY_SET;
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/felix/framework/ServiceRegistry.java b/src/main/java/org/apache/felix/framework/ServiceRegistry.java
new file mode 100644
index 0000000..46c880b
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/ServiceRegistry.java
@@ -0,0 +1,818 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework;
+
+import java.util.*;
+
+import org.apache.felix.framework.capabilityset.CapabilitySet;
+import org.apache.felix.framework.capabilityset.SimpleFilter;
+import org.apache.felix.framework.wiring.BundleCapabilityImpl;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceException;
+import org.osgi.framework.ServiceFactory;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.framework.wiring.BundleCapability;
+
+public class ServiceRegistry
+{
+    private final Logger m_logger;
+    private long m_currentServiceId = 1L;
+    // Maps bundle to an array of service registrations.
+    private final Map m_regsMap = Collections.synchronizedMap(new HashMap());
+    // Capability set for all service registrations.
+    private final CapabilitySet m_regCapSet;
+
+    // Maps registration to thread to keep track when a
+    // registration is in use, which will cause other
+    // threads to wait.
+    private final Map m_lockedRegsMap = new HashMap();
+    // Maps bundle to an array of usage counts.
+    private final Map m_inUseMap = new HashMap();
+
+    private final ServiceRegistryCallbacks m_callbacks;
+
+    private final WeakHashMap<ServiceReference, ServiceReference> m_blackList =
+        new WeakHashMap<ServiceReference, ServiceReference>();
+
+    private final static Class<?>[] m_hookClasses = {
+        org.osgi.framework.hooks.bundle.CollisionHook.class,
+        org.osgi.framework.hooks.bundle.FindHook.class,
+        org.osgi.framework.hooks.bundle.EventHook.class,
+        org.osgi.framework.hooks.service.EventHook.class,
+        org.osgi.framework.hooks.service.EventListenerHook.class,
+        org.osgi.framework.hooks.service.FindHook.class,
+        org.osgi.framework.hooks.service.ListenerHook.class,
+        org.osgi.framework.hooks.weaving.WeavingHook.class,
+        org.osgi.framework.hooks.resolver.ResolverHookFactory.class,
+        org.osgi.service.url.URLStreamHandlerService.class,
+        java.net.ContentHandler.class
+    };
+    private final Map<Class<?>, Set<ServiceReference<?>>> m_allHooks =
+        new HashMap<Class<?>, Set<ServiceReference<?>>>();
+
+    public ServiceRegistry(Logger logger, ServiceRegistryCallbacks callbacks)
+    {
+        m_logger = logger;
+        m_callbacks = callbacks;
+
+        List indices = new ArrayList();
+        indices.add(Constants.OBJECTCLASS);
+        m_regCapSet = new CapabilitySet(indices, false);
+    }
+
+    public ServiceReference[] getRegisteredServices(Bundle bundle)
+    {
+        ServiceRegistration[] regs = (ServiceRegistration[]) m_regsMap.get(bundle);
+        if (regs != null)
+        {
+            List refs = new ArrayList(regs.length);
+            for (int i = 0; i < regs.length; i++)
+            {
+                try
+                {
+                    refs.add(regs[i].getReference());
+                }
+                catch (IllegalStateException ex)
+                {
+                    // Don't include the reference as it is not valid anymore
+                }
+            }
+            return (ServiceReference[]) refs.toArray(new ServiceReference[refs.size()]);
+        }
+        return null;
+    }
+
+    // Caller is expected to fire REGISTERED event.
+    public ServiceRegistration registerService(
+        BundleContext context, String[] classNames, Object svcObj, Dictionary dict)
+    {
+        ServiceRegistrationImpl reg = null;
+
+        synchronized (this)
+        {
+            Bundle bundle = context.getBundle();
+
+            // Create the service registration.
+            reg = new ServiceRegistrationImpl(
+                this, bundle, classNames, new Long(m_currentServiceId++), svcObj, dict);
+
+            // Keep track of registered hooks.
+            addHooks(classNames, svcObj, reg.getReference());
+
+            // Get the bundles current registered services.
+            ServiceRegistration[] regs = (ServiceRegistration[]) m_regsMap.get(bundle);
+            m_regsMap.put(bundle, addServiceRegistration(regs, reg));
+            m_regCapSet.addCapability((BundleCapabilityImpl) reg.getReference());
+        }
+
+        return reg;
+    }
+
+    public void unregisterService(Bundle bundle, ServiceRegistration reg)
+    {
+        // If this is a hook, it should be removed.
+        removeHook(reg.getReference());
+
+        synchronized (this)
+        {
+            // Note that we don't lock the service registration here using
+            // the m_lockedRegsMap because we want to allow bundles to get
+            // the service during the unregistration process. However, since
+            // we do remove the registration from the service registry, no
+            // new bundles will be able to look up the service.
+
+            // Now remove the registered service.
+            ServiceRegistration[] regs = (ServiceRegistration[]) m_regsMap.get(bundle);
+            m_regsMap.put(bundle, removeServiceRegistration(regs, reg));
+            m_regCapSet.removeCapability((BundleCapabilityImpl) reg.getReference());
+        }
+
+        // Notify callback objects about unregistering service.
+        if (m_callbacks != null)
+        {
+            m_callbacks.serviceChanged(
+                new ServiceEvent(ServiceEvent.UNREGISTERING, reg.getReference()), null);
+        }
+
+        // Now forcibly unget the service object for all stubborn clients.
+        ServiceReference ref = reg.getReference();
+        Bundle[] clients = getUsingBundles(ref);
+        for (int i = 0; (clients != null) && (i < clients.length); i++)
+        {
+            while (ungetService(clients[i], reg.getReference()))
+                ; // Keep removing until it is no longer possible
+        }
+        // Invalidate registration
+        ((ServiceRegistrationImpl) reg).invalidate();
+        // Bundles are allowed to get a reference while unregistering
+        for (int i = 0; (clients != null) && (i < clients.length); i++)
+        {
+            while (ungetService(clients[i], ref))
+                ; // Keep removing until it is no longer possible
+        }
+    }
+
+    /**
+     * This method retrieves all services registrations for the specified
+     * bundle and invokes <tt>ServiceRegistration.unregister()</tt> on each
+     * one. This method is only called be the framework to clean up after
+     * a stopped bundle.
+     * @param bundle the bundle whose services should be unregistered.
+    **/
+    public void unregisterServices(Bundle bundle)
+    {
+        // Simply remove all service registrations for the bundle.
+        ServiceRegistration[] regs = null;
+        synchronized (this)
+        {
+            regs = (ServiceRegistration[]) m_regsMap.get(bundle);
+        }
+
+        // Note, there is no race condition here with respect to the
+        // bundle registering more services, because its bundle context
+        // has already been invalidated by this point, so it would not
+        // be able to register more services.
+
+        // Unregister each service.
+        for (int i = 0; (regs != null) && (i < regs.length); i++)
+        {
+            if (((ServiceRegistrationImpl) regs[i]).isValid())
+            {
+                try
+                {
+                    regs[i].unregister();
+                }
+                catch (IllegalStateException e)
+                {
+                    // Ignore exception if the service has already been unregistered
+                }
+            }
+        }
+
+        // Now remove the bundle itself.
+        synchronized (this)
+        {
+            m_regsMap.remove(bundle);
+        }
+    }
+
+    public synchronized List getServiceReferences(String className, SimpleFilter filter)
+    {
+        if ((className == null) && (filter == null))
+        {
+            // Return all services.
+            filter = new SimpleFilter(Constants.OBJECTCLASS, "*", SimpleFilter.PRESENT);
+        }
+        else if ((className != null) && (filter == null))
+        {
+            // Return services matching the class name.
+            filter = new SimpleFilter(Constants.OBJECTCLASS, className, SimpleFilter.EQ);
+        }
+        else if ((className != null) && (filter != null))
+        {
+            // Return services matching the class name and filter.
+            List filters = new ArrayList(2);
+            filters.add(new SimpleFilter(Constants.OBJECTCLASS, className, SimpleFilter.EQ));
+            filters.add(filter);
+            filter = new SimpleFilter(null, filters, SimpleFilter.AND);
+        }
+        // else just use the specified filter.
+
+        Set<BundleCapability> matches = m_regCapSet.match(filter, false);
+
+        return new ArrayList(matches);
+    }
+
+    public synchronized ServiceReference[] getServicesInUse(Bundle bundle)
+    {
+        UsageCount[] usages = (UsageCount[]) m_inUseMap.get(bundle);
+        if (usages != null)
+        {
+            ServiceReference[] refs = new ServiceReference[usages.length];
+            for (int i = 0; i < refs.length; i++)
+            {
+                refs[i] = usages[i].m_ref;
+            }
+            return refs;
+        }
+        return null;
+    }
+
+    public <S> S getService(Bundle bundle, ServiceReference<S> ref)
+    {
+        UsageCount usage = null;
+        Object svcObj = null;
+
+        // Get the service registration.
+        ServiceRegistrationImpl reg =
+            ((ServiceRegistrationImpl.ServiceReferenceImpl) ref).getRegistration();
+
+        synchronized (this)
+        {
+            // First make sure that no existing operation is currently
+            // being performed by another thread on the service registration.
+            for (Object o = m_lockedRegsMap.get(reg); (o != null); o = m_lockedRegsMap.get(reg))
+            {
+                // We don't allow cycles when we call out to the service factory.
+                if (o.equals(Thread.currentThread()))
+                {
+                    throw new ServiceException(
+                        "ServiceFactory.getService() resulted in a cycle.",
+                        ServiceException.FACTORY_ERROR,
+                        null);
+                }
+
+                // Otherwise, wait for it to be freed.
+                try
+                {
+                    wait();
+                }
+                catch (InterruptedException ex)
+                {
+                }
+            }
+
+            // Lock the service registration.
+            m_lockedRegsMap.put(reg, Thread.currentThread());
+
+            // Make sure the service registration is still valid.
+            if (reg.isValid())
+            {
+                // Get the usage count, if any.
+                usage = getUsageCount(bundle, ref);
+
+                // If we don't have a usage count, then create one and
+                // since the spec says we increment usage count before
+                // actually getting the service object.
+                if (usage == null)
+                {
+                    usage = addUsageCount(bundle, ref);
+                }
+
+                // Increment the usage count and grab the already retrieved
+                // service object, if one exists.
+                usage.m_count++;
+                svcObj = usage.m_svcObj;
+            }
+        }
+
+        // If we have a usage count, but no service object, then we haven't
+        // cached the service object yet, so we need to create one now without
+        // holding the lock, since we will potentially call out to a service
+        // factory.
+        try
+        {
+            if ((usage != null) && (svcObj == null))
+            {
+                svcObj = reg.getService(bundle);
+            }
+        }
+        finally
+        {
+            // If we successfully retrieved a service object, then we should
+            // cache it in the usage count. If not, we should flush the usage
+            // count. Either way, we need to unlock the service registration
+            // so that any threads waiting for it can continue.
+            synchronized (this)
+            {
+                // Before caching the service object, double check to see if
+                // the registration is still valid, since it may have been
+                // unregistered while we didn't hold the lock.
+                if (!reg.isValid() || (svcObj == null))
+                {
+                    flushUsageCount(bundle, ref);
+                }
+                else
+                {
+                    usage.m_svcObj = svcObj;
+                }
+                m_lockedRegsMap.remove(reg);
+                notifyAll();
+            }
+        }
+
+        return (S) svcObj;
+    }
+
+    public boolean ungetService(Bundle bundle, ServiceReference ref)
+    {
+        UsageCount usage = null;
+        ServiceRegistrationImpl reg =
+            ((ServiceRegistrationImpl.ServiceReferenceImpl) ref).getRegistration();
+
+        synchronized (this)
+        {
+            // First make sure that no existing operation is currently
+            // being performed by another thread on the service registration.
+            for (Object o = m_lockedRegsMap.get(reg); (o != null); o = m_lockedRegsMap.get(reg))
+            {
+                // We don't allow cycles when we call out to the service factory.
+                if (o.equals(Thread.currentThread()))
+                {
+                    throw new IllegalStateException(
+                        "ServiceFactory.ungetService() resulted in a cycle.");
+                }
+
+                // Otherwise, wait for it to be freed.
+                try
+                {
+                    wait();
+                }
+                catch (InterruptedException ex)
+                {
+                }
+            }
+
+            // Get the usage count.
+            usage = getUsageCount(bundle, ref);
+            // If there is no cached services, then just return immediately.
+            if (usage == null)
+            {
+                return false;
+            }
+
+            // Lock the service registration.
+            m_lockedRegsMap.put(reg, Thread.currentThread());
+        }
+
+        // If usage count will go to zero, then unget the service
+        // from the registration; we do this outside the lock
+        // since this might call out to the service factory.
+        try
+        {
+            if (usage.m_count == 1)
+            {
+                // Remove reference from usages array.
+                ((ServiceRegistrationImpl.ServiceReferenceImpl) ref)
+                    .getRegistration().ungetService(bundle, usage.m_svcObj);
+            }
+        }
+        finally
+        {
+            // Finally, decrement usage count and flush if it goes to zero or
+            // the registration became invalid while we were not holding the
+            // lock. Either way, unlock the service registration so that any
+            // threads waiting for it can continue.
+            synchronized (this)
+            {
+                // Decrement usage count, which spec says should happen after
+                // ungetting the service object.
+                usage.m_count--;
+
+                // If the registration is invalid or the usage count has reached
+                // zero, then flush it.
+                if (!reg.isValid() || (usage.m_count <= 0))
+                {
+                    usage.m_svcObj = null;
+                    flushUsageCount(bundle, ref);
+                }
+
+                // Release the registration lock so any waiting threads can
+                // continue.
+                m_lockedRegsMap.remove(reg);
+                notifyAll();
+            }
+        }
+
+        return true;
+    }
+
+
+    /**
+     * This is a utility method to release all services being
+     * used by the specified bundle.
+     * @param bundle the bundle whose services are to be released.
+    **/
+    public void ungetServices(Bundle bundle)
+    {
+        UsageCount[] usages;
+        synchronized (this)
+        {
+            usages = (UsageCount[]) m_inUseMap.get(bundle);
+        }
+
+        if (usages == null)
+        {
+            return;
+        }
+
+        // Note, there is no race condition here with respect to the
+        // bundle using more services, because its bundle context
+        // has already been invalidated by this point, so it would not
+        // be able to look up more services.
+
+        // Remove each service object from the
+        // service cache.
+        for (int i = 0; i < usages.length; i++)
+        {
+            // Keep ungetting until all usage count is zero.
+            while (ungetService(bundle, usages[i].m_ref))
+            {
+                // Empty loop body.
+            }
+        }
+    }
+
+    public synchronized Bundle[] getUsingBundles(ServiceReference ref)
+    {
+        Bundle[] bundles = null;
+        for (Iterator iter = m_inUseMap.entrySet().iterator(); iter.hasNext(); )
+        {
+            Map.Entry entry = (Map.Entry) iter.next();
+            Bundle bundle = (Bundle) entry.getKey();
+            UsageCount[] usages = (UsageCount[]) entry.getValue();
+            for (int useIdx = 0; useIdx < usages.length; useIdx++)
+            {
+                if (usages[useIdx].m_ref.equals(ref))
+                {
+                    // Add the bundle to the array to be returned.
+                    if (bundles == null)
+                    {
+                        bundles = new Bundle[] { bundle };
+                    }
+                    else
+                    {
+                        Bundle[] nbs = new Bundle[bundles.length + 1];
+                        System.arraycopy(bundles, 0, nbs, 0, bundles.length);
+                        nbs[bundles.length] = bundle;
+                        bundles = nbs;
+                    }
+                }
+            }
+        }
+        return bundles;
+    }
+
+    void servicePropertiesModified(ServiceRegistration reg, Dictionary oldProps)
+    {
+        updateHook(reg.getReference());
+        if (m_callbacks != null)
+        {
+            m_callbacks.serviceChanged(
+                new ServiceEvent(ServiceEvent.MODIFIED, reg.getReference()), oldProps);
+        }
+    }
+
+    public Logger getLogger()
+    {
+        return m_logger;
+    }
+
+    private static ServiceRegistration[] addServiceRegistration(
+        ServiceRegistration[] regs, ServiceRegistration reg)
+    {
+        if (regs == null)
+        {
+            regs = new ServiceRegistration[] { reg };
+        }
+        else
+        {
+            ServiceRegistration[] newRegs = new ServiceRegistration[regs.length + 1];
+            System.arraycopy(regs, 0, newRegs, 0, regs.length);
+            newRegs[regs.length] = reg;
+            regs = newRegs;
+        }
+        return regs;
+    }
+
+    private static ServiceRegistration[] removeServiceRegistration(
+        ServiceRegistration[] regs, ServiceRegistration reg)
+    {
+        for (int i = 0; (regs != null) && (i < regs.length); i++)
+        {
+            if (regs[i].equals(reg))
+            {
+                // If this is the only usage, then point to empty list.
+                if ((regs.length - 1) == 0)
+                {
+                    regs = new ServiceRegistration[0];
+                }
+                // Otherwise, we need to do some array copying.
+                else
+                {
+                    ServiceRegistration[] newRegs = new ServiceRegistration[regs.length - 1];
+                    System.arraycopy(regs, 0, newRegs, 0, i);
+                    if (i < newRegs.length)
+                    {
+                        System.arraycopy(
+                            regs, i + 1, newRegs, i, newRegs.length - i);
+                    }
+                    regs = newRegs;
+                }
+            }
+        }
+        return regs;
+    }
+
+    /**
+     * Utility method to retrieve the specified bundle's usage count for the
+     * specified service reference.
+     * @param bundle The bundle whose usage counts are being searched.
+     * @param ref The service reference to find in the bundle's usage counts.
+     * @return The associated usage count or null if not found.
+    **/
+    private UsageCount getUsageCount(Bundle bundle, ServiceReference ref)
+    {
+        UsageCount[] usages = (UsageCount[]) m_inUseMap.get(bundle);
+        for (int i = 0; (usages != null) && (i < usages.length); i++)
+        {
+            if (usages[i].m_ref.equals(ref))
+            {
+                return usages[i];
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Utility method to update the specified bundle's usage count array to
+     * include the specified service. This method should only be called
+     * to add a usage count for a previously unreferenced service. If the
+     * service already has a usage count, then the existing usage count
+     * counter simply needs to be incremented.
+     * @param bundle The bundle acquiring the service.
+     * @param ref The service reference of the acquired service.
+     * @param svcObj The service object of the acquired service.
+    **/
+    private UsageCount addUsageCount(Bundle bundle, ServiceReference ref)
+    {
+        UsageCount[] usages = (UsageCount[]) m_inUseMap.get(bundle);
+
+        UsageCount usage = new UsageCount();
+        usage.m_ref = ref;
+
+        if (usages == null)
+        {
+            usages = new UsageCount[] { usage };
+        }
+        else
+        {
+            UsageCount[] newUsages = new UsageCount[usages.length + 1];
+            System.arraycopy(usages, 0, newUsages, 0, usages.length);
+            newUsages[usages.length] = usage;
+            usages = newUsages;
+        }
+
+        m_inUseMap.put(bundle, usages);
+
+        return usage;
+    }
+
+    /**
+     * Utility method to flush the specified bundle's usage count for the
+     * specified service reference. This should be called to completely
+     * remove the associated usage count object for the specified service
+     * reference. If the goal is to simply decrement the usage, then get
+     * the usage count and decrement its counter. This method will also
+     * remove the specified bundle from the "in use" map if it has no more
+     * usage counts after removing the usage count for the specified service
+     * reference.
+     * @param bundle The bundle whose usage count should be removed.
+     * @param ref The service reference whose usage count should be removed.
+    **/
+    private void flushUsageCount(Bundle bundle, ServiceReference ref)
+    {
+        UsageCount[] usages = (UsageCount[]) m_inUseMap.get(bundle);
+        for (int i = 0; (usages != null) && (i < usages.length); i++)
+        {
+            if (usages[i].m_ref.equals(ref))
+            {
+                // If this is the only usage, then point to empty list.
+                if ((usages.length - 1) == 0)
+                {
+                    usages = null;
+                }
+                // Otherwise, we need to do some array copying.
+                else
+                {
+                    UsageCount[] newUsages = new UsageCount[usages.length - 1];
+                    System.arraycopy(usages, 0, newUsages, 0, i);
+                    if (i < newUsages.length)
+                    {
+                        System.arraycopy(
+                            usages, i + 1, newUsages, i, newUsages.length - i);
+                    }
+                    usages = newUsages;
+                }
+            }
+        }
+
+        if (usages != null)
+        {
+            m_inUseMap.put(bundle, usages);
+        }
+        else
+        {
+            m_inUseMap.remove(bundle);
+        }
+    }
+
+    //
+    // Hook-related methods.
+    //
+
+    boolean isHookBlackListed(ServiceReference sr)
+    {
+        return m_blackList.containsKey(sr);
+    }
+
+    void blackListHook(ServiceReference sr)
+    {
+        m_blackList.put(sr, sr);
+    }
+
+    static boolean isHook(String[] classNames, Class<?> hookClass, Object svcObj)
+    {
+        // For a service factory, we can only match names.
+        if (svcObj instanceof ServiceFactory)
+        {
+            for (String className : classNames)
+            {
+                if (className.equals(hookClass.getName()))
+                {
+                    return true;
+                }
+            }
+        }
+
+        // For a service object, check if its class matches.
+        if (hookClass.isAssignableFrom(svcObj.getClass()))
+        {
+            // But still only if it is registered under that interface.
+            String hookName = hookClass.getName();
+            for (String className : classNames)
+            {
+                if (className.equals(hookName))
+                {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private void addHooks(String[] classNames, Object svcObj, ServiceReference<?> ref)
+    {
+        for (Class<?> hookClass : m_hookClasses)
+        {
+            if (isHook(classNames, hookClass, svcObj))
+            {
+                synchronized (m_allHooks)
+                {
+                    Set<ServiceReference<?>> hooks = m_allHooks.get(hookClass);
+                    if (hooks == null)
+                    {
+                        hooks = new TreeSet<ServiceReference<?>>(Collections.reverseOrder());
+                        m_allHooks.put(hookClass, hooks);
+                    }
+                    hooks.add(ref);
+                }
+            }
+        }
+    }
+
+    private void updateHook(ServiceReference ref)
+    {
+        // We maintain the hooks sorted, so if ranking has changed for example,
+        // we need to ensure the order remains correct by resorting the hooks.
+        Object svcObj = ((ServiceRegistrationImpl.ServiceReferenceImpl) ref)
+                .getRegistration().getService();
+        String [] classNames = (String[]) ref.getProperty(Constants.OBJECTCLASS);
+
+        for (Class<?> hookClass : m_hookClasses)
+        {
+            if (isHook(classNames, hookClass, svcObj))
+            {
+                synchronized (m_allHooks)
+                {
+                    Set<ServiceReference<?>> hooks = m_allHooks.get(hookClass);
+                    if (hooks != null)
+                    {
+                        List<ServiceReference<?>> refs = new ArrayList<ServiceReference<?>>(hooks);
+                        hooks.clear();
+                        hooks.addAll(refs);
+                    }
+                }
+            }
+        }
+    }
+
+    private void removeHook(ServiceReference ref)
+    {
+        Object svcObj = ((ServiceRegistrationImpl.ServiceReferenceImpl) ref)
+            .getRegistration().getService();
+        String [] classNames = (String[]) ref.getProperty(Constants.OBJECTCLASS);
+
+        for (Class<?> hookClass : m_hookClasses)
+        {
+            if (isHook(classNames, hookClass, svcObj))
+            {
+                synchronized (m_allHooks)
+                {
+                    Set<ServiceReference<?>> hooks = m_allHooks.get(hookClass);
+                    if (hooks != null)
+                    {
+                        hooks.remove(ref);
+                        if (hooks.isEmpty())
+                        {
+                            m_allHooks.remove(hookClass);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    public <S> Set<ServiceReference<S>> getHooks(Class<S> hookClass)
+    {
+        synchronized (m_allHooks)
+        {
+            Set<ServiceReference<?>> hooks = m_allHooks.get(hookClass);
+            if (hooks != null)
+            {
+                SortedSet sorted = new TreeSet<ServiceReference<?>>(Collections.reverseOrder());
+                sorted.addAll(hooks);
+                return asTypedSortedSet(sorted);
+            }
+            return Collections.EMPTY_SET;
+        }
+    }
+
+    private static <S> SortedSet<ServiceReference<S>> asTypedSortedSet(
+        SortedSet<ServiceReference<?>> ss)
+    {
+        return (SortedSet<ServiceReference<S>>) (SortedSet) ss;
+    }
+
+    private static class UsageCount
+    {
+        public int m_count = 0;
+        public ServiceReference m_ref = null;
+        public Object m_svcObj = null;
+    }
+
+    public interface ServiceRegistryCallbacks
+    {
+        void serviceChanged(ServiceEvent event, Dictionary oldProps);
+    }
+}
diff --git a/src/main/java/org/apache/felix/framework/StartLevelImpl.java b/src/main/java/org/apache/felix/framework/StartLevelImpl.java
new file mode 100644
index 0000000..274ef7e
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/StartLevelImpl.java
@@ -0,0 +1,103 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework;
+
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.startlevel.BundleStartLevel;
+import org.osgi.framework.startlevel.FrameworkStartLevel;
+import org.osgi.service.startlevel.StartLevel;
+
+/**
+ * StartLevel service implementation.
+ * @author <a href="mailto:dev at felix.apache.org">Felix Project Team</a>
+**/
+public class StartLevelImpl implements StartLevel
+{
+    private final Felix m_felix;
+
+    StartLevelImpl(Felix felix)
+    {
+        m_felix = felix;
+    }
+
+    /* (non-Javadoc)
+     * @see org.osgi.service.startlevel.StartLevel#getStartLevel()
+    **/
+    public int getStartLevel()
+    {
+        return m_felix.adapt(FrameworkStartLevel.class).getStartLevel();
+    }
+
+    /* (non-Javadoc)
+     * @see org.osgi.service.startlevel.StartLevel#setStartLevel(int)
+    **/
+    public void setStartLevel(int startlevel)
+    {
+        m_felix.adapt(FrameworkStartLevel.class).setStartLevel(startlevel);
+    }
+
+    /* (non-Javadoc)
+     * @see org.osgi.service.startlevel.StartLevel#getBundleStartLevel(org.osgi.framework.Bundle)
+    **/
+    public int getBundleStartLevel(Bundle bundle)
+    {
+        return bundle.adapt(BundleStartLevel.class).getStartLevel();
+    }
+
+    /* (non-Javadoc)
+     * @see org.osgi.service.startlevel.StartLevel#setBundleStartLevel(org.osgi.framework.Bundle, int)
+    **/
+    public void setBundleStartLevel(Bundle bundle, int startlevel)
+    {
+        bundle.adapt(BundleStartLevel.class).setStartLevel(startlevel);
+    }
+
+    /* (non-Javadoc)
+     * @see org.osgi.service.startlevel.StartLevel#getInitialBundleStartLevel()
+    **/
+    public int getInitialBundleStartLevel()
+    {
+        return m_felix.adapt(FrameworkStartLevel.class).getInitialBundleStartLevel();
+    }
+
+    /* (non-Javadoc)
+     * @see org.osgi.service.startlevel.StartLevel#setInitialBundleStartLevel(int)
+    **/
+    public void setInitialBundleStartLevel(int startlevel)
+    {
+        m_felix.adapt(FrameworkStartLevel.class).setInitialBundleStartLevel(startlevel);
+    }
+
+    /* (non-Javadoc)
+     * @see org.osgi.service.startlevel.StartLevel#isBundlePersistentlyStarted(org.osgi.framework.Bundle)
+    **/
+    public boolean isBundlePersistentlyStarted(Bundle bundle)
+    {
+        return bundle.adapt(BundleStartLevel.class).isPersistentlyStarted();
+    }
+
+    /* (non-Javadoc)
+     * @see org.osgi.service.startlevel.StartLevel#isBundleActivationPolicyUsed(org.osgi.framework.Bundle)
+    **/
+	public boolean isBundleActivationPolicyUsed(Bundle bundle)
+    {
+        return bundle.adapt(BundleStartLevel.class).isActivationPolicyUsed();
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/felix/framework/StatefulResolver.java b/src/main/java/org/apache/felix/framework/StatefulResolver.java
new file mode 100644
index 0000000..6c6e75f
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/StatefulResolver.java
@@ -0,0 +1,1654 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.NoSuchElementException;
+import java.util.Set;
+import java.util.StringTokenizer;
+import org.apache.felix.framework.capabilityset.CapabilitySet;
+import org.apache.felix.framework.capabilityset.SimpleFilter;
+import org.apache.felix.framework.resolver.CandidateComparator;
+import org.apache.felix.framework.resolver.ResolveException;
+import org.apache.felix.framework.resolver.Resolver;
+import org.apache.felix.framework.resolver.ResolverImpl;
+import org.apache.felix.framework.resolver.ResolverWire;
+import org.apache.felix.framework.util.ShrinkableCollection;
+import org.apache.felix.framework.util.Util;
+import org.apache.felix.framework.util.manifestparser.R4Library;
+import org.apache.felix.framework.wiring.BundleCapabilityImpl;
+import org.apache.felix.framework.wiring.BundleRequirementImpl;
+import org.apache.felix.framework.wiring.BundleWireImpl;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.BundlePermission;
+import org.osgi.framework.CapabilityPermission;
+import org.osgi.framework.Constants;
+import org.osgi.framework.PackagePermission;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.hooks.resolver.ResolverHook;
+import org.osgi.framework.hooks.resolver.ResolverHookFactory;
+import org.osgi.framework.wiring.BundleCapability;
+import org.osgi.framework.wiring.BundleRequirement;
+import org.osgi.framework.wiring.BundleRevision;
+import org.osgi.framework.wiring.BundleWire;
+import org.osgi.framework.wiring.BundleWiring;
+
+class StatefulResolver
+{
+    private final Logger m_logger;
+    private final Felix m_felix;
+    private final Resolver m_resolver;
+    private boolean m_isResolving = false;
+
+    // Set of all revisions.
+    private final Set<BundleRevision> m_revisions;
+    // Set of all fragments.
+    private final Set<BundleRevision> m_fragments;
+    // Capability sets.
+    private final Map<String, CapabilitySet> m_capSets;
+    // Maps singleton symbolic names to list of bundle revisions sorted by version.
+    private final Map<String, List<BundleRevision>> m_singletons;
+    // Selected singleton bundle revisions.
+    private final Set<BundleRevision> m_selectedSingletons;
+    // Execution environment.
+    private final String m_fwkExecEnvStr;
+    // Parsed framework environments
+    private final Set<String> m_fwkExecEnvSet;
+
+    StatefulResolver(Felix felix)
+    {
+        m_felix = felix;
+        m_logger = m_felix.getLogger();
+        m_resolver = new ResolverImpl(m_logger);
+
+        m_revisions = new HashSet<BundleRevision>();
+        m_fragments = new HashSet<BundleRevision>();
+        m_capSets = new HashMap<String, CapabilitySet>();
+        m_singletons = new HashMap<String, List<BundleRevision>>();
+        m_selectedSingletons = new HashSet<BundleRevision>();
+
+        String fwkExecEnvStr =
+            (String) m_felix.getConfig().get(Constants.FRAMEWORK_EXECUTIONENVIRONMENT);
+        m_fwkExecEnvStr = (fwkExecEnvStr != null) ? fwkExecEnvStr.trim() : null;
+        m_fwkExecEnvSet = parseExecutionEnvironments(fwkExecEnvStr);
+
+        List<String> indices = new ArrayList<String>();
+        indices.add(BundleRevision.BUNDLE_NAMESPACE);
+        m_capSets.put(BundleRevision.BUNDLE_NAMESPACE, new CapabilitySet(indices, true));
+
+        indices = new ArrayList<String>();
+        indices.add(BundleRevision.PACKAGE_NAMESPACE);
+        m_capSets.put(BundleRevision.PACKAGE_NAMESPACE, new CapabilitySet(indices, true));
+
+        indices = new ArrayList<String>();
+        indices.add(BundleRevision.HOST_NAMESPACE);
+        m_capSets.put(BundleRevision.HOST_NAMESPACE,  new CapabilitySet(indices, true));
+    }
+
+    synchronized void addRevision(BundleRevision br)
+    {
+        // Always attempt to remove the revision, since
+        // this method can be used for re-indexing a revision
+        // after it has been resolved.
+        removeRevision(br);
+
+        m_revisions.add(br);
+
+        // Add singletons to the singleton map.
+        boolean isSingleton = Util.isSingleton(br);
+        if (isSingleton)
+        {
+            // Index the new singleton.
+            addToSingletonMap(m_singletons, br);
+        }
+
+        // We always need to index non-singleton bundle capabilities, but
+        // singleton bundles only need to be index if they are resolved.
+        // Unresolved singleton capabilities are only indexed before a
+        // resolve operation when singleton selection is performed.
+        if (!isSingleton || (br.getWiring() != null))
+        {
+            if (Util.isFragment(br))
+            {
+                m_fragments.add(br);
+            }
+            indexCapabilities(br);
+        }
+    }
+
+    synchronized void removeRevision(BundleRevision br)
+    {
+        if (m_revisions.remove(br))
+        {
+            m_fragments.remove(br);
+            deindexCapabilities(br);
+
+            // If this module is a singleton, then remove it from the
+            // singleton map.
+            List<BundleRevision> revisions = m_singletons.get(br.getSymbolicName());
+            if (revisions != null)
+            {
+                revisions.remove(br);
+                if (revisions.isEmpty())
+                {
+                    m_singletons.remove(br.getSymbolicName());
+                }
+            }
+        }
+    }
+
+    boolean isEffective(BundleRequirement req)
+    {
+        String effective = req.getDirectives().get(Constants.EFFECTIVE_DIRECTIVE);
+        return ((effective == null) || effective.equals(Constants.EFFECTIVE_RESOLVE));
+    }
+
+    synchronized List<BundleCapability> findProviders(
+        BundleRequirement req, boolean obeyMandatory)
+    {
+        ResolverHookRecord record = new ResolverHookRecord(
+            Collections.<ServiceReference<ResolverHookFactory>, ResolverHook>emptyMap(), null);
+        return findProvidersInternal(record, req, obeyMandatory);
+    }
+
+    synchronized List<BundleCapability> findProvidersInternal(
+        ResolverHookRecord record, BundleRequirement req, boolean obeyMandatory)
+    {
+        List<BundleCapability> result = new ArrayList<BundleCapability>();
+
+        CapabilitySet capSet = m_capSets.get(req.getNamespace());
+        if (capSet != null)
+        {
+            // Get the requirement's filter; if this is our own impl we
+            // have a shortcut to get the already parsed filter, otherwise
+            // we must parse it from the directive.
+            SimpleFilter sf;
+            if (req instanceof BundleRequirementImpl)
+            {
+                sf = ((BundleRequirementImpl) req).getFilter();
+            }
+            else
+            {
+                String filter = req.getDirectives().get(Constants.FILTER_DIRECTIVE);
+                if (filter == null)
+                {
+                    sf = new SimpleFilter(null, null, SimpleFilter.MATCH_ALL);
+                }
+                else
+                {
+                    sf = SimpleFilter.parse(filter);
+                }
+            }
+
+            // Find the matching candidates.
+            Set<BundleCapability> matches = capSet.match(sf, obeyMandatory);
+            // Filter matching candidates.
+            for (BundleCapability cap : matches)
+            {
+                // Filter according to security.
+                if (filteredBySecurity(req, cap))
+                {
+                    continue;
+                }
+                // Filter already resolved hosts, since we don't support
+                // dynamic attachment of fragments.
+                if (req.getNamespace().equals(BundleRevision.HOST_NAMESPACE)
+                    && (cap.getRevision().getWiring() != null))
+                {
+                    continue;
+                }
+
+                result.add(cap);
+            }
+        }
+
+        // If we have resolver hooks, then we may need to filter our results
+        // based on a whitelist and/or fine-grained candidate filtering.
+        if (!result.isEmpty() && !record.getResolverHookRefs().isEmpty())
+        {
+            // It we have a whitelist, then first filter out candidates
+            // from disallowed revisions.
+            if (record.getBundleRevisionWhitelist() != null)
+            {
+                for (Iterator<BundleCapability> it = result.iterator(); it.hasNext(); )
+                {
+                    if (!record.getBundleRevisionWhitelist().contains(it.next().getRevision()))
+                    {
+                        it.remove();
+                    }
+                }
+            }
+
+            // Now give the hooks a chance to do fine-grained filtering.
+            ShrinkableCollection<BundleCapability> shrinkable =
+                new ShrinkableCollection<BundleCapability>(result);
+            for (ResolverHook hook : record.getResolverHooks())
+            {
+                try
+                {
+                    Felix.m_secureAction
+                        .invokeResolverHookMatches(hook, req, shrinkable);
+                }
+                catch (Throwable th)
+                {
+                    m_logger.log(Logger.LOG_WARNING, "Resolver hook exception.", th);
+                }
+            }
+        }
+
+        Collections.sort(result, new CandidateComparator());
+
+        return result;
+    }
+
+    private boolean filteredBySecurity(BundleRequirement req, BundleCapability cap)
+    {
+        if (System.getSecurityManager() != null)
+        {
+            BundleRevisionImpl reqRevision = (BundleRevisionImpl) req.getRevision();
+
+            if (req.getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE))
+            {
+                if (!((BundleProtectionDomain) ((BundleRevisionImpl) cap.getRevision()).getProtectionDomain()).impliesDirect(
+                    new PackagePermission((String) cap.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE),
+                    PackagePermission.EXPORTONLY)) ||
+                    !((reqRevision == null) ||
+                        ((BundleProtectionDomain) reqRevision.getProtectionDomain()).impliesDirect(
+                            new PackagePermission((String) cap.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE),
+                            cap.getRevision().getBundle(),PackagePermission.IMPORT))
+                    ))
+                {
+                    if (reqRevision != cap.getRevision())
+                    {
+                        return true;
+                    }
+                }
+            }
+            else if (req.getNamespace().equals(BundleRevision.BUNDLE_NAMESPACE))
+            {   if (!((BundleProtectionDomain) ((BundleRevisionImpl) cap.getRevision()).getProtectionDomain()).impliesDirect(
+                    new BundlePermission(cap.getRevision().getSymbolicName(), BundlePermission.PROVIDE)) ||
+                    !((reqRevision == null) ||
+                        ((BundleProtectionDomain) reqRevision.getProtectionDomain()).impliesDirect(
+                            new BundlePermission(reqRevision.getSymbolicName(), BundlePermission.REQUIRE))
+                    ))
+                {
+                    return true;
+                }
+            }
+            else if (req.getNamespace().equals(BundleRevision.HOST_NAMESPACE))
+            {
+                if (!((BundleProtectionDomain) reqRevision.getProtectionDomain())
+                    .impliesDirect(new BundlePermission(
+                        reqRevision.getSymbolicName(),
+                        BundlePermission.FRAGMENT))
+                || !((BundleProtectionDomain) ((BundleRevisionImpl) cap.getRevision()).getProtectionDomain())
+                    .impliesDirect(new BundlePermission(
+                        cap.getRevision().getSymbolicName(),
+                        BundlePermission.HOST)))
+                {
+                    return true;
+                }
+            }
+            else  if (!req.getNamespace().equals("osgi.ee"))
+            {
+                if (!((BundleProtectionDomain) ((BundleRevisionImpl) cap.getRevision()).getProtectionDomain()).impliesDirect(
+                    new CapabilityPermission(req.getNamespace(), CapabilityPermission.PROVIDE))
+                    ||
+                    !((reqRevision == null) || ((BundleProtectionDomain) reqRevision.getProtectionDomain()).impliesDirect(
+                    new CapabilityPermission(req.getNamespace(), cap.getAttributes(), cap.getRevision().getBundle(), CapabilityPermission.REQUIRE))))
+                {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    void resolve(
+        Set<BundleRevision> mandatory,
+        Set<BundleRevision> optional)
+        throws ResolveException, BundleException
+    {
+        // Acquire global lock.
+        boolean locked = m_felix.acquireGlobalLock();
+        if (!locked)
+        {
+            throw new ResolveException(
+                "Unable to acquire global lock for resolve.", null, null);
+        }
+
+        // Make sure we are not already resolving, which can be
+        // the case if a resolver hook does something bad.
+        if (m_isResolving)
+        {
+            m_felix.releaseGlobalLock();
+            throw new IllegalStateException("Nested resolve operations not allowed.");
+        }
+        m_isResolving = true;
+
+        Map<BundleRevision, List<ResolverWire>> wireMap = null;
+        try
+        {
+            // Make our own copy of revisions.
+            mandatory = (mandatory.isEmpty())
+                ? mandatory : new HashSet<BundleRevision>(mandatory);
+            optional = (optional.isEmpty())
+                ? optional : new HashSet<BundleRevision>(optional);
+
+            // Prepare resolver hooks, if any.
+            ResolverHookRecord record = prepareResolverHooks(mandatory, optional);
+
+            // Select any singletons in the resolver state.
+            selectSingletons(record);
+
+            // Extensions are resolved differently.
+            for (Iterator<BundleRevision> it = mandatory.iterator(); it.hasNext(); )
+            {
+                BundleRevision br = it.next();
+                BundleImpl bundle = (BundleImpl) br.getBundle();
+                if (bundle.isExtension())
+                {
+                    it.remove();
+                }
+                else if (Util.isSingleton(br) && !isSelectedSingleton(br))
+                {
+                    throw new ResolveException("Singleton conflict.", br, null);
+                }
+            }
+            for (Iterator<BundleRevision> it = optional.iterator(); it.hasNext(); )
+            {
+                BundleRevision br = it.next();
+                BundleImpl bundle = (BundleImpl) br.getBundle();
+                if (bundle.isExtension())
+                {
+                    it.remove();
+                }
+                else if (Util.isSingleton(br) && !isSelectedSingleton(br))
+                {
+                    it.remove();
+                }
+            }
+
+            // Catch any resolve exception to rethrow later because
+            // we may need to call end() on resolver hooks.
+            ResolveException rethrow = null;
+            try
+            {
+                // Resolve the revision.
+                wireMap = m_resolver.resolve(
+                    new ResolveContextImpl(
+                        this,
+                        getWirings(),
+                        record,
+                        mandatory,
+                        optional,
+                        getFragments()));
+            }
+            catch (ResolveException ex)
+            {
+                rethrow = ex;
+            }
+
+            // Release resolver hooks, if any.
+            releaseResolverHooks(record);
+
+            // If the resolve failed, rethrow the exception.
+            if (rethrow != null)
+            {
+                throw rethrow;
+            }
+
+            // Otherwise, mark all revisions as resolved.
+            markResolvedRevisions(wireMap);
+        }
+        finally
+        {
+            // Clear resolving flag.
+            m_isResolving = false;
+            // Always release the global lock.
+            m_felix.releaseGlobalLock();
+        }
+
+        fireResolvedEvents(wireMap);
+    }
+
+    BundleRevision resolve(BundleRevision revision, String pkgName)
+        throws ResolveException, BundleException
+    {
+        BundleRevision provider = null;
+
+        // We cannot dynamically import if the revision is not already resolved
+        // or if it is not allowed, so check that first. Note: We check if the
+        // dynamic import is allowed without holding any locks, but this is
+        // okay since the resolver will double check later after we have
+        // acquired the global lock below.
+        if ((revision.getWiring() != null) && isAllowedDynamicImport(revision, pkgName))
+        {
+            // Acquire global lock.
+            boolean locked = m_felix.acquireGlobalLock();
+            if (!locked)
+            {
+                throw new ResolveException(
+                    "Unable to acquire global lock for resolve.", revision, null);
+            }
+
+            // Make sure we are not already resolving, which can be
+            // the case if a resolver hook does something bad.
+            if (m_isResolving)
+            {
+                m_felix.releaseGlobalLock();
+                throw new IllegalStateException("Nested resolve operations not allowed.");
+            }
+            m_isResolving = true;
+
+            Map<BundleRevision, List<ResolverWire>> wireMap = null;
+            try
+            {
+                // Double check to make sure that someone hasn't beaten us to
+                // dynamically importing the package, which can happen if two
+                // threads are racing to do so. If we have an existing wire,
+                // then just return it instead.
+                provider = ((BundleWiringImpl) revision.getWiring())
+                    .getImportedPackageSource(pkgName);
+                if (provider == null)
+                {
+                    // Prepare resolver hooks, if any.
+                    ResolverHookRecord record =
+                        prepareResolverHooks(
+                            Collections.singleton(revision), Collections.EMPTY_SET);
+
+                    // Select any singletons in the resolver state.
+                    selectSingletons(record);
+
+                    // Catch any resolve exception to rethrow later because
+                    // we may need to call end() on resolver hooks.
+                    ResolveException rethrow = null;
+                    try
+                    {
+                        wireMap = m_resolver.resolve(
+                            new ResolveContextImpl(
+                                this,
+                                getWirings(),
+                                record,
+                                Collections.EMPTY_LIST,
+                                Collections.EMPTY_LIST,
+                                getFragments()),
+                            revision, pkgName);
+                    }
+                    catch (ResolveException ex)
+                    {
+                        rethrow = ex;
+                    }
+
+                    // Release resolver hooks, if any.
+                    releaseResolverHooks(record);
+
+                    // If the resolve failed, rethrow the exception.
+                    if (rethrow != null)
+                    {
+                        throw rethrow;
+                    }
+
+                    if ((wireMap != null) && wireMap.containsKey(revision))
+                    {
+                        List<ResolverWire> dynamicWires = wireMap.remove(revision);
+                        ResolverWire dynamicWire = dynamicWires.get(0);
+
+                        // Mark all revisions as resolved.
+                        markResolvedRevisions(wireMap);
+
+                        // Dynamically add new wire to importing revision.
+                        if (dynamicWire != null)
+                        {
+                            BundleWire bw = new BundleWireImpl(
+                                dynamicWire.getRequirer(),
+                                dynamicWire.getRequirement(),
+                                dynamicWire.getProvider(),
+                                dynamicWire.getCapability());
+
+                            m_felix.getDependencies().addDependent(bw);
+
+                            ((BundleWiringImpl) revision.getWiring()).addDynamicWire(bw);
+
+                            m_felix.getLogger().log(
+                                Logger.LOG_DEBUG,
+                                "DYNAMIC WIRE: " + dynamicWire);
+
+                            provider = ((BundleWiringImpl) revision.getWiring())
+                                .getImportedPackageSource(pkgName);
+                        }
+                    }
+                }
+            }
+            finally
+            {
+                // Clear resolving flag.
+                m_isResolving = false;
+                // Always release the global lock.
+                m_felix.releaseGlobalLock();
+            }
+
+            fireResolvedEvents(wireMap);
+        }
+
+        return provider;
+    }
+
+    private ResolverHookRecord prepareResolverHooks(
+        Set<BundleRevision> mandatory, Set<BundleRevision> optional)
+        throws BundleException
+    {
+        // This map maps the hook factory service to the actual hook objects. It
+        // needs to be a map that preserves insertion order to ensure that we call 
+        // hooks in the correct order.
+        // The hooks are added in the order that m_felix.getHooks() returns them which
+        // is also the order in which they should be called.
+        Map<ServiceReference<ResolverHookFactory>, ResolverHook> hookMap =
+            new LinkedHashMap<ServiceReference<ResolverHookFactory>, ResolverHook>();
+
+        // Get resolver hook factories.
+        Set<ServiceReference<ResolverHookFactory>> hookRefs =
+            m_felix.getHooks(ResolverHookFactory.class);
+        Collection<BundleRevision> whitelist;
+
+        if (!hookRefs.isEmpty())
+        {
+            // Create triggers list.
+            Set<BundleRevision> triggers;
+            if (!mandatory.isEmpty() && !optional.isEmpty())
+            {
+                triggers = new HashSet<BundleRevision>(mandatory);
+                triggers.addAll(optional);
+            }
+            else
+            {
+                triggers = (mandatory.isEmpty())
+                    ? optional : mandatory;
+            }
+            triggers = Collections.unmodifiableSet(triggers);
+
+            BundleException rethrow = null;
+            
+            // Create resolver hook objects by calling begin() on factory.
+            for (ServiceReference<ResolverHookFactory> ref : hookRefs)
+            {
+                try
+                {
+                    ResolverHookFactory rhf = m_felix.getService(m_felix, ref);
+                    if (rhf != null)
+                    {
+                        ResolverHook hook =
+                            Felix.m_secureAction
+                                .invokeResolverHookFactory(rhf, triggers);
+                        if (hook != null)
+                        {
+                            hookMap.put(ref, hook);
+                        }
+                    }
+                }
+                catch (Throwable ex)
+                {
+                    rethrow = new BundleException(
+                        "Resolver hook exception: " + ex.getMessage(),
+                        BundleException.REJECTED_BY_HOOK,
+                        ex);
+                    // Resolver hook spec: if there is an exception during the resolve operation; abort.
+                    // So we break here to make sure that no further resolver hooks are created.
+                    break; 
+                }
+            }
+
+            if (rethrow != null)
+            {
+                for (ResolverHook hook : hookMap.values())
+                {
+                    try
+                    {
+                        Felix.m_secureAction.invokeResolverHookEnd(hook);
+                    }
+                    catch (Exception ex)
+                    {
+                        rethrow = new BundleException(
+                                "Resolver hook exception: " + ex.getMessage(),
+                                BundleException.REJECTED_BY_HOOK,
+                                ex);
+                    }
+                }
+
+                throw rethrow;
+            }
+
+            // Ask hooks to indicate which revisions should not be resolved.
+            whitelist = new ShrinkableCollection<BundleRevision>(getUnresolvedRevisions());
+            int originalSize = whitelist.size();
+            for (ResolverHook hook : hookMap.values())
+            {
+                try
+                {
+                    Felix.m_secureAction
+                        .invokeResolverHookResolvable(hook, whitelist);
+                }
+                catch (Throwable ex)
+                {
+                    rethrow = new BundleException(
+                        "Resolver hook exception: " + ex.getMessage(),
+                        BundleException.REJECTED_BY_HOOK,
+                        ex);
+                    // Resolver hook spec: if there is an exception during the resolve operation; abort.
+                    // So we break here to make sure that no further resolver operations are executed.
+                    break; 
+                }
+            }
+
+            if (rethrow != null)
+            {
+                for (ResolverHook hook : hookMap.values())
+                {
+                    try
+                    {
+                        Felix.m_secureAction.invokeResolverHookEnd(hook);
+                    }
+                    catch (Exception ex)
+                    {
+                        rethrow = new BundleException(
+                                "Resolver hook exception: " + ex.getMessage(),
+                                BundleException.REJECTED_BY_HOOK,
+                                ex);
+                    }
+                }
+
+                throw rethrow;
+            }
+
+            // If nothing was removed, then just null the whitelist
+            // as an optimization.
+            if (whitelist.size() == originalSize)
+            {
+                whitelist = null;
+            }
+
+            // Check to make sure the target revisions are allowed to resolve.
+            if (whitelist != null)
+            {
+                // We only need to check this for the non-dynamic import
+                // case. The dynamic import case will only have one resolved
+                // trigger revision in the mandatory set, so ignore that case.
+                if (mandatory.isEmpty()
+                    || !optional.isEmpty()
+                    || (mandatory.iterator().next().getWiring() == null))
+                {
+                    mandatory.retainAll(whitelist);
+                    optional.retainAll(whitelist);
+                    if (mandatory.isEmpty() && optional.isEmpty())
+                    {
+                        throw new ResolveException(
+                            "Resolver hook prevented resolution.", null, null);
+                    }
+                }
+            }
+        }
+        else
+        {
+            whitelist = null;
+        }
+
+        return new ResolverHookRecord(hookMap, whitelist);
+    }
+
+    private void releaseResolverHooks(ResolverHookRecord record)
+        throws BundleException
+    {
+        // If we have resolver hooks, we must call end() on them.
+        if (!record.getResolverHookRefs().isEmpty())
+        {
+            // Verify that all resolver hook service references are still valid
+            // Call end() on resolver hooks.
+            for (ResolverHook hook : record.getResolverHooks())
+            {
+                try
+                {
+                    Felix.m_secureAction.invokeResolverHookEnd(hook);
+                }
+                catch (Throwable th)
+                {
+                    m_logger.log(
+                        Logger.LOG_WARNING, "Resolver hook exception.", th);
+                }
+            }
+            // Verify that all hook service references are still valid
+            // and unget all resolver hook factories.
+            boolean invalid = false;
+            for (ServiceReference<ResolverHookFactory> ref : record.getResolverHookRefs())
+            {
+                if (ref.getBundle() == null)
+                {
+                    invalid = true;
+                }
+                m_felix.ungetService(m_felix, ref);
+            }
+            if (invalid)
+            {
+                throw new BundleException(
+                    "Resolver hook service unregistered during resolve.",
+                    BundleException.REJECTED_BY_HOOK);
+            }
+        }
+    }
+
+    // This method duplicates a lot of logic from:
+    // ResolverImpl.getDynamicImportCandidates()
+    // This is only a rough check since it doesn't include resolver hooks.
+    boolean isAllowedDynamicImport(BundleRevision revision, String pkgName)
+    {
+        // Unresolved revisions cannot dynamically import, nor can the default
+        // package be dynamically imported.
+        if ((revision.getWiring() == null) || pkgName.length() == 0)
+        {
+            return false;
+        }
+
+        // If the revision doesn't have dynamic imports, then just return
+        // immediately.
+        List<BundleRequirement> dynamics =
+            Util.getDynamicRequirements(revision.getWiring().getRequirements(null));
+        if ((dynamics == null) || dynamics.isEmpty())
+        {
+            return false;
+        }
+
+        // If the revision exports this package, then we cannot
+        // attempt to dynamically import it.
+        for (BundleCapability cap : revision.getWiring().getCapabilities(null))
+        {
+            if (cap.getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE)
+                && cap.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE).equals(pkgName))
+            {
+                return false;
+            }
+        }
+
+        // If this revision already imports or requires this package, then
+        // we cannot dynamically import it.
+        if (((BundleWiringImpl) revision.getWiring()).hasPackageSource(pkgName))
+        {
+            return false;
+        }
+
+        // Loop through the importer's dynamic requirements to determine if
+        // there is a matching one for the package from which we want to
+        // load a class.
+        Map<String, Object> attrs = Collections.singletonMap(
+            BundleRevision.PACKAGE_NAMESPACE, (Object) pkgName);
+        BundleRequirementImpl req = new BundleRequirementImpl(
+            revision,
+            BundleRevision.PACKAGE_NAMESPACE,
+            Collections.EMPTY_MAP,
+            attrs);
+        List<BundleCapability> candidates = findProviders(req, false);
+
+        // Try to find a dynamic requirement that matches the capabilities.
+        BundleRequirementImpl dynReq = null;
+        for (int dynIdx = 0;
+            (candidates.size() > 0) && (dynReq == null) && (dynIdx < dynamics.size());
+            dynIdx++)
+        {
+            for (Iterator<BundleCapability> itCand = candidates.iterator();
+                (dynReq == null) && itCand.hasNext(); )
+            {
+                BundleCapability cap = itCand.next();
+                if (CapabilitySet.matches(
+                    (BundleCapabilityImpl) cap,
+                    ((BundleRequirementImpl) dynamics.get(dynIdx)).getFilter()))
+                {
+                    dynReq = (BundleRequirementImpl) dynamics.get(dynIdx);
+                }
+            }
+        }
+
+        // If we found a matching dynamic requirement, then filter out
+        // any candidates that do not match it.
+        if (dynReq != null)
+        {
+            for (Iterator<BundleCapability> itCand = candidates.iterator();
+                itCand.hasNext(); )
+            {
+                BundleCapability cap = itCand.next();
+                if (!CapabilitySet.matches(
+                    (BundleCapabilityImpl) cap, dynReq.getFilter()))
+                {
+                    itCand.remove();
+                }
+            }
+        }
+        else
+        {
+            candidates.clear();
+        }
+
+        return !candidates.isEmpty();
+    }
+
+    private void markResolvedRevisions(Map<BundleRevision, List<ResolverWire>> wireMap)
+        throws ResolveException
+    {
+        boolean debugLog = m_felix.getLogger().getLogLevel() >= Logger.LOG_DEBUG;
+
+        // DO THIS IN THREE PASSES:
+        // 1. Aggregate fragments per host.
+        // 2. Attach wires and fragments to hosts.
+        //    -> If fragments fail to attach, then undo.
+        // 3. Mark hosts and fragments as resolved.
+
+        // First pass.
+        if (wireMap != null)
+        {
+            // First pass: Loop through the wire map to find the host wires
+            // for any fragments and map a host to all of its fragments.
+            Map<BundleRevision, List<BundleRevision>> hosts =
+                new HashMap<BundleRevision, List<BundleRevision>>();
+            for (Entry<BundleRevision, List<ResolverWire>> entry : wireMap.entrySet())
+            {
+                BundleRevision revision = entry.getKey();
+                List<ResolverWire> wires = entry.getValue();
+
+                if (Util.isFragment(revision))
+                {
+                    for (Iterator<ResolverWire> itWires = wires.iterator();
+                        itWires.hasNext(); )
+                    {
+                        ResolverWire w = itWires.next();
+                        List<BundleRevision> fragments = hosts.get(w.getProvider());
+                        if (fragments == null)
+                        {
+                            fragments = new ArrayList<BundleRevision>();
+                            hosts.put(w.getProvider(), fragments);
+                        }
+                        fragments.add(w.getRequirer());
+                    }
+                }
+            }
+
+            // Second pass: Loop through the wire map to do three things:
+            // 1) convert resolver wires to bundle wires 2) create wiring
+            // objects for revisions and 3) record dependencies among
+            // revisions. We don't actually set the wirings here because
+            // that indicates that a revision is resolved and we don't want
+            // to mark anything as resolved unless we succussfully create
+            // all wirings.
+            Map<BundleRevision, BundleWiringImpl> wirings =
+                new HashMap<BundleRevision, BundleWiringImpl>(wireMap.size());
+            for (Entry<BundleRevision, List<ResolverWire>> entry : wireMap.entrySet())
+            {
+                BundleRevision revision = entry.getKey();
+                List<ResolverWire> resolverWires = entry.getValue();
+
+                List<BundleWire> bundleWires =
+                    new ArrayList<BundleWire>(resolverWires.size());
+
+                // Need to special case fragments since they may already have
+                // wires if they are already attached to another host; if that
+                // is the case, then we want to merge the old host wires with
+                // the new ones.
+                if ((revision.getWiring() != null) && Util.isFragment(revision))
+                {
+                    // Fragments only have host wires, so just add them all.
+                    bundleWires.addAll(revision.getWiring().getRequiredWires(null));
+                }
+
+                // Loop through resolver wires to calculate the package
+                // space implied by the wires as well as to record the
+                // dependencies.
+                Map<String, BundleRevision> importedPkgs =
+                    new HashMap<String, BundleRevision>();
+                Map<String, List<BundleRevision>> requiredPkgs =
+                    new HashMap<String, List<BundleRevision>>();
+                for (ResolverWire rw : resolverWires)
+                {
+                    BundleWire bw = new BundleWireImpl(
+                        rw.getRequirer(),
+                        rw.getRequirement(),
+                        rw.getProvider(),
+                        rw.getCapability());
+                    bundleWires.add(bw);
+
+                    if (Util.isFragment(revision))
+                    {
+                        if (debugLog)
+                        {
+                            m_felix.getLogger().log(
+                                Logger.LOG_DEBUG,
+                                "FRAGMENT WIRE: " + rw.toString());
+                        }
+                    }
+                    else
+                    {
+                        if (debugLog)
+                        {
+                            m_felix.getLogger().log(Logger.LOG_DEBUG, "WIRE: " + rw.toString());
+                        }
+
+                        if (rw.getCapability().getNamespace()
+                            .equals(BundleRevision.PACKAGE_NAMESPACE))
+                        {
+                            importedPkgs.put(
+                                (String) rw.getCapability().getAttributes()
+                                    .get(BundleRevision.PACKAGE_NAMESPACE),
+                                rw.getProvider());
+                        }
+                        else if (rw.getCapability().getNamespace()
+                            .equals(BundleRevision.BUNDLE_NAMESPACE))
+                        {
+                            Set<String> pkgs = calculateExportedAndReexportedPackages(
+                                    rw.getProvider(),
+                                    wireMap,
+                                    new HashSet<String>(),
+                                    new HashSet<BundleRevision>());
+                            for (String pkg : pkgs)
+                            {
+                                List<BundleRevision> revs = requiredPkgs.get(pkg);
+                                if (revs == null)
+                                {
+                                    revs = new ArrayList<BundleRevision>();
+                                    requiredPkgs.put(pkg, revs);
+                                }
+                                revs.add(rw.getProvider());
+                            }
+                        }
+                    }
+                }
+
+                List<BundleRevision> fragments = hosts.get(revision);
+                try
+                {
+                    wirings.put(
+                        revision,
+                        new BundleWiringImpl(
+                            m_felix.getLogger(),
+                            m_felix.getConfig(),
+                            this,
+                            (BundleRevisionImpl) revision,
+                            fragments,
+                            bundleWires,
+                            importedPkgs,
+                            requiredPkgs));
+                }
+                catch (Exception ex)
+                {
+                    // This is a fatal error, so undo everything and
+                    // throw an exception.
+                    for (Entry<BundleRevision, BundleWiringImpl> wiringEntry
+                        : wirings.entrySet())
+                    {
+                        // Dispose of wiring.
+                        try
+                        {
+                            wiringEntry.getValue().dispose();
+                        }
+                        catch (Exception ex2)
+                        {
+                            // We are in big trouble.
+                            RuntimeException rte = new RuntimeException(
+                                "Unable to clean up resolver failure.", ex2);
+                            m_felix.getLogger().log(
+                                Logger.LOG_ERROR,
+                                rte.getMessage(), ex2);
+                            throw rte;
+                        }
+                    }
+
+                    ResolveException re = new ResolveException(
+                        "Unable to resolve " + revision,
+                        revision, null);
+                    re.initCause(ex);
+                    m_felix.getLogger().log(
+                        Logger.LOG_ERROR,
+                        re.getMessage(), ex);
+                    throw re;
+                }
+            }
+
+            // Third pass: Loop through the wire map to mark revision as resolved
+            // and update the resolver state.
+            for (Entry<BundleRevision, BundleWiringImpl> entry : wirings.entrySet())
+            {
+                BundleRevisionImpl revision = (BundleRevisionImpl) entry.getKey();
+
+                // Mark revision as resolved.
+                BundleWiring wiring = entry.getValue();
+                revision.resolve(entry.getValue());
+
+                // Record dependencies.
+                for (BundleWire bw : wiring.getRequiredWires(null))
+                {
+                    m_felix.getDependencies().addDependent(bw);
+                }
+
+                // Reindex the revision's capabilities since its resolved
+                // capabilities could be different than its declared ones
+                // (e.g., due to substitutable exports).
+                addRevision(revision);
+
+                // Update the state of the revision's bundle to resolved as well.
+                markBundleResolved(revision);
+            }
+        }
+    }
+
+    private void markBundleResolved(BundleRevision revision)
+    {
+        // Update the bundle's state to resolved when the
+        // current revision is resolved; just ignore resolve
+        // events for older revisions since this only occurs
+        // when an update is done on an unresolved bundle
+        // and there was no refresh performed.
+        BundleImpl bundle = (BundleImpl) revision.getBundle();
+
+        // Lock the bundle first.
+        try
+        {
+            // Acquire bundle lock.
+            try
+            {
+                m_felix.acquireBundleLock(
+                    bundle, Bundle.INSTALLED | Bundle.RESOLVED | Bundle.ACTIVE);
+            }
+            catch (IllegalStateException ex)
+            {
+                // There is nothing we can do.
+            }
+            if (bundle.adapt(BundleRevision.class) == revision)
+            {
+                if (bundle.getState() != Bundle.INSTALLED)
+                {
+                    m_felix.getLogger().log(bundle,
+                        Logger.LOG_WARNING,
+                        "Received a resolve event for a bundle that has already been resolved.");
+                }
+                else
+                {
+                    m_felix.setBundleStateAndNotify(bundle, Bundle.RESOLVED);
+                }
+            }
+        }
+        finally
+        {
+            m_felix.releaseBundleLock(bundle);
+        }
+    }
+
+    private void fireResolvedEvents(Map<BundleRevision, List<ResolverWire>> wireMap)
+    {
+        if (wireMap != null)
+        {
+            Iterator<Entry<BundleRevision, List<ResolverWire>>> iter =
+                wireMap.entrySet().iterator();
+            // Iterate over the map to fire necessary RESOLVED events.
+            while (iter.hasNext())
+            {
+                Entry<BundleRevision, List<ResolverWire>> entry = iter.next();
+                BundleRevision revision = entry.getKey();
+
+                // Fire RESOLVED events for all fragments.
+                List<BundleRevision> fragments =
+                    Util.getFragments(revision.getWiring());
+                for (int i = 0; i < fragments.size(); i++)
+                {
+                    m_felix.fireBundleEvent(
+                        BundleEvent.RESOLVED, fragments.get(i).getBundle());
+                }
+                m_felix.fireBundleEvent(BundleEvent.RESOLVED, revision.getBundle());
+            }
+        }
+    }
+
+    private static Set<String> calculateExportedAndReexportedPackages(
+        BundleRevision br,
+        Map<BundleRevision, List<ResolverWire>> wireMap,
+        Set<String> pkgs,
+        Set<BundleRevision> cycles)
+    {
+        if (!cycles.contains(br))
+        {
+            cycles.add(br);
+
+            // Add all exported packages.
+            for (BundleCapability cap : br.getDeclaredCapabilities(null))
+            {
+                if (cap.getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE))
+                {
+                    pkgs.add((String)
+                        cap.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE));
+                }
+            }
+
+            // Now check to see if any required bundles are required with reexport
+            // visibility, since we need to include those packages too.
+            if (br.getWiring() == null)
+            {
+                for (ResolverWire rw : wireMap.get(br))
+                {
+                    if (rw.getCapability().getNamespace().equals(
+                        BundleRevision.BUNDLE_NAMESPACE))
+                    {
+                        String dir = rw.getRequirement()
+                            .getDirectives().get(Constants.VISIBILITY_DIRECTIVE);
+                        if ((dir != null) && (dir.equals(Constants.VISIBILITY_REEXPORT)))
+                        {
+                            calculateExportedAndReexportedPackages(
+                                rw.getProvider(),
+                                wireMap,
+                                pkgs,
+                                cycles);
+                        }
+                    }
+                }
+            }
+            else
+            {
+                for (BundleWire bw : br.getWiring().getRequiredWires(null))
+                {
+                    if (bw.getCapability().getNamespace().equals(
+                        BundleRevision.BUNDLE_NAMESPACE))
+                    {
+                        String dir = bw.getRequirement()
+                            .getDirectives().get(Constants.VISIBILITY_DIRECTIVE);
+                        if ((dir != null) && (dir.equals(Constants.VISIBILITY_REEXPORT)))
+                        {
+                            calculateExportedAndReexportedPackages(
+                                bw.getProviderWiring().getRevision(),
+                                wireMap,
+                                pkgs,
+                                cycles);
+                        }
+                    }
+                }
+            }
+        }
+
+        return pkgs;
+    }
+
+    private synchronized void indexCapabilities(BundleRevision br)
+    {
+        List<BundleCapability> caps =
+            (Util.isFragment(br) || (br.getWiring() == null))
+                ? br.getDeclaredCapabilities(null)
+                : br.getWiring().getCapabilities(null);
+        if (caps != null)
+        {
+            for (BundleCapability cap : caps)
+            {
+                // If the capability is from a different revision, then
+                // don't index it since it is a capability from a fragment.
+                // In that case, the fragment capability is still indexed.
+                // It will be the resolver's responsibility to find all
+                // attached hosts for fragments.
+                if (cap.getRevision() == br)
+                {
+                    CapabilitySet capSet = m_capSets.get(cap.getNamespace());
+                    if (capSet == null)
+                    {
+                        capSet = new CapabilitySet(null, true);
+                        m_capSets.put(cap.getNamespace(), capSet);
+                    }
+                    capSet.addCapability(cap);
+                }
+            }
+        }
+    }
+
+    private synchronized void deindexCapabilities(BundleRevision br)
+    {
+        // We only need be concerned with declared capabilities here,
+        // because resolved capabilities will be a subset, since fragment
+        // capabilities are not considered to be part of the host.
+        List<BundleCapability> caps = br.getDeclaredCapabilities(null);
+        if (caps != null)
+        {
+            for (BundleCapability cap : caps)
+            {
+                CapabilitySet capSet = m_capSets.get(cap.getNamespace());
+                if (capSet != null)
+                {
+                    capSet.removeCapability(cap);
+                }
+            }
+        }
+    }
+
+    private synchronized boolean isSelectedSingleton(BundleRevision br)
+    {
+        return m_selectedSingletons.contains(br);
+    }
+
+    private synchronized void selectSingletons(ResolverHookRecord record)
+        throws BundleException
+    {
+        // First deindex any unresolved singletons to make sure
+        // there aren't any available from previous resolves.
+        // Also remove them from the fragment list, for the same
+        // reason.
+        m_selectedSingletons.clear();
+        for (Entry<String, List<BundleRevision>> entry : m_singletons.entrySet())
+        {
+            for (BundleRevision singleton : entry.getValue())
+            {
+                if (singleton.getWiring() == null)
+                {
+                    deindexCapabilities(singleton);
+                    m_fragments.remove(singleton);
+                }
+            }
+        }
+
+        // If no resolver hooks, then use default singleton selection
+        // algorithm, otherwise defer to the resolver hooks.
+        if (record.getResolverHookRefs().isEmpty())
+        {
+            selectDefaultSingletons(record);
+        }
+        else
+        {
+            selectSingletonsUsingHooks(record);
+        }
+    }
+
+    /*
+     * Selects the singleton with the highest version from groupings
+     * based on the symbolic name. No selection is made if the group
+     * already has a resolved singleton.
+     */
+    private void selectDefaultSingletons(ResolverHookRecord record)
+    {
+        // Now select the singletons available for this resolve operation.
+        for (Entry<String, List<BundleRevision>> entry : m_singletons.entrySet())
+        {
+            selectSingleton(record, entry.getValue());
+        }
+    }
+
+    /*
+     * Groups singletons based on resolver hook filtering and then selects
+     * the singleton from each group with the highest version that is in
+     * the resolver hook whitelist. No selection is made if a group already
+     * has a resolved singleton in it.
+     */
+    private void selectSingletonsUsingHooks(ResolverHookRecord record)
+        throws BundleException
+    {
+        // Convert singleton bundle revision map into a map using
+        // bundle capabilities instead, since this is what the resolver
+        // hooks require.
+        Map<BundleCapability, Collection<BundleCapability>> allCollisions
+            = new HashMap<BundleCapability, Collection<BundleCapability>>();
+        for (Entry<String, List<BundleRevision>> entry : m_singletons.entrySet())
+        {
+            Collection<BundleCapability> bundleCaps =
+                new ArrayList<BundleCapability>();
+            for (BundleRevision br : entry.getValue())
+            {
+                List<BundleCapability> caps =
+                    br.getDeclaredCapabilities(BundleRevision.BUNDLE_NAMESPACE);
+                if (!caps.isEmpty())
+                {
+                    bundleCaps.add(caps.get(0));
+                }
+            }
+
+            for (BundleCapability bc : bundleCaps)
+            {
+                Collection<BundleCapability> capCopy =
+                    new ShrinkableCollection<BundleCapability>(
+                        new ArrayList<BundleCapability>(bundleCaps));
+                capCopy.remove(bc);
+                allCollisions.put(bc, capCopy);
+            }
+        }
+
+        // Invoke hooks to allow them to filter singleton collisions.
+        for (ResolverHook hook : record.getResolverHooks())
+        {
+            for (Entry<BundleCapability, Collection<BundleCapability>> entry
+                : allCollisions.entrySet())
+            {
+                try
+                {
+                    Felix.m_secureAction
+                        .invokeResolverHookSingleton(hook, entry.getKey(), entry.getValue());
+                }
+                catch (Throwable ex)
+                {
+                    throw new BundleException(
+                        "Resolver hook exception: " + ex.getMessage(),
+                        BundleException.REJECTED_BY_HOOK,
+                        ex);
+                }
+            }
+        }
+
+        // Create groups according to how the resolver hooks filtered the
+        // collisions.
+        List<List<BundleRevision>> groups = new ArrayList<List<BundleRevision>>();
+        while (!allCollisions.isEmpty())
+        {
+            BundleCapability target = allCollisions.entrySet().iterator().next().getKey();
+            groups.add(groupSingletons(allCollisions, target, new ArrayList<BundleRevision>()));
+        }
+
+        // Now select the singletons available for this resolve operation.
+        for (List<BundleRevision> group : groups)
+        {
+            selectSingleton(record, group);
+        }
+    }
+
+    private List<BundleRevision> groupSingletons(
+        Map<BundleCapability, Collection<BundleCapability>> allCollisions,
+        BundleCapability target, List<BundleRevision> group)
+    {
+        if (!group.contains(target.getRevision()))
+        {
+            // Add the target since it is implicitly part of the group.
+            group.add(target.getRevision());
+
+            // Recursively add the revisions of any singleton's in the
+            // target's collisions.
+            Collection<BundleCapability> collisions = allCollisions.remove(target);
+            for (BundleCapability collision : collisions)
+            {
+                groupSingletons(allCollisions, collision, group);
+            }
+
+            // Need to check the values of other collisions for this target
+            // and add those to the target's group too, since collisions are
+            // treated as two-way relationships. Repeat until there are no
+            // collision groups left that contain the target capability.
+            boolean repeat;
+            do
+            {
+                repeat = false;
+                for (Entry<BundleCapability, Collection<BundleCapability>> entry:
+                    allCollisions.entrySet())
+                {
+                    if (entry.getValue().contains(target))
+                    {
+                        repeat = true;
+                        groupSingletons(allCollisions, entry.getKey(), group);
+                        break;
+                    }
+                }
+            }
+            while (repeat);
+        }
+        return group;
+    }
+
+    /*
+     * Selects the highest bundle revision from the group that is
+     * in the resolver hook whitelist (if there are hooks). No
+     * selection is made if there is an already resolved singleton
+     * in the group, since it is already indexed.
+     */
+    private void selectSingleton(ResolverHookRecord record, List<BundleRevision> singletons)
+    {
+        BundleRevision selected = null;
+        for (BundleRevision singleton : singletons)
+        {
+            // If a singleton is already resolved,
+            // then there is nothing to do.
+            if (singleton.getWiring() != null)
+            {
+                selected = null;
+                break;
+            }
+            // If this singleton is not in the whitelist, then it cannot
+            // be selected. If it is, in can only be selected if it has
+            // a higher version than the currently selected singleton, if
+            // there is one.
+            if (((record.getBundleRevisionWhitelist() == null) || record.getBundleRevisionWhitelist().contains(singleton))
+                && ((selected == null)
+                    || (selected.getVersion().compareTo(singleton.getVersion()) > 0)))
+            {
+                selected = singleton;
+            }
+        }
+        if (selected != null)
+        {
+            // Record the selected singleton.
+            m_selectedSingletons.add(selected);
+            // Index its capabilities.
+            indexCapabilities(selected);
+            // If the selected singleton is a fragment, then
+            // add it to the list of fragments.
+            if (Util.isFragment(selected))
+            {
+                m_fragments.add(selected);
+            }
+        }
+    }
+
+    private synchronized Set<BundleRevision> getFragments()
+    {
+        Set<BundleRevision> fragments = new HashSet(m_fragments);
+        // Filter out any fragments that are not the current revision.
+        for (Iterator<BundleRevision> it = fragments.iterator(); it.hasNext(); )
+        {
+            BundleRevision fragment = it.next();
+            BundleRevision currentFragmentRevision =
+                fragment.getBundle().adapt(BundleRevision.class);
+            if (fragment != currentFragmentRevision)
+            {
+                it.remove();
+            }
+        }
+        return fragments;
+    }
+
+    void checkNativeLibraries(BundleRevision revision) throws ResolveException
+    {
+        // Next, try to resolve any native code, since the revision is
+        // not resolvable if its native code cannot be loaded.
+        List<R4Library> libs = ((BundleRevisionImpl) revision).getDeclaredNativeLibraries();
+        if (libs != null)
+        {
+            String msg = null;
+            // Verify that all native libraries exist in advance; this will
+            // throw an exception if the native library does not exist.
+            for (int libIdx = 0; (msg == null) && (libIdx < libs.size()); libIdx++)
+            {
+                String entryName = libs.get(libIdx).getEntryName();
+                if (entryName != null)
+                {
+                    if (!((BundleRevisionImpl) revision).getContent().hasEntry(entryName))
+                    {
+                        msg = "Native library does not exist: " + entryName;
+                    }
+                }
+            }
+            // If we have a zero-length native library array, then
+            // this means no native library class could be selected
+            // so we should fail to resolve.
+            if (libs.isEmpty())
+            {
+                msg = "No matching native libraries found.";
+            }
+            if (msg != null)
+            {
+                throw new ResolveException(msg, revision, null);
+            }
+        }
+    }
+
+    private synchronized Set<BundleRevision> getUnresolvedRevisions()
+    {
+        Set<BundleRevision> unresolved = new HashSet<BundleRevision>();
+        for (BundleRevision revision : m_revisions)
+        {
+            if (revision.getWiring() == null)
+            {
+                unresolved.add(revision);
+            }
+        }
+        return unresolved;
+    }
+
+    private synchronized Map<BundleRevision, BundleWiring> getWirings()
+    {
+        Map<BundleRevision, BundleWiring> wirings = new HashMap<BundleRevision, BundleWiring>();
+
+        for (BundleRevision revision : m_revisions)
+        {
+            if (revision.getWiring() != null)
+            {
+                wirings.put(revision, revision.getWiring());
+            }
+        }
+        return wirings;
+    }
+
+    /**
+     * Updates the framework wide execution environment string and a cached Set of
+     * execution environment tokens from the comma delimited list specified by the
+     * system variable 'org.osgi.framework.executionenvironment'.
+     * @param fwkExecEnvStr Comma delimited string of provided execution environments
+     * @return the parsed set of execution environments
+    **/
+    private static Set<String> parseExecutionEnvironments(String fwkExecEnvStr)
+    {
+        Set<String> newSet = new HashSet<String>();
+        if (fwkExecEnvStr != null)
+        {
+            StringTokenizer tokens = new StringTokenizer(fwkExecEnvStr, ",");
+            while (tokens.hasMoreTokens())
+            {
+                newSet.add(tokens.nextToken().trim());
+            }
+        }
+        return newSet;
+    }
+
+    private static void addToSingletonMap(
+        Map<String, List<BundleRevision>> singletons, BundleRevision br)
+    {
+        List<BundleRevision> revisions = singletons.get(br.getSymbolicName());
+        if (revisions == null)
+        {
+            revisions = new ArrayList<BundleRevision>();
+        }
+        revisions.add(br);
+        singletons.put(br.getSymbolicName(), revisions);
+    }
+
+    static class ResolverHookRecord
+    {
+        final Map<ServiceReference<ResolverHookFactory>, ResolverHook> m_resolveHookMap;
+        final Collection<BundleRevision> m_brWhitelist;
+ 
+        /** The map passed in must be of an ordered type, so that the iteration order over the values
+         * is predictable.
+         */
+        ResolverHookRecord(Map<ServiceReference<ResolverHookFactory>, ResolverHook> resolveHookMap,
+            Collection<BundleRevision> brWhiteList)
+        {
+            m_resolveHookMap = resolveHookMap;
+            m_brWhitelist = brWhiteList;
+        }
+        
+        Collection<BundleRevision> getBundleRevisionWhitelist() 
+        {
+            return m_brWhitelist;
+        }
+
+        Set<ServiceReference<ResolverHookFactory>> getResolverHookRefs()
+        {
+            return m_resolveHookMap.keySet();
+        }
+
+        // This slightly over the top implementation to obtain the hooks is to ensure that at the point that
+        // the actual hook is obtained, the service is still registered. There are CT tests that unregister
+        // the hook service while iterating over the hooks and expect that the unregistered hook is not called
+        // in that case.
+        Iterable<ResolverHook> getResolverHooks()
+        {
+            return new Iterable<ResolverHook>()
+            {
+                public Iterator<ResolverHook> iterator()
+                {
+                    return new Iterator<ResolverHook>()
+                    {
+                        private Iterator<Map.Entry<ServiceReference<ResolverHookFactory>, ResolverHook>> it =
+                            m_resolveHookMap.entrySet().iterator();
+                        private Entry<ServiceReference<ResolverHookFactory>, ResolverHook> next = null;
+
+                        public boolean hasNext()
+                        {
+                            if (next == null)
+                                findNext();
+
+                            return next != null;
+                        }
+
+                        public ResolverHook next()
+                        {
+                            if (next == null)
+                                findNext();
+
+                            if (next == null)
+                                throw new NoSuchElementException();
+
+                            ResolverHook hook = next.getValue();
+                            next = null;
+                            return hook;
+                        }
+
+                        // Find the next hook on the iterator, but only if the service is still registered.
+                        // If the service has since been unregistered, skip the hook.
+                        private void findNext()
+                        {
+                            while (it.hasNext())
+                            {
+                                next = it.next();
+                                if (next.getKey().getBundle() != null)
+                                    return;
+                                else
+                                    next = null;
+                            }
+                        }
+
+                        public void remove()
+                        {
+                            throw new UnsupportedOperationException();
+                        }
+                    };
+                }
+            };
+        }
+    }
+}
diff --git a/src/main/java/org/apache/felix/framework/URLHandlers.java b/src/main/java/org/apache/felix/framework/URLHandlers.java
new file mode 100644
index 0000000..dfe0828
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/URLHandlers.java
@@ -0,0 +1,744 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework;
+
+import java.net.ContentHandler;
+import java.net.ContentHandlerFactory;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.URLStreamHandler;
+import java.net.URLStreamHandlerFactory;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.StringTokenizer;
+
+import org.apache.felix.framework.util.FelixConstants;
+import org.apache.felix.framework.util.SecureAction;
+import org.apache.felix.framework.util.SecurityManagerEx;
+import org.osgi.service.url.URLStreamHandlerService;
+
+/**
+ * <p>
+ * This class is a singleton and implements the stream and content handler
+ * factories for all framework instances executing within the JVM. Any
+ * calls to retrieve stream or content handlers is routed through this class
+ * and it acts as a multiplexer for all framework instances. To achieve this,
+ * all framework instances register with this class when they are created so
+ * that it can maintain a centralized registry of instances.
+ * </p>
+ * <p>
+ * When this class receives a request for a stream or content handler, it
+ * always returns a proxy handler instead of only returning a proxy if a
+ * handler currently exists. This approach is used for three reasons:
+ * </p>
+ * <ol>
+ *   <li>Potential caching behavior by the JVM of stream handlers does not give
+ *       you a second chance to provide a handler.
+ *   </li>
+ *   <li>Due to the dynamic nature of OSGi services, handlers may appear at
+ *       any time, so always creating a proxy makes sense.
+ *   </li>
+ *   <li>Since these handler factories service all framework instances,
+ *       some instances may have handlers and others may not, so returning
+ *       a proxy is the only answer that makes sense.
+ *   </li>
+ * </ol>
+ * <p>
+ * It is possible to disable the URL Handlers service by setting the
+ * <tt>framework.service.urlhandlers</tt> configuration property to <tt>false</tt>.
+ * When multiple framework instances are in use, if no framework instances enable
+ * the URL Handlers service, then the singleton stream and content factories will
+ * never be set (i.e., <tt>URL.setURLStreamHandlerFactory()</tt> and
+ * <tt>URLConnection.setContentHandlerFactory()</tt>). However, if one instance
+ * enables URL Handlers service, then the factory methods will be invoked. In
+ * that case, framework instances that disable the URL Handlers service will
+ * simply not provide that services to their contained bundles, while framework
+ * instances with the service enabled will.
+ * </p>
+**/
+class URLHandlers implements URLStreamHandlerFactory, ContentHandlerFactory
+{
+    private static final Class[] CLASS_TYPE = new Class[]{Class.class};
+
+    private static final Class URLHANDLERS_CLASS = URLHandlers.class;
+
+    private static final SecureAction m_secureAction = new SecureAction();
+
+    private static volatile SecurityManagerEx m_sm = null;
+    private static volatile URLHandlers m_handler = null;
+
+    // This maps classloaders of URLHandlers in other classloaders to lists of
+    // their frameworks.
+    private static Map m_classloaderToFrameworkLists = new HashMap();
+
+    // The list to hold all enabled frameworks registered with this handlers
+    private static final List m_frameworks = new ArrayList();
+    private static int m_counter = 0;
+
+    private static Map m_contentHandlerCache = null;
+    private static Map m_streamHandlerCache = null;
+    private static URLStreamHandlerFactory m_streamHandlerFactory;
+    private static ContentHandlerFactory m_contentHandlerFactory;
+    private static final String STREAM_HANDLER_PACKAGE_PROP = "java.protocol.handler.pkgs";
+    private static final String DEFAULT_STREAM_HANDLER_PACKAGE = "sun.net.www.protocol|com.ibm.oti.net.www.protocol|gnu.java.net.protocol|wonka.net|com.acunia.wonka.net|org.apache.harmony.luni.internal.net.www.protocol|weblogic.utils|weblogic.net|javax.net.ssl|COM.newmonics.www.protocols";
+    private static Object m_rootURLHandlers;
+
+    private static final String m_streamPkgs;
+    private static final Map m_builtIn = new HashMap();
+    private static final boolean m_loaded;
+
+    static
+    {
+        String pkgs = new SecureAction().getSystemProperty(STREAM_HANDLER_PACKAGE_PROP, "");
+        m_streamPkgs = (pkgs.equals(""))
+            ? DEFAULT_STREAM_HANDLER_PACKAGE
+            : pkgs + "|" + DEFAULT_STREAM_HANDLER_PACKAGE;
+        m_loaded = (null != URLHandlersStreamHandlerProxy.class) &&
+            (null != URLHandlersContentHandlerProxy.class) && (null != URLStreamHandlerService.class);
+    }
+
+
+    private static final Map m_handlerToURL = new HashMap();
+    private void init(String protocol, URLStreamHandlerFactory factory)
+    {
+        try
+        {
+            URLStreamHandler handler = getBuiltInStreamHandler(protocol, factory);
+            if (handler != null)
+            {
+                URL url = new URL(protocol, null, -1, "", handler);
+                m_handlerToURL.put(handler, url);
+            }
+        }
+        catch (Throwable ex)
+        {
+            // Ignore, this is a best effort (maybe log it or something).
+        }
+    }
+
+    /**
+     * <p>
+     * Only one instance of this class is created per classloader
+     * and that one instance is registered as the stream and content handler
+     * factories for the JVM. Unless, we already register one from a different
+     * classloader. In this case we attach to this root.
+     * </p>
+    **/
+    private URLHandlers()
+    {
+        m_sm = new SecurityManagerEx();
+        synchronized (URL.class)
+        {
+            URLStreamHandlerFactory currentFactory = null;
+            try
+            {
+                currentFactory = (URLStreamHandlerFactory) m_secureAction.swapStaticFieldIfNotClass(URL.class,
+                    URLStreamHandlerFactory.class, URLHANDLERS_CLASS, "streamHandlerLock");
+            }
+            catch (Throwable ex)
+            {
+                // Ignore, this is a best effort (maybe log it or something)
+            }
+
+            init("file", currentFactory);
+            init("ftp", currentFactory);
+            init("http", currentFactory);
+            init("https", currentFactory);
+            try
+            {
+                getBuiltInStreamHandler("jar", currentFactory);
+            }
+            catch (Throwable ex)
+            {
+                // Ignore, this is a best effort (maybe log it or something)
+            }
+
+            if (currentFactory != null)
+            {
+                try
+                {
+                    URL.setURLStreamHandlerFactory(currentFactory);
+                }
+                catch (Throwable ex)
+                {
+                    // Ignore, this is a best effort (maybe log it or something)
+                }
+            }
+
+            try
+            {
+                URL.setURLStreamHandlerFactory(this);
+                m_streamHandlerFactory = this;
+                m_rootURLHandlers = this;
+                // try to flush the cache (gnu/classpath doesn't do it itself)
+                try
+                {
+                    m_secureAction.flush(URL.class, URL.class);
+                }
+                catch (Throwable t)
+                {
+                    // Not much we can do
+                }
+            }
+            catch (Error err)
+            {
+                try
+                {
+                    // there already is a factory set so try to swap it with ours.
+                    m_streamHandlerFactory = (URLStreamHandlerFactory)
+                        m_secureAction.swapStaticFieldIfNotClass(URL.class,
+                        URLStreamHandlerFactory.class, URLHANDLERS_CLASS, "streamHandlerLock");
+
+                    if (m_streamHandlerFactory == null)
+                    {
+                        throw err;
+                    }
+                    if (!m_streamHandlerFactory.getClass().getName().equals(URLHANDLERS_CLASS.getName()))
+                    {
+                        URL.setURLStreamHandlerFactory(this);
+                        m_rootURLHandlers = this;
+                    }
+                    else if (URLHANDLERS_CLASS != m_streamHandlerFactory.getClass())
+                    {
+                        try
+                        {
+                            m_secureAction.invoke(
+                                m_secureAction.getDeclaredMethod(m_streamHandlerFactory.getClass(),
+                                "registerFrameworkListsForContextSearch",
+                                new Class[]{ClassLoader.class, List.class}),
+                                m_streamHandlerFactory, new Object[]{ URLHANDLERS_CLASS.getClassLoader(),
+                                    m_frameworks });
+                            m_rootURLHandlers = m_streamHandlerFactory;
+                        }
+                        catch (Exception ex)
+                        {
+                            throw new RuntimeException(ex.getMessage());
+                        }
+                    }
+                }
+                catch (Exception e)
+                {
+                    throw err;
+                }
+            }
+
+            try
+            {
+                URLConnection.setContentHandlerFactory(this);
+                m_contentHandlerFactory = this;
+                // try to flush the cache (gnu/classpath doesn't do it itself)
+                try
+                {
+                    m_secureAction.flush(URLConnection.class, URLConnection.class);
+                }
+                catch (Throwable t)
+                {
+                    // Not much we can do
+                }
+            }
+            catch (Error err)
+            {
+                // there already is a factory set so try to swap it with ours.
+                try
+                {
+                    m_contentHandlerFactory = (ContentHandlerFactory)
+                        m_secureAction.swapStaticFieldIfNotClass(
+                            URLConnection.class, ContentHandlerFactory.class,
+                            URLHANDLERS_CLASS, null);
+                    if (m_contentHandlerFactory == null)
+                    {
+                        throw err;
+                    }
+                    if (!m_contentHandlerFactory.getClass().getName().equals(
+                        URLHANDLERS_CLASS.getName()))
+                    {
+                        URLConnection.setContentHandlerFactory(this);
+                    }
+                }
+                catch (Exception ex)
+                {
+                    throw err;
+                }
+            }
+        }
+        // are we not the new root?
+        if (!((m_streamHandlerFactory == this) || !URLHANDLERS_CLASS.getName().equals(
+            m_streamHandlerFactory.getClass().getName())))
+        {
+            m_sm = null;
+            m_handlerToURL.clear();
+            m_builtIn.clear();
+        }
+    }
+
+    static void registerFrameworkListsForContextSearch(ClassLoader index,
+        List frameworkLists)
+    {
+        synchronized (URL.class)
+        {
+            synchronized (m_classloaderToFrameworkLists)
+            {
+                m_classloaderToFrameworkLists.put(index, frameworkLists);
+            }
+        }
+    }
+
+    static void unregisterFrameworkListsForContextSearch(ClassLoader index)
+    {
+        synchronized (URL.class)
+        {
+            synchronized (m_classloaderToFrameworkLists)
+            {
+                m_classloaderToFrameworkLists.remove(index);
+                if (m_classloaderToFrameworkLists.isEmpty() )
+                {
+                    synchronized (m_frameworks)
+                    {
+                        if (m_frameworks.isEmpty())
+                        {
+                            try
+                            {
+                                m_secureAction.swapStaticFieldIfNotClass(URL.class,
+                                    URLStreamHandlerFactory.class, null, "streamHandlerLock");
+                            }
+                            catch (Exception ex)
+                            {
+                                // TODO log this
+                                ex.printStackTrace();
+                            }
+
+                            if (m_streamHandlerFactory.getClass() != URLHANDLERS_CLASS)
+                            {
+                                URL.setURLStreamHandlerFactory(m_streamHandlerFactory);
+                            }
+                            try
+                            {
+                                m_secureAction.swapStaticFieldIfNotClass(
+                                    URLConnection.class, ContentHandlerFactory.class,
+                                    null, null);
+                            }
+                            catch (Exception ex)
+                            {
+                                // TODO log this
+                                ex.printStackTrace();
+                            }
+
+                            if (m_contentHandlerFactory.getClass() != URLHANDLERS_CLASS)
+                            {
+                                URLConnection.setContentHandlerFactory(m_contentHandlerFactory);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    private URLStreamHandler getBuiltInStreamHandler(String protocol, URLStreamHandlerFactory factory)
+    {
+        synchronized (m_builtIn)
+        {
+            if (m_builtIn.containsKey(protocol))
+            {
+                return (URLStreamHandler) m_builtIn.get(protocol);
+            }
+        }
+        if (factory != null)
+        {
+            URLStreamHandler result = factory.createURLStreamHandler(protocol);
+            if (result != null)
+            {
+                return addToCache(protocol, result);
+            }
+        }
+        // Check for built-in handlers for the mime type.
+        // Iterate over built-in packages.
+        URLStreamHandler handler = loadBuiltInStreamHandler(protocol, null);
+        if (handler == null)
+        {
+            handler = loadBuiltInStreamHandler(protocol, ClassLoader.getSystemClassLoader());
+        }
+        return addToCache(protocol, handler);
+    }
+
+    private URLStreamHandler loadBuiltInStreamHandler(String protocol, ClassLoader classLoader) {
+        StringTokenizer pkgTok = new StringTokenizer(m_streamPkgs, "| ");
+        while (pkgTok.hasMoreTokens())
+        {
+            String pkg = pkgTok.nextToken().trim();
+            String className = pkg + "." + protocol + ".Handler";
+            try
+            {
+                // If a built-in handler is found then cache and return it
+                Class handler = m_secureAction.forName(className, classLoader);
+                if (handler != null)
+                {
+                    return (URLStreamHandler) handler.newInstance();
+                }
+            }
+            catch (Throwable ex)
+            {
+                // This could be a class not found exception or an
+                // instantiation exception, not much we can do in either
+                // case other than ignore it.
+            }
+        }
+        // This is a workaround for android - Starting with 4.1 the built-in core handler
+        // are not following the normal naming nore package schema :-(
+        String androidHandler = null;
+        if ("file".equalsIgnoreCase(protocol))
+        {
+            androidHandler = "libcore.net.url.FileHandler";
+        }
+        else if ("ftp".equalsIgnoreCase(protocol))
+        {
+            androidHandler = "libcore.net.url.FtpHandler";
+        }
+        else if ("http".equalsIgnoreCase(protocol))
+        {
+            androidHandler = "libcore.net.http.HttpHandler";
+        }
+        else if ("https".equalsIgnoreCase(protocol))
+        {
+            androidHandler = "libcore.net.http.HttpsHandler";
+        }
+        else if ("jar".equalsIgnoreCase(protocol))
+        {
+            androidHandler = "libcore.net.url.JarHandler";
+        }
+        if (androidHandler != null)
+        {
+            try
+            {
+                // If a built-in handler is found then cache and return it
+                Class handler = m_secureAction.forName(androidHandler, classLoader);
+                if (handler != null)
+                {
+                    return (URLStreamHandler) handler.newInstance();
+                }
+            }
+            catch (Throwable ex)
+            {
+                // This could be a class not found exception or an
+                // instantiation exception, not much we can do in either
+                // case other than ignore it.
+            }
+        }
+        return null;
+    }
+
+    private synchronized URLStreamHandler addToCache(String protocol, URLStreamHandler result)
+    {
+        if (!m_builtIn.containsKey(protocol))
+            {
+                m_builtIn.put(protocol, result);
+                return result;
+            }
+        return (URLStreamHandler) m_builtIn.get(protocol);
+    }
+
+    /**
+     * <p>
+     * This is a method implementation for the <tt>URLStreamHandlerFactory</tt>
+     * interface. It simply creates a stream handler proxy object for the
+     * specified protocol. It caches the returned proxy; therefore, subsequent
+     * requests for the same protocol will receive the same handler proxy.
+     * </p>
+     * @param protocol the protocol for which a stream handler should be returned.
+     * @return a stream handler proxy for the specified protocol.
+    **/
+    public URLStreamHandler createURLStreamHandler(String protocol)
+    {
+        // See if there is a cached stream handler.
+        // IMPLEMENTATION NOTE: Caching is not strictly necessary for
+        // stream handlers since the Java runtime caches them. Caching is
+        // performed for code consistency between stream and content
+        // handlers and also because caching behavior may not be guaranteed
+        // across different JRE implementations.
+        URLStreamHandler handler = getFromStreamCache(protocol);
+
+        if (handler != null)
+        {
+            return handler;
+        }
+        // If this is the framework's "bundle:" protocol, then return
+        // a handler for that immediately, since no one else can be
+        // allowed to deal with it.
+        if (protocol.equals(FelixConstants.BUNDLE_URL_PROTOCOL))
+        {
+            return addToStreamCache(protocol,
+                new URLHandlersBundleStreamHandler(m_secureAction));
+        }
+
+       handler = getBuiltInStreamHandler(protocol,
+           (m_streamHandlerFactory != this) ? m_streamHandlerFactory : null);
+
+        // If built-in content handler, then create a proxy handler.
+        return addToStreamCache(protocol,
+            new URLHandlersStreamHandlerProxy(protocol, m_secureAction,
+                handler, (URL) m_handlerToURL.get(handler)));
+    }
+
+    /**
+     * <p>
+     * This is a method implementation for the <tt>ContentHandlerFactory</tt>
+     * interface. It simply creates a content handler proxy object for the
+     * specified mime type. It caches the returned proxy; therefore, subsequent
+     * requests for the same content type will receive the same handler proxy.
+     * </p>
+     * @param mimeType the mime type for which a content handler should be returned.
+     * @return a content handler proxy for the specified mime type.
+    **/
+    public ContentHandler createContentHandler(String mimeType)
+    {
+        // See if there is a cached stream handler.
+        // IMPLEMENTATION NOTE: Caching is not strictly necessary for
+        // stream handlers since the Java runtime caches them. Caching is
+        // performed for code consistency between stream and content
+        // handlers and also because caching behavior may not be guaranteed
+        // across different JRE implementations.
+        ContentHandler handler = getFromContentCache(mimeType);
+
+        if (handler != null)
+        {
+            return handler;
+        }
+
+        return addToContentCache(mimeType,
+            new URLHandlersContentHandlerProxy(mimeType, m_secureAction,
+            (m_contentHandlerFactory != this) ? m_contentHandlerFactory : null));
+    }
+
+    private synchronized ContentHandler addToContentCache(String mimeType, ContentHandler handler)
+    {
+        if (m_contentHandlerCache == null)
+        {
+            m_contentHandlerCache = new HashMap();
+        }
+        return (ContentHandler) addToCache(m_contentHandlerCache, mimeType, handler);
+    }
+
+    private synchronized ContentHandler getFromContentCache(String mimeType)
+    {
+        return (ContentHandler) ((m_contentHandlerCache != null) ?
+            m_contentHandlerCache.get(mimeType) : null);
+    }
+
+    private synchronized URLStreamHandler addToStreamCache(String protocol, URLStreamHandler handler)
+    {
+        if (m_streamHandlerCache == null)
+        {
+            m_streamHandlerCache = new HashMap();
+        }
+        return (URLStreamHandler) addToCache(m_streamHandlerCache, protocol, handler);
+    }
+
+    private synchronized URLStreamHandler getFromStreamCache(String protocol)
+    {
+        return (URLStreamHandler) ((m_streamHandlerCache != null) ?
+            m_streamHandlerCache.get(protocol) : null);
+    }
+
+    private Object addToCache(Map cache, String key, Object value)
+    {
+        if (value == null)
+        {
+            return null;
+        }
+
+        Object result = cache.get(key);
+
+        if (result == null)
+        {
+            cache.put(key, value);
+            result = value;
+        }
+        return result;
+    }
+
+    /**
+     * <p>
+     * Static method that adds a framework instance to the centralized
+     * instance registry.
+     * </p>
+     * @param framework the framework instance to be added to the instance
+     *        registry.
+     * @param enable a flag indicating whether or not the framework wants to
+     *        enable the URL Handlers service.
+    **/
+    public static void registerFrameworkInstance(Object framework, boolean enable)
+    {
+        synchronized (m_frameworks)
+        {
+            // If the URL Handlers service is not going to be enabled,
+            // then return immediately.
+            if (enable)
+            {
+                // We need to create an instance if this is the first
+                // time this method is called, which will set the handler
+                // factories.
+                if (m_handler == null)
+                {
+                    m_handler = new URLHandlers();
+                }
+                m_frameworks.add(framework);
+            }
+            m_counter++;
+        }
+    }
+
+    /**
+     * <p>
+     * Static method that removes a framework instance from the centralized
+     * instance registry.
+     * </p>
+     * @param framework the framework instance to be removed from the instance
+     *        registry.
+    **/
+    public static void unregisterFrameworkInstance(Object framework)
+    {
+        boolean unregister = false;
+        synchronized (m_frameworks)
+        {
+            m_counter--;
+            if (m_frameworks.remove(framework))
+            {
+                if (m_frameworks.isEmpty())
+                {
+                    unregister = true;
+                    m_handler = null;
+                }
+            }
+        }
+        if (unregister)
+        {
+             try
+             {
+                 m_secureAction.invoke(m_secureAction.getDeclaredMethod(
+                     m_rootURLHandlers.getClass(),
+                     "unregisterFrameworkListsForContextSearch",
+                     new Class[]{ ClassLoader.class}),
+                     m_rootURLHandlers,
+                     new Object[] {URLHANDLERS_CLASS.getClassLoader()});
+             }
+             catch (Exception e)
+             {
+                 // This should not happen
+                 e.printStackTrace();
+             }
+        }
+    }
+
+    /**
+     * <p>
+     * This method returns the system bundle context for the caller.
+     * It determines the appropriate system bundle by retrieving the
+     * class call stack and find the first class that is loaded from
+     * a bundle. It then checks to see which of the registered framework
+     * instances owns the class and returns its system bundle context.
+     * </p>
+     * @return the system bundle context associated with the caller or
+     *         <tt>null</tt> if no associated framework was found.
+    **/
+    public static Object getFrameworkFromContext()
+    {
+        // This is a hack. The idea is to return the only registered framework
+        synchronized (m_classloaderToFrameworkLists)
+        {
+            if (m_classloaderToFrameworkLists.isEmpty())
+            {
+                synchronized (m_frameworks)
+                {
+                    if ((m_counter == 1) && (m_frameworks.size() == 1))
+                    {
+                        return m_frameworks.get(0);
+                    }
+                }
+            }
+        }
+        // get the current class call stack.
+        Class[] stack = m_sm.getClassContext();
+        // Find the first class that is loaded from a bundle.
+        Class targetClass = null;
+        for (int i = 0; i < stack.length; i++)
+        {
+            if (stack[i].getClassLoader() != null)
+            {
+                String name = stack[i].getClassLoader().getClass().getName();
+                if (name.startsWith("org.apache.felix.framework.ModuleImpl$ModuleClassLoader")
+                    || name.equals("org.apache.felix.framework.searchpolicy.ContentClassLoader")
+                    || name.startsWith("org.apache.felix.framework.BundleWiringImpl$BundleClassLoader"))
+                {
+                    targetClass = stack[i];
+                    break;
+                }
+            }
+        }
+
+        // If we found a class loaded from a bundle, then iterate
+        // over the framework instances and see which framework owns
+        // the bundle that loaded the class.
+        if (targetClass != null)
+        {
+            synchronized (m_classloaderToFrameworkLists)
+            {
+                ClassLoader index = targetClass.getClassLoader().getClass().getClassLoader();
+
+                List frameworks = (List) m_classloaderToFrameworkLists.get(
+                    index);
+
+                if ((frameworks == null) && (index == URLHANDLERS_CLASS.getClassLoader()))
+                {
+                    frameworks = m_frameworks;
+                }
+                if (frameworks != null)
+                {
+                    synchronized (frameworks)
+                    {
+                        // Check the registry of framework instances
+                        for (int i = 0; i < frameworks.size(); i++)
+                        {
+                            Object framework = frameworks.get(i);
+                            try
+                            {
+                                if (m_secureAction.invoke(
+                                    m_secureAction.getDeclaredMethod(framework.getClass(),
+                                    "getBundle", CLASS_TYPE),
+                                    framework, new Object[]{targetClass}) != null)
+                                {
+                                    return framework;
+                                }
+                            }
+                            catch (Exception ex)
+                            {
+                                // This should not happen but if it does there is
+                                // not much we can do other then ignore it.
+                                // Maybe log this or something.
+                                ex.printStackTrace();
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        return null;
+    }
+}
diff --git a/src/main/java/org/apache/felix/framework/URLHandlersActivator.java b/src/main/java/org/apache/felix/framework/URLHandlersActivator.java
new file mode 100644
index 0000000..4eca450
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/URLHandlersActivator.java
@@ -0,0 +1,123 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework;
+
+import java.net.ContentHandler;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.felix.framework.util.FelixConstants;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.url.URLStreamHandlerService;
+import org.osgi.util.tracker.ServiceTracker;
+
+/**
+ * <p>
+ * Simple activator class used by the system bundle to enable the
+ * URL Handlers service. The only purpose of this class is to call
+ * <tt>URLHandlers.registerInstance()</tt> when the framework is
+ * started and <tt>URLHandlers.unregisterInstance()</tt> when the
+ * framework is stopped.
+ *</p>
+**/
+class URLHandlersActivator implements BundleActivator
+{
+    private final Map m_configMap;
+    private final Felix m_framework;
+
+    public URLHandlersActivator(Map configMap, Felix framework)
+    {
+        m_configMap = configMap;
+        m_framework = framework;
+    }
+
+    //
+    // Bundle activator methods.
+    //
+
+    public void start(BundleContext context)
+    {
+        // Only register the framework with the URL Handlers service
+        // if the service is enabled.
+        boolean enable = (m_configMap.get(
+                FelixConstants.SERVICE_URLHANDLERS_PROP) == null)
+                ? true
+                : !m_configMap.get(FelixConstants.SERVICE_URLHANDLERS_PROP).equals("false");
+
+        if (enable)
+        {
+            m_framework.setURLHandlersActivator(this);
+        }
+        URLHandlers.registerFrameworkInstance(m_framework, enable);
+    }
+
+    public void stop(BundleContext context)
+    {
+        URLHandlers.unregisterFrameworkInstance(m_framework);
+        m_framework.setURLHandlersActivator(null);
+    }
+
+    protected Object getStreamHandlerService(String protocol)
+    {
+        return get(
+            m_framework.getHooks(URLStreamHandlerService.class),
+            "url.handler.protocol", protocol);
+    }
+
+    protected Object getContentHandlerService(String mimeType)
+    {
+        return get(
+            m_framework.getHooks(ContentHandler.class),
+            "url.content.mimetype", mimeType);
+    }
+
+    private <S> S get(Set<ServiceReference<S>> hooks, String key, String value)
+    {
+    	Object service = null;
+        if (!hooks.isEmpty())
+        {
+            for (ServiceReference<S> ref : hooks)
+            {
+                Object values = ref.getProperty(key);
+                if (values instanceof String[])
+                {
+                    for (int valueIdx = 0;
+                        (valueIdx < ((String[]) values).length) && (service == null);
+                        valueIdx++)
+                    {
+                        if (value.equals(((String[]) values)[valueIdx]))
+                        {
+                            return m_framework.getService(m_framework, ref);
+                        }
+                    }
+                }
+                else if (value.equals(values))
+                {
+                    return m_framework.getService(m_framework, ref);
+                }
+            }
+        }
+
+        return null;
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/felix/framework/URLHandlersBundleStreamHandler.java b/src/main/java/org/apache/felix/framework/URLHandlersBundleStreamHandler.java
new file mode 100644
index 0000000..091f682
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/URLHandlersBundleStreamHandler.java
@@ -0,0 +1,154 @@
+/* 
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework;
+
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.net.*;
+
+import org.apache.felix.framework.util.SecureAction;
+import org.apache.felix.framework.util.Util;
+import org.osgi.framework.AdminPermission;
+import org.osgi.framework.Bundle;
+
+class URLHandlersBundleStreamHandler extends URLStreamHandler
+{
+    private final Felix m_framework;
+    private final SecureAction m_action;
+
+    public URLHandlersBundleStreamHandler(Felix framework)
+    {
+        m_framework = framework;
+        m_action = null;
+    }
+
+    public URLHandlersBundleStreamHandler(SecureAction action)
+    {
+        m_framework = null;
+        m_action = action;
+    }
+
+    protected synchronized URLConnection openConnection(URL url) throws IOException
+    {
+        if (!"felix".equals(url.getAuthority()))
+        {
+            checkPermission(url);
+        }
+        if (m_framework != null)
+        {
+            return new URLHandlersBundleURLConnection(url, m_framework);
+        }
+        
+        Object framework = URLHandlers.getFrameworkFromContext();
+        
+        if (framework != null)
+        {
+            if (framework instanceof Felix)
+            {
+                return new URLHandlersBundleURLConnection(url, (Felix) framework);
+            }
+            try
+            {
+                Class targetClass = framework.getClass().getClassLoader().loadClass(
+                    URLHandlersBundleURLConnection.class.getName());
+                
+                Constructor constructor = m_action.getConstructor(targetClass, 
+                        new Class[]{URL.class, framework.getClass().getClassLoader().loadClass(
+                                Felix.class.getName())});
+                m_action.setAccesssible(constructor);
+                return (URLConnection) m_action.invoke(constructor, new Object[]{url, framework});
+            }
+            catch (Exception ex)
+            {
+                throw new IOException(ex.getMessage());
+            }
+        }
+        throw new IOException("No framework context found");
+    }
+
+    protected void parseURL(URL u, String spec, int start, int limit) 
+    {
+        super.parseURL(u, spec, start, limit);
+
+        if (checkPermission(u))
+        {
+            super.setURL(u, u.getProtocol(), u.getHost(), u.getPort(), "felix", u.getUserInfo(), u.getPath(), u.getQuery(), u.getRef());
+        }
+    }
+
+    protected String toExternalForm(URL u) 
+    {
+        StringBuffer result = new StringBuffer();
+        result.append(u.getProtocol());
+        result.append("://");
+        result.append(u.getHost());
+        result.append(':');
+        result.append(u.getPort());
+        if (u.getPath() != null) 
+        {
+            result.append(u.getPath());
+        }
+        if (u.getQuery() != null) 
+        {
+            result.append('?');
+            result.append(u.getQuery());
+        }
+        if (u.getRef() != null) 
+        {
+            result.append("#");
+            result.append(u.getRef());
+        }
+        return result.toString();
+    }
+    
+    protected java.net.InetAddress getHostAddress(URL u)
+    {
+        return null;
+    }
+
+    private boolean checkPermission(URL u)
+    {
+        SecurityManager sm = System.getSecurityManager();
+        if (sm != null)
+        {
+            Object framework = m_framework;
+            if (framework == null)
+            {
+                framework = URLHandlers.getFrameworkFromContext();
+                if (!(framework instanceof Felix))
+                {
+                    return false;
+                }
+            }
+            Felix felix = (Felix) framework;
+            long bundleId = Util.getBundleIdFromRevisionId(u.getHost());
+            Bundle bundle = felix.getBundle(bundleId);
+            if (bundle != null)
+            {
+                sm.checkPermission(new AdminPermission(bundle, AdminPermission.RESOURCE));
+                return true;
+            }
+        }
+        else
+        {
+            return true;
+        }
+        return false;
+    }
+}
diff --git a/src/main/java/org/apache/felix/framework/URLHandlersBundleURLConnection.java b/src/main/java/org/apache/felix/framework/URLHandlersBundleURLConnection.java
new file mode 100644
index 0000000..aa52034
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/URLHandlersBundleURLConnection.java
@@ -0,0 +1,239 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.net.URLConnection;
+import java.security.Permission;
+
+import org.apache.felix.framework.util.Util;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.wiring.BundleRevision;
+import org.osgi.framework.wiring.BundleRevisions;
+import org.osgi.framework.wiring.BundleWiring;
+
+class URLHandlersBundleURLConnection extends URLConnection
+{
+    private Felix m_framework;
+    private BundleRevision m_targetRevision;
+    private int m_classPathIdx = -1;
+    private int m_contentLength;
+    private long m_contentTime;
+    private String m_contentType;
+    private InputStream m_is;
+
+    public URLHandlersBundleURLConnection(URL url)
+    {
+        super(url);
+    }
+
+    public URLHandlersBundleURLConnection(URL url, Felix framework)
+        throws IOException
+    {
+        super(url);
+
+        // If this is an attempt to create a connection to the root of
+        // the bundle, then throw an exception since this isn't possible.
+        // We only allow "/" as a valid URL so it can be used as context
+        // for creating other URLs.
+        String path = url.getPath();
+        if ((path == null) || (path.length() == 0) || path.equals("/"))
+        {
+            throw new IOException("Resource does not exist: " + url);
+        }
+
+        m_framework = framework;
+
+        // If we don't have a framework instance, try to find
+        // one from the call context.
+        if (m_framework == null)
+        {
+            Object tmp = URLHandlers.getFrameworkFromContext();
+            if (tmp instanceof Felix)
+            {
+                m_framework = (Felix) tmp;
+            }
+        }
+
+        // If there is still no framework, then error.
+        if (m_framework == null)
+        {
+            throw new IOException("Unable to find framework for URL: " + url);
+        }
+        // Verify that the resource pointed to by the URL exists.
+        // The URL is constructed like this:
+        //     bundle://<revision-id>:<bundle-classpath-index>/<resource-path>
+        // Where <revision-id> = <bundle-id>.<revision>
+        long bundleId = Util.getBundleIdFromRevisionId(url.getHost());
+        Bundle bundle = m_framework.getBundle(bundleId);
+        if (bundle == null)
+        {
+            throw new IOException("No bundle associated with resource: " + url);
+        }
+        m_contentTime = bundle.getLastModified();
+
+        // Get the bundle's revisions to find the target revision.
+        BundleRevisions revisions = bundle.adapt(BundleRevisions.class);
+        if ((revisions == null) || revisions.getRevisions().isEmpty())
+        {
+            throw new IOException("Resource does not exist: " + url);
+        }
+
+        // Search for matching revision name.
+        for (BundleRevision br : revisions.getRevisions())
+        {
+            if (((BundleRevisionImpl) br).getId().equals(url.getHost()))
+            {
+                m_targetRevision = br;
+                break;
+            }
+        }
+
+        // If not found, assume the current revision.
+        if (m_targetRevision == null)
+        {
+            m_targetRevision = revisions.getRevisions().get(0);
+        }
+
+        // If the resource cannot be found at the current class path index,
+        // then search them all in order to see if it can be found. This is
+        // necessary since the user might create a resource URL from another
+        // resource URL and not realize they have the wrong class path entry.
+        // Of course, this approach won't work in cases where there are multiple
+        // resources with the same path, since it will always find the first
+        // one on the class path.
+        m_classPathIdx = url.getPort();
+        if (m_classPathIdx < 0)
+        {
+            m_classPathIdx = 0;
+        }
+        if (!((BundleRevisionImpl) m_targetRevision)
+            .hasInputStream(m_classPathIdx, url.getPath()))
+        {
+            BundleWiring wiring = m_targetRevision.getWiring();
+            ClassLoader cl = (wiring != null) ? wiring.getClassLoader() : null;
+            URL newurl = (cl != null) ? cl.getResource(url.getPath()) : null;
+            if (newurl == null)
+            {
+                throw new IOException("Resource does not exist: " + url);
+            }
+            m_classPathIdx = newurl.getPort();
+        }
+    }
+
+    public synchronized void connect() throws IOException
+    {
+        if (!connected)
+        {
+            if ((m_targetRevision == null) || (m_classPathIdx < 0))
+            {
+                throw new IOException("Resource does not exist: " + url);
+            }
+            m_is = ((BundleRevisionImpl)
+                m_targetRevision).getInputStream(m_classPathIdx, url.getPath());
+            m_contentLength = (m_is == null) ? 0 : m_is.available();
+            m_contentType = URLConnection.guessContentTypeFromName(url.getFile());
+            connected = true;
+        }
+    }
+
+    public InputStream getInputStream()
+        throws IOException
+    {
+        connect();
+
+        return m_is;
+    }
+
+    public int getContentLength()
+    {
+        try
+        {
+            connect();
+        }
+        catch(IOException ex)
+        {
+            return -1;
+        }
+
+        return m_contentLength;
+    }
+
+    public long getLastModified()
+    {
+        try
+        {
+            connect();
+        }
+        catch(IOException ex)
+        {
+            return 0;
+        }
+
+        if (m_contentTime != -1L)
+        {
+            return m_contentTime;
+        }
+        else
+        {
+            return 0L;
+        }
+    }
+
+    public String getContentType()
+    {
+        try
+        {
+            connect();
+        }
+        catch (IOException ex)
+        {
+            return null;
+        }
+
+        return m_contentType;
+    }
+
+    public Permission getPermission()
+    {
+        // TODO: SECURITY - This should probably return a FilePermission
+        // to access the bundle JAR file, but we don't have the
+        // necessary information here to construct the absolute
+        // path of the JAR file...so it would take some
+        // re-arranging to get this to work.
+        return null;
+    }
+
+    /**
+     * Retrieve the entry as a URL using standard protocols such as file: and jar:
+     *
+     * @return the local URL
+     */
+    URL getLocalURL()
+    {
+        if ((m_targetRevision == null) || (m_classPathIdx < 0))
+        {
+            return url;
+        }
+        return ((BundleRevisionImpl)
+            m_targetRevision).getLocalURL(m_classPathIdx, url.getPath());
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/felix/framework/URLHandlersContentHandlerProxy.java b/src/main/java/org/apache/felix/framework/URLHandlersContentHandlerProxy.java
new file mode 100644
index 0000000..354fab8
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/URLHandlersContentHandlerProxy.java
@@ -0,0 +1,198 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework;
+
+import java.io.IOException;
+import java.net.ContentHandler;
+import java.net.ContentHandlerFactory;
+import java.net.URLConnection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.StringTokenizer;
+
+import org.apache.felix.framework.util.SecureAction;
+
+/**
+ * <p>
+ * This class implements a content handler proxy. When the content handler
+ * proxy instance is created, it is associated with a particular mime type
+ * and will answer all future requests for content of that type. It does
+ * not directly handle the content requests, but delegates the requests to
+ * an underlying content handler service.
+ * </p>
+ * <p>
+ * The proxy for a particular mime type is used for all framework instances
+ * that may contain their own content handler services. When performing a
+ * content handler operation, the proxy retrieves the handler service from
+ * the framework instance associated with the current call stack and delegates
+ * the call to the handler service.
+ * </p>
+ * <p>
+ * The proxy will create simple content handler service trackers for each
+ * framework instance. The trackers will listen to service events in its
+ * respective framework instance to maintain a reference to the "best"
+ * content handler service at any given time.
+ * </p>
+**/
+class URLHandlersContentHandlerProxy extends ContentHandler
+{
+    private static final Class[] STRING_TYPES = new Class[]{String.class};
+
+    private static final String CONTENT_HANDLER_PACKAGE_PROP = "java.content.handler.pkgs";
+    private static final String DEFAULT_CONTENT_HANDLER_PACKAGE = "sun.net.www.content|com.ibm.oti.net.www.content|gnu.java.net.content|org.apache.harmony.luni.internal.net.www.content|COM.newmonics.www.content";
+
+    private static final Map m_builtIn = new HashMap();
+    private static final String m_pkgs;
+
+    static
+    {
+        String pkgs = new SecureAction().getSystemProperty(CONTENT_HANDLER_PACKAGE_PROP, "");
+        m_pkgs = (pkgs.equals(""))
+            ? DEFAULT_CONTENT_HANDLER_PACKAGE
+            : pkgs + "|" + DEFAULT_CONTENT_HANDLER_PACKAGE;
+    }
+
+    private final ContentHandlerFactory m_factory;
+
+    private final String m_mimeType;
+    private final SecureAction m_action;
+
+    public URLHandlersContentHandlerProxy(String mimeType, SecureAction action,
+        ContentHandlerFactory factory)
+    {
+        m_mimeType = mimeType;
+        m_action = action;
+        m_factory = factory;
+    }
+
+    //
+    // ContentHandler interface method.
+    //
+
+    public Object getContent(URLConnection urlc) throws IOException
+    {
+        ContentHandler svc = getContentHandlerService();
+        if (svc == null)
+        {
+            return urlc.getInputStream();
+        }
+        return svc.getContent(urlc);
+    }
+
+    /**
+     * <p>
+     * Private method to retrieve the content handler service from the
+     * framework instance associated with the current call stack. A
+     * simple service tracker is created and cached for the associated
+     * framework instance when this method is called.
+     * </p>
+     * @return the content handler service from the framework instance
+     *         associated with the current call stack or <tt>null</tt>
+     *         is no service is available.
+    **/
+    private ContentHandler getContentHandlerService()
+    {
+        // Get the framework instance associated with call stack.
+        Object framework = URLHandlers.getFrameworkFromContext();
+
+        if (framework == null)
+        {
+            return getBuiltIn();
+        }
+        try
+        {
+            ContentHandler service;
+            if (framework instanceof Felix)
+            {
+                service = (ContentHandler) ((Felix) framework).getContentHandlerService(m_mimeType);
+            }
+            else
+            {
+                service = (ContentHandler) m_action.invoke(
+                    m_action.getDeclaredMethod(framework.getClass(), "getContentHandlerService", STRING_TYPES),
+                    framework, new Object[]{m_mimeType});
+            }
+
+            return (service == null) ? getBuiltIn() : service;
+        }
+        catch (Exception ex)
+        {
+            // TODO: log this or something
+            ex.printStackTrace();
+            return null;
+        }
+    }
+
+    private ContentHandler getBuiltIn()
+    {
+        synchronized (m_builtIn)
+        {
+            if (m_builtIn.containsKey(m_mimeType))
+            {
+                return (ContentHandler) m_builtIn.get(m_mimeType);
+            }
+        }
+        if (m_factory != null)
+        {
+            ContentHandler result = m_factory.createContentHandler(m_mimeType);
+            if (result != null)
+            {
+                return addToCache(m_mimeType, result);
+            }
+        }
+        // Check for built-in handlers for the mime type.
+        // Remove periods, slashes, and dashes from mime type.
+        String fixedType = m_mimeType.replace('.', '_').replace('/', '.').replace('-', '_');
+
+        // Iterate over built-in packages.
+        StringTokenizer pkgTok = new StringTokenizer(m_pkgs, "| ");
+        while (pkgTok.hasMoreTokens())
+        {
+            String pkg = pkgTok.nextToken().trim();
+            String className = pkg + "." + fixedType;
+            try
+            {
+                // If a built-in handler is found then cache and return it
+                Class handler = m_action.forName(className, null);
+                if (handler != null)
+                {
+                    return addToCache(m_mimeType,
+                        (ContentHandler) handler.newInstance());
+                }
+            }
+            catch (Exception ex)
+            {
+                // This could be a class not found exception or an
+                // instantiation exception, not much we can do in either
+                // case other than ignore it.
+            }
+        }
+        return addToCache(m_mimeType, null);
+    }
+
+    private synchronized ContentHandler addToCache(String mimeType, ContentHandler handler)
+    {
+        if (!m_builtIn.containsKey(mimeType))
+        {
+            m_builtIn.put(mimeType, handler);
+            return handler;
+        }
+        return (ContentHandler) m_builtIn.get(mimeType);
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/felix/framework/URLHandlersStreamHandlerProxy.java b/src/main/java/org/apache/felix/framework/URLHandlersStreamHandlerProxy.java
new file mode 100644
index 0000000..de4fea1
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/URLHandlersStreamHandlerProxy.java
@@ -0,0 +1,651 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework;
+
+import java.io.IOException;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.net.InetAddress;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.URLStreamHandler;
+
+import org.apache.felix.framework.util.SecureAction;
+import org.osgi.service.url.URLStreamHandlerService;
+import org.osgi.service.url.URLStreamHandlerSetter;
+
+/**
+ * <p>
+ * This class implements a stream handler proxy. When the stream handler
+ * proxy instance is created, it is associated with a particular protocol
+ * and will answer all future requests for handling of that stream type. It
+ * does not directly handle the stream handler requests, but delegates the
+ * requests to an underlying stream handler service.
+ * </p>
+ * <p>
+ * The proxy instance for a particular protocol is used for all framework
+ * instances that may contain their own stream handler services. When
+ * performing a stream handler operation, the proxy retrieves the handler
+ * service from the framework instance associated with the current call
+ * stack and delegates the call to the handler service.
+ * </p>
+ * <p>
+ * The proxy will create simple stream handler service trackers for each
+ * framework instance. The trackers will listen to service events in its
+ * respective framework instance to maintain a reference to the "best"
+ * stream handler service at any given time.
+ * </p>
+**/
+public class URLHandlersStreamHandlerProxy extends URLStreamHandler
+    implements URLStreamHandlerSetter, InvocationHandler
+{
+    private static final Class[] URL_PROXY_CLASS;
+    private static final Class[] STRING_TYPES = new Class[]{String.class};
+    private static final Method EQUALS;
+    private static final Method GET_DEFAULT_PORT;
+    private static final Method GET_HOST_ADDRESS;
+    private static final Method HASH_CODE;
+    private static final Method HOSTS_EQUAL;
+    private static final Method OPEN_CONNECTION;
+    private static final Method OPEN_CONNECTION_PROXY;
+    private static final Method SAME_FILE;
+    private static final Method TO_EXTERNAL_FORM;
+
+    static {
+        SecureAction action = new SecureAction();
+        try
+        {
+            EQUALS = URLStreamHandler.class.getDeclaredMethod("equals",
+                new Class[]{URL.class, URL.class});
+            action.setAccesssible(EQUALS);
+            GET_DEFAULT_PORT = URLStreamHandler.class.getDeclaredMethod("getDefaultPort",
+                (Class[]) null);
+            action.setAccesssible(GET_DEFAULT_PORT);
+            GET_HOST_ADDRESS = URLStreamHandler.class.getDeclaredMethod(
+                    "getHostAddress", new Class[]{URL.class});
+            action.setAccesssible(GET_HOST_ADDRESS);
+            HASH_CODE = URLStreamHandler.class.getDeclaredMethod(
+                    "hashCode", new Class[]{URL.class});
+            action.setAccesssible(HASH_CODE);
+            HOSTS_EQUAL = URLStreamHandler.class.getDeclaredMethod(
+                    "hostsEqual", new Class[]{URL.class, URL.class});
+            action.setAccesssible(HOSTS_EQUAL);
+            OPEN_CONNECTION = URLStreamHandler.class.getDeclaredMethod(
+                    "openConnection", new Class[]{URL.class});
+            action.setAccesssible(OPEN_CONNECTION);
+            SAME_FILE = URLStreamHandler.class.getDeclaredMethod(
+                    "sameFile", new Class[]{URL.class, URL.class});
+            action.setAccesssible(SAME_FILE);
+            TO_EXTERNAL_FORM = URLStreamHandler.class.getDeclaredMethod(
+                   "toExternalForm", new Class[]{URL.class});
+            action.setAccesssible(TO_EXTERNAL_FORM);
+        }
+        catch (Exception ex)
+        {
+            throw new RuntimeException(ex.getMessage());
+        }
+
+        Method open_connection_proxy = null;
+        Class[] url_proxy_class = null;
+        try
+        {
+        	url_proxy_class = new Class[]{URL.class, java.net.Proxy.class};
+            open_connection_proxy = URLStreamHandler.class.getDeclaredMethod(
+                "openConnection", url_proxy_class);
+            action.setAccesssible(open_connection_proxy);
+        }
+        catch (Throwable ex)
+        {
+           open_connection_proxy = null;
+           url_proxy_class = null;
+        }
+        OPEN_CONNECTION_PROXY = open_connection_proxy;
+        URL_PROXY_CLASS = url_proxy_class;
+    }
+
+    private final Object m_service;
+    private final SecureAction m_action;
+    private final URLStreamHandler m_builtIn;
+    private final URL m_builtInURL;
+    private final String m_protocol;
+
+    public URLHandlersStreamHandlerProxy(String protocol,
+        SecureAction action, URLStreamHandler builtIn, URL builtInURL)
+    {
+        m_protocol = protocol;
+        m_service = null;
+        m_action = action;
+        m_builtIn = builtIn;
+        m_builtInURL = builtInURL;
+    }
+
+    private URLHandlersStreamHandlerProxy(Object service, SecureAction action)
+    {
+        m_protocol = null;
+        m_service = service;
+        m_action = action;
+        m_builtIn = null;
+        m_builtInURL = null;
+    }
+
+    //
+    // URLStreamHandler interface methods.
+    //
+    protected boolean equals(URL url1, URL url2)
+    {
+        Object svc = getStreamHandlerService();
+        if (svc == null)
+        {
+            throw new IllegalStateException(
+                "Unknown protocol: " + url1.getProtocol());
+        }
+        if (svc instanceof URLStreamHandlerService)
+        {
+            return ((URLStreamHandlerService) svc).equals(url1, url2);
+        }
+        try
+        {
+            return ((Boolean) EQUALS.invoke(svc, new Object[]{url1, url2})).booleanValue();
+        }
+        catch (Exception ex)
+        {
+            throw new IllegalStateException("Stream handler unavailable due to: " + ex.getMessage());
+        }
+    }
+
+    protected int getDefaultPort()
+    {
+        Object svc = getStreamHandlerService();
+        if (svc == null)
+        {
+            throw new IllegalStateException("Stream handler unavailable.");
+        }
+        if (svc instanceof URLStreamHandlerService)
+        {
+            return ((URLStreamHandlerService) svc).getDefaultPort();
+        }
+        try
+        {
+            return ((Integer) GET_DEFAULT_PORT.invoke(svc, null)).intValue();
+        }
+        catch (Exception ex)
+        {
+            throw new IllegalStateException("Stream handler unavailable due to: " + ex.getMessage());
+        }
+    }
+
+    protected InetAddress getHostAddress(URL url)
+    {
+        Object svc = getStreamHandlerService();
+        if (svc == null)
+        {
+            throw new IllegalStateException(
+                "Unknown protocol: " + url.getProtocol());
+        }
+        if (svc instanceof URLStreamHandlerService)
+        {
+            return ((URLStreamHandlerService) svc).getHostAddress(url);
+        }
+        try
+        {
+            return (InetAddress) GET_HOST_ADDRESS.invoke(svc, new Object[]{url});
+        }
+        catch (Exception ex)
+        {
+            throw new IllegalStateException("Stream handler unavailable due to: " + ex.getMessage());
+        }
+    }
+
+    protected int hashCode(URL url)
+    {
+        Object svc = getStreamHandlerService();
+        if (svc == null)
+        {
+            throw new IllegalStateException(
+                "Unknown protocol: " + url.getProtocol());
+        }
+        if (svc instanceof URLStreamHandlerService)
+        {
+            return ((URLStreamHandlerService) svc).hashCode(url);
+        }
+        try
+        {
+            return ((Integer) HASH_CODE.invoke(svc, new Object[]{url})).intValue();
+        }
+        catch (Exception ex)
+        {
+            throw new IllegalStateException("Stream handler unavailable due to: " + ex.getMessage());
+        }
+    }
+
+    protected boolean hostsEqual(URL url1, URL url2)
+    {
+        Object svc = getStreamHandlerService();
+        if (svc == null)
+        {
+            throw new IllegalStateException(
+                "Unknown protocol: " + url1.getProtocol());
+        }
+        if (svc instanceof URLStreamHandlerService)
+        {
+            return ((URLStreamHandlerService) svc).hostsEqual(url1, url2);
+        }
+        try
+        {
+            return ((Boolean) HOSTS_EQUAL.invoke(svc, new Object[]{url1, url2})).booleanValue();
+        }
+        catch (Exception ex)
+        {
+            throw new IllegalStateException("Stream handler unavailable due to: " + ex.getMessage());
+        }
+    }
+
+    protected URLConnection openConnection(URL url) throws IOException
+    {
+        Object svc = getStreamHandlerService();
+        if (svc == null)
+        {
+            throw new MalformedURLException("Unknown protocol: " + url.toString());
+        }
+        if (svc instanceof URLStreamHandlerService)
+        {
+            return ((URLStreamHandlerService) svc).openConnection(url);
+        }
+        try
+        {
+            if ("http".equals(url.getProtocol()) &&
+                "felix.extensions".equals(url.getHost()) &&
+                9 == url.getPort())
+            {
+                try
+                {
+                    Object handler =  m_action.getDeclaredField(
+                        ExtensionManager.class, "m_extensionManager", null);
+
+                    if (handler != null)
+                    {
+                        return (URLConnection) m_action.invoke(
+                            m_action.getMethod(handler.getClass(),
+                            "openConnection", new Class[]{URL.class}), handler,
+                            new Object[]{url});
+                    }
+
+                    throw new IOException("Extensions not supported or ambiguous context.");
+                }
+                catch (IOException ex)
+                {
+                    throw ex;
+                }
+                catch (Exception ex)
+                {
+                    throw new IOException(ex.getMessage());
+                }
+            }
+            return (URLConnection) OPEN_CONNECTION.invoke(svc, new Object[]{url});
+        }
+        catch (IOException ex)
+        {
+            throw ex;
+        }
+        catch (Exception ex)
+        {
+            throw new IllegalStateException("Stream handler unavailable due to: " + ex.getMessage());
+        }
+    }
+
+    protected URLConnection openConnection(URL url, java.net.Proxy proxy) throws IOException
+    {
+        Object svc = getStreamHandlerService();
+        if (svc == null)
+        {
+            throw new MalformedURLException("Unknown protocol: " + url.toString());
+        }
+        if (svc instanceof URLStreamHandlerService)
+        {
+            Method method;
+            try
+            {
+                method = svc.getClass().getMethod("openConnection", URL_PROXY_CLASS);
+            }
+            catch (NoSuchMethodException e)
+            {
+                RuntimeException rte = new UnsupportedOperationException(e.getMessage());
+                rte.initCause(e);
+                throw rte;
+            }
+            try
+            {
+                m_action.setAccesssible(method);
+                return (URLConnection) method.invoke(svc, new Object[]{url, proxy});
+            }
+            catch (Exception e)
+            {
+                if (e instanceof IOException)
+                {
+                    throw (IOException) e;
+                }
+                throw new IOException(e.getMessage());
+            }
+        }
+        try
+        {
+            return (URLConnection) OPEN_CONNECTION_PROXY.invoke(svc, new Object[]{url, proxy});
+        }
+        catch (Exception ex)
+        {
+            if (ex instanceof IOException)
+            {
+                throw (IOException) ex;
+            }
+            throw new IllegalStateException("Stream handler unavailable due to: " + ex.getMessage());
+        }
+    }
+
+    // We use this thread local to detect whether we have a reentrant entry to the parseURL
+    // method. This can happen do to some difference between gnu/classpath and sun jvms
+    // For more see inside the method.
+    private static final ThreadLocal m_loopCheck = new ThreadLocal();
+    protected void parseURL(URL url, String spec, int start, int limit)
+    {
+        Object svc = getStreamHandlerService();
+        if (svc == null)
+        {
+            throw new IllegalStateException(
+                "Unknown protocol: " + url.getProtocol());
+        }
+        if (svc instanceof URLStreamHandlerService)
+        {
+            ((URLStreamHandlerService) svc).parseURL(this, url, spec, start, limit);
+        }
+        else
+        {
+            try
+            {
+                URL test = null;
+                // In order to cater for built-in urls being over-writable we need to use a
+                // somewhat strange hack. We use a hidden feature inside the jdk which passes
+                // the handler of the url given as a context to a new URL to that URL as its
+                // handler. This way, we can create a new URL which will use the given built-in
+                // handler to parse the url. Subsequently, we can use the information from that
+                // URL to call set with the correct values.
+                if (m_builtInURL != null)
+                {
+                    // However, if we are on gnu/classpath we have to pass the handler directly
+                    // because the hidden feature is not there. Funnily, the workaround to pass
+                    // pass the handler directly doesn't work on sun as their handler detects
+                    // that it is not the same as the one inside the url and throws an exception
+                    // Luckily it doesn't do that on gnu/classpath. We detect that we need to
+                    // pass the handler directly by using the m_loopCheck thread local to detect
+                    // that we parseURL has been called inside a call to parseURL.
+                    if (m_loopCheck.get() != null)
+                    {
+                        test = new URL(new URL(m_builtInURL, url.toExternalForm()), spec, (URLStreamHandler) svc);
+                    }
+                    else
+                    {
+                        // Set-up the thread local as we don't expect to be called again until we are
+                        // done. Otherwise, we are on gnu/classpath
+                        m_loopCheck.set(Thread.currentThread());
+                        try
+                        {
+                            test = new URL(new URL(m_builtInURL, url.toExternalForm()), spec);
+                        }
+                        finally
+                        {
+                            m_loopCheck.set(null);
+                        }
+                    }
+                }
+                else
+                {
+                    // We don't have a url with a built-in handler for this but still want to create
+                    // the url with the buil-in handler as we could find one now. This might not
+                    // work for all handlers on sun but it is better then doing nothing.
+                    test = m_action.createURL(url, spec, (URLStreamHandler) svc);
+                }
+
+                super.setURL(url, test.getProtocol(), test.getHost(), test.getPort(),test.getAuthority(),
+                    test.getUserInfo(), test.getPath(), test.getQuery(), test.getRef());
+            }
+            catch (Exception ex)
+            {
+                throw new IllegalStateException("Stream handler unavailable due to: " + ex.getMessage());
+            }
+        }
+    }
+
+    protected boolean sameFile(URL url1, URL url2)
+    {
+        Object svc = getStreamHandlerService();
+        if (svc == null)
+        {
+            throw new IllegalStateException(
+                "Unknown protocol: " + url1.getProtocol());
+        }
+        if (svc instanceof URLStreamHandlerService)
+        {
+            return ((URLStreamHandlerService) svc).sameFile(url1, url2);
+        }
+        try
+        {
+            return ((Boolean) SAME_FILE.invoke(
+                svc, new Object[]{url1, url2})).booleanValue();
+        }
+        catch (Exception ex)
+        {
+            throw new IllegalStateException("Stream handler unavailable due to: " + ex.getMessage());
+        }
+    }
+
+    public void setURL(
+        URL url, String protocol, String host, int port, String authority,
+        String userInfo, String path, String query, String ref)
+    {
+        super.setURL(url, protocol, host, port, authority, userInfo, path, query, ref);
+    }
+
+    public void setURL(
+        URL url, String protocol, String host, int port, String file, String ref)
+    {
+        super.setURL(url, protocol, host, port, file, ref);
+    }
+
+    protected String toExternalForm(URL url)
+    {
+        return toExternalForm(url, getStreamHandlerService());
+    }
+
+    private String toExternalForm(URL url, Object svc)
+    {
+        if (svc == null)
+        {
+            throw new IllegalStateException(
+                "Unknown protocol: " + url.getProtocol());
+        }
+        if (svc instanceof URLStreamHandlerService)
+        {
+            return ((URLStreamHandlerService) svc).toExternalForm(url);
+        }
+        try
+        {
+            try
+            {
+                String result = (String) TO_EXTERNAL_FORM.invoke(
+                    svc, new Object[]{url});
+
+                // mika does return an invalid format if we have a url with the
+                // protocol only (<proto>://null) - we catch this case now
+                if ((result != null) && (result.equals(url.getProtocol() + "://null")))
+                {
+                    result = url.getProtocol() + ":";
+                }
+
+                return result;
+            }
+            catch (InvocationTargetException ex)
+            {
+               Throwable t = ex.getTargetException();
+               if (t instanceof Exception)
+               {
+                   throw (Exception) t;
+               }
+               else if (t instanceof Error)
+               {
+                   throw (Error) t;
+               }
+               else
+               {
+                   throw new IllegalStateException("Unknown throwable: " + t);
+               }
+            }
+        }
+        catch (NullPointerException ex)
+        {
+            // workaround for harmony and possibly J9. The issue is that
+            // their implementation of URLStreamHandler.toExternalForm()
+            // assumes that URL.getFile() doesn't return null but in our
+            // case it can -- hence, we catch the NPE and do the work
+            // ourselvs. The only difference is that we check whether the
+            // URL.getFile() is null or not.
+            StringBuffer answer = new StringBuffer();
+            answer.append(url.getProtocol());
+            answer.append(':');
+            String authority = url.getAuthority();
+            if ((authority != null) && (authority.length() > 0))
+            {
+                answer.append("//"); //$NON-NLS-1$
+                answer.append(url.getAuthority());
+            }
+
+            String file = url.getFile();
+            String ref = url.getRef();
+            if (file != null)
+            {
+                answer.append(file);
+            }
+            if (ref != null)
+            {
+                answer.append('#');
+                answer.append(ref);
+            }
+            return answer.toString();
+        }
+        catch (Exception ex)
+        {
+            throw new IllegalStateException("Stream handler unavailable due to: " + ex.getMessage());
+        }
+    }
+
+    /**
+     * <p>
+     * Private method to retrieve the stream handler service from the
+     * framework instance associated with the current call stack. A
+     * simple service tracker is created and cached for the associated
+     * framework instance when this method is called.
+     * </p>
+     * @return the stream handler service from the framework instance
+     *         associated with the current call stack or <tt>null</tt>
+     *         is no service is available.
+    **/
+    private Object getStreamHandlerService()
+    {
+        try
+        {
+            // Get the framework instance associated with call stack.
+            Object framework = URLHandlers.getFrameworkFromContext();
+         
+            if (framework == null)
+            {
+                return m_builtIn;
+            }
+
+
+            Object service = null;
+            if (framework instanceof Felix)
+            {
+                service = ((Felix) framework).getStreamHandlerService(m_protocol);
+            }
+            else
+            {
+                service = m_action.invoke(
+                    m_action.getDeclaredMethod(framework.getClass(), "getStreamHandlerService", STRING_TYPES),
+                    framework, new Object[]{m_protocol});
+            }
+
+            if (service == null)
+            {
+                return m_builtIn;
+            }
+            if (service instanceof URLStreamHandlerService)
+            {
+                return (URLStreamHandlerService) service;
+            }
+            return (URLStreamHandlerService) Proxy.newProxyInstance(
+                URLStreamHandlerService.class.getClassLoader(),
+                new Class[]{URLStreamHandlerService.class},
+                new URLHandlersStreamHandlerProxy(service, m_action));
+        }
+        catch (ThreadDeath td)
+        {
+            throw td;
+        }
+        catch (Throwable t)
+        {
+            // In case that we are inside tomcat - the problem is that the webapp classloader
+            // creates a new url to load a class. This gets us to this method. Now, if we
+            // trigger a classload while executing tomcat is creating a new url and we end-up with
+            // a loop which is cut short after two iterations (because of a circularclassload).
+            // We catch this exception (and all others) and just return the built-in handler
+            // (if we have any) as this way we at least eventually get started (this just means
+            // that we don't use the potentially provided built-in handler overwrite).
+            return m_builtIn;
+        }
+    }
+
+    public Object invoke(Object obj, Method method, Object[] params)
+        throws Throwable
+    {
+        try
+        {
+
+            Class[] types = method.getParameterTypes();
+            if (m_service == null)
+            {
+                return m_action.invoke(m_action.getMethod(this.getClass(), method.getName(), types), this, params);
+            }
+            if ("parseURL".equals(method.getName()))
+            {
+                types[0] = m_service.getClass().getClassLoader().loadClass(
+                    URLStreamHandlerSetter.class.getName());
+                params[0] = Proxy.newProxyInstance(
+                    m_service.getClass().getClassLoader(), new Class[]{types[0]},
+                    (URLHandlersStreamHandlerProxy) params[0]);
+            }
+            return m_action.invokeDirect(m_action.getDeclaredMethod(m_service.getClass(),
+                method.getName(), types), m_service, params);
+        }
+        catch (Exception ex)
+        {
+            throw ex;
+        }
+    }
+}
diff --git a/src/main/java/org/apache/felix/framework/WovenClassImpl.java b/src/main/java/org/apache/felix/framework/WovenClassImpl.java
new file mode 100644
index 0000000..2f44e5a
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/WovenClassImpl.java
@@ -0,0 +1,376 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework;
+
+import java.security.ProtectionDomain;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+import org.apache.felix.framework.util.ImmutableList;
+import org.apache.felix.framework.util.manifestparser.ManifestParser;
+import org.osgi.framework.AdminPermission;
+import org.osgi.framework.hooks.weaving.WovenClass;
+import org.osgi.framework.wiring.BundleRequirement;
+import org.osgi.framework.wiring.BundleWiring;
+
+class WovenClassImpl implements WovenClass, List<String>
+{
+    private final String m_className;
+    private final BundleWiring m_wiring;
+    private byte[] m_bytes;
+    private List<String> m_imports = new ArrayList<String>();
+    private Class m_definedClass = null;
+    private boolean m_isComplete = false;
+
+    /* package */ WovenClassImpl(String className, BundleWiring wiring, byte[] bytes)
+    {
+        m_className = className;
+        m_wiring = wiring;
+        m_bytes = bytes;
+    }
+
+    synchronized void complete(Class definedClass, byte[] bytes, List<String> imports)
+    {
+        m_isComplete = true;
+        m_definedClass = definedClass;
+        m_bytes = (bytes == null) ? m_bytes : bytes;
+        m_imports = (imports == null)
+            ? ImmutableList.newInstance(m_imports)
+            : ImmutableList.newInstance(imports);
+    }
+
+    public synchronized byte[] getBytes()
+    {
+        SecurityManager sm = System.getSecurityManager();
+        if (sm != null)
+        {
+            sm.checkPermission(new AdminPermission(m_wiring.getBundle(), AdminPermission.WEAVE));
+        }
+        byte[] bytes = m_bytes;
+        if (m_isComplete)
+        {
+            bytes = new byte[m_bytes.length];
+            System.arraycopy(m_bytes, 0, bytes, 0, m_bytes.length);
+        }
+        return bytes;
+    }
+
+    public synchronized void setBytes(byte[] bytes)
+    {
+        SecurityManager sm = System.getSecurityManager();
+        if (sm != null)
+        {
+            sm.checkPermission(new AdminPermission(m_wiring.getBundle(), AdminPermission.WEAVE));
+        }
+        if (m_isComplete)
+        {
+            throw new IllegalStateException(
+                "Cannot change bytes after class weaving is completed.");
+        }
+        else
+        {
+            m_bytes = bytes;
+        }
+    }
+
+    synchronized List<String> getDynamicImportsInternal()
+    {
+        return m_imports;
+    }
+
+    public synchronized List<String> getDynamicImports()
+    {
+        return this;
+    }
+
+    public synchronized boolean isWeavingComplete()
+    {
+        return m_isComplete;
+    }
+
+    public String getClassName()
+    {
+        return m_className;
+    }
+
+    public ProtectionDomain getProtectionDomain()
+    {
+        return ((BundleImpl) m_wiring.getRevision().getBundle()).getProtectionDomain();
+    }
+
+    public synchronized Class<?> getDefinedClass()
+    {
+        return m_definedClass;
+    }
+
+    public BundleWiring getBundleWiring()
+    {
+        return m_wiring;
+    }
+
+    //
+    // List<String> implementation for dynamic imports.
+    //
+    // Design-wise this could be separated out into a separate type,
+    // but since it will only ever be used for this purpose it didn't
+    // appear to make much sense to introduce another type for it.
+
+    public synchronized int size()
+    {
+        return m_imports.size();
+    }
+
+    public synchronized boolean isEmpty()
+    {
+        return m_imports.isEmpty();
+    }
+
+    public synchronized boolean contains(Object o)
+    {
+        return m_imports.contains(o);
+    }
+
+    public synchronized Iterator<String> iterator()
+    {
+        return m_imports.iterator();
+    }
+
+    public synchronized Object[] toArray()
+    {
+        return m_imports.toArray();
+    }
+
+    public synchronized <T> T[] toArray(T[] ts)
+    {
+        return m_imports.toArray(ts);
+    }
+
+    public synchronized boolean add(String s)
+    {
+        SecurityManager sm = System.getSecurityManager();
+        if (sm != null)
+        {
+            sm.checkPermission(new AdminPermission(m_wiring.getBundle(), AdminPermission.WEAVE));
+        }
+        if (s != null)
+        {
+            try
+            {
+                List<BundleRequirement> reqs =
+                    ManifestParser.parseDynamicImportHeader(null, null, s);
+            }
+            catch (Exception ex)
+            {
+                RuntimeException re =
+                    new IllegalArgumentException("Unable to parse dynamic import.");
+                re.initCause(ex);
+                throw re;
+            }
+            return m_imports.add(s);
+        }
+        return false;
+    }
+
+    public synchronized boolean remove(Object o)
+    {
+        SecurityManager sm = System.getSecurityManager();
+        if (sm != null)
+        {
+            sm.checkPermission(new AdminPermission(m_wiring.getBundle(), AdminPermission.WEAVE));
+        }
+        return m_imports.remove(o);
+    }
+
+    public synchronized boolean containsAll(Collection<?> collection)
+    {
+        return m_imports.containsAll(collection);
+    }
+
+    public synchronized boolean addAll(Collection<? extends String> collection)
+    {
+        SecurityManager sm = System.getSecurityManager();
+        if (sm != null)
+        {
+            sm.checkPermission(new AdminPermission(m_wiring.getBundle(), AdminPermission.WEAVE));
+        }
+        for (String s : collection)
+        {
+            try
+            {
+                List<BundleRequirement> reqs =
+                    ManifestParser.parseDynamicImportHeader(null, null, s);
+            }
+            catch (Exception ex)
+            {
+                RuntimeException re =
+                    new IllegalArgumentException("Unable to parse dynamic import.");
+                re.initCause(ex);
+                throw re;
+            }
+        }
+        return m_imports.addAll(collection);
+    }
+
+    public synchronized boolean addAll(int i, Collection<? extends String> collection)
+    {
+        SecurityManager sm = System.getSecurityManager();
+        if (sm != null)
+        {
+            sm.checkPermission(new AdminPermission(m_wiring.getBundle(), AdminPermission.WEAVE));
+        }
+        for (String s : collection)
+        {
+            try
+            {
+                List<BundleRequirement> reqs =
+                    ManifestParser.parseDynamicImportHeader(null, null, s);
+            }
+            catch (Exception ex)
+            {
+                RuntimeException re =
+                    new IllegalArgumentException("Unable to parse dynamic import.");
+                re.initCause(ex);
+                throw re;
+            }
+        }
+        return m_imports.addAll(i, collection);
+    }
+
+    public synchronized boolean removeAll(Collection<?> collection)
+    {
+        SecurityManager sm = System.getSecurityManager();
+        if (sm != null)
+        {
+            sm.checkPermission(new AdminPermission(m_wiring.getBundle(), AdminPermission.WEAVE));
+        }
+        return m_imports.removeAll(collection);
+    }
+
+    public synchronized boolean retainAll(Collection<?> collection)
+    {
+        SecurityManager sm = System.getSecurityManager();
+        if (sm != null)
+        {
+            sm.checkPermission(new AdminPermission(m_wiring.getBundle(), AdminPermission.WEAVE));
+        }
+        return m_imports.retainAll(collection);
+    }
+
+    public synchronized void clear()
+    {
+        m_imports.clear();
+    }
+
+    public synchronized String get(int i)
+    {
+        return m_imports.get(i);
+    }
+
+    public synchronized String set(int i, String s)
+    {
+        SecurityManager sm = System.getSecurityManager();
+        if (sm != null)
+        {
+            sm.checkPermission(new AdminPermission(m_wiring.getBundle(), AdminPermission.WEAVE));
+        }
+        try
+        {
+            List<BundleRequirement> reqs =
+                ManifestParser.parseDynamicImportHeader(null, null, s);
+        }
+        catch (Exception ex)
+        {
+            RuntimeException re =
+                new IllegalArgumentException("Unable to parse dynamic import.");
+            re.initCause(ex);
+            throw re;
+        }
+        return m_imports.set(i, s);
+    }
+
+    public synchronized void add(int i, String s)
+    {
+        SecurityManager sm = System.getSecurityManager();
+        if (sm != null)
+        {
+            sm.checkPermission(new AdminPermission(m_wiring.getBundle(), AdminPermission.WEAVE));
+        }
+        try
+        {
+            List<BundleRequirement> reqs =
+                ManifestParser.parseDynamicImportHeader(null, null, s);
+        }
+        catch (Exception ex)
+        {
+            RuntimeException re =
+                new IllegalArgumentException("Unable to parse dynamic import.");
+            re.initCause(ex);
+            throw re;
+        }
+        m_imports.add(i, s);
+    }
+
+    public synchronized String remove(int i)
+    {
+        SecurityManager sm = System.getSecurityManager();
+        if (sm != null)
+        {
+            sm.checkPermission(new AdminPermission(m_wiring.getBundle(), AdminPermission.WEAVE));
+        }
+        return m_imports.remove(i);
+    }
+
+    public synchronized int indexOf(Object o)
+    {
+        return m_imports.indexOf(o);
+    }
+
+    public synchronized int lastIndexOf(Object o)
+    {
+        return m_imports.lastIndexOf(o);
+    }
+
+    public synchronized ListIterator<String> listIterator()
+    {
+        return m_imports.listIterator();
+    }
+
+    public synchronized ListIterator<String> listIterator(int i)
+    {
+        return m_imports.listIterator(i);
+    }
+
+    public synchronized List<String> subList(int i, int i1)
+    {
+        return m_imports.subList(i, i1);
+    }
+
+    byte[] _getBytes()
+    {
+        byte[] bytes = m_bytes;
+        if (m_isComplete)
+        {
+            bytes = new byte[m_bytes.length];
+            System.arraycopy(m_bytes, 0, bytes, 0, m_bytes.length);
+        }
+        return bytes;
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/felix/framework/cache/BundleArchive.java b/src/main/java/org/apache/felix/framework/cache/BundleArchive.java
new file mode 100644
index 0000000..73c418e
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/cache/BundleArchive.java
@@ -0,0 +1,1378 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.cache;
+
+import java.io.*;
+
+import java.util.Map;
+import java.util.SortedMap;
+import java.util.TreeMap;
+import org.apache.felix.framework.Logger;
+import org.apache.felix.framework.util.WeakZipFileFactory;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.Constants;
+
+/**
+ * <p>
+ * This class is a logical abstraction for a bundle archive. This class,
+ * combined with <tt>BundleCache</tt> and concrete <tt>BundleRevision</tt>
+ * subclasses, implement the bundle cache for Felix. The bundle archive
+ * abstracts the actual bundle content into revisions and the revisions
+ * provide access to the actual bundle content. When a bundle is
+ * installed it has one revision associated with its content. Updating a
+ * bundle adds another revision for the updated content. Any number of
+ * revisions can be associated with a bundle archive. When the bundle
+ * (or framework) is refreshed, then all old revisions are purged and only
+ * the most recent revision is maintained.
+ * </p>
+ * <p>
+ * The content associated with a revision can come in many forms, such as
+ * a standard JAR file or an exploded bundle directory. The bundle archive
+ * is responsible for creating all revision instances during invocations
+ * of the <tt>revise()</tt> method call. Internally, it determines the
+ * concrete type of revision type by examining the location string as an
+ * URL. Currently, it supports standard JAR files, referenced JAR files,
+ * and referenced directories. Examples of each type of URL are, respectively:
+ * </p>
+ * <ul>
+ *   <li><tt>http://www.foo.com/bundle.jar</tt></li>
+ *   <li><tt>reference:file:/foo/bundle.jar</tt></li>
+ *   <li><tt>reference:file:/foo/bundle/</tt></li>
+ * </ul>
+ * <p>
+ * The "<tt>reference:</tt>" notation signifies that the resource should be
+ * used "in place", meaning that they will not be copied. For referenced JAR
+ * files, some resources may still be copied, such as embedded JAR files or
+ * native libraries, but for referenced exploded bundle directories, nothing
+ * will be copied. Currently, reference URLs can only refer to "file:" targets.
+ * </p>
+ * @see org.apache.felix.framework.cache.BundleCache
+ * @see org.apache.felix.framework.cache.BundleRevision
+**/
+public class BundleArchive
+{
+    public static final transient String FILE_PROTOCOL = "file:";
+    public static final transient String REFERENCE_PROTOCOL = "reference:";
+    public static final transient String INPUTSTREAM_PROTOCOL = "inputstream:";
+
+    private static final transient String BUNDLE_INFO_FILE = "bundle.info";
+    private static final transient String REVISION_LOCATION_FILE = "revision.location";
+    private static final transient String REVISION_DIRECTORY = "version";
+    private static final transient String DATA_DIRECTORY = "data";
+
+    private final Logger m_logger;
+    private final Map m_configMap;
+    private final WeakZipFileFactory m_zipFactory;
+    private final File m_archiveRootDir;
+    private final boolean m_isSingleBundleFile;
+
+    private long m_id = -1;
+    private String m_originalLocation = null;
+    private int m_persistentState = -1;
+    private int m_startLevel = -1;
+    private long m_lastModified = -1;
+
+    /**
+     * The refresh count field is used when generating the bundle revision
+     * directory name where native libraries are extracted. This is necessary
+     * because Sun's JVM requires a one-to-one mapping between native libraries
+     * and class loaders where the native library is uniquely identified by its
+     * absolute path in the file system. This constraint creates a problem when
+     * a bundle is refreshed, because it gets a new class loader. Using the
+     * refresh counter to generate the name of the bundle revision directory
+     * resolves this problem because each time bundle is refresh, the native
+     * library will have a unique name. As a result of the unique name, the JVM
+     * will then reload the native library without a problem.
+    **/
+    private long m_refreshCount = -1;
+
+    // Maps a Long revision number to a BundleRevision.
+    private final SortedMap<Long, BundleArchiveRevision> m_revisions
+        = new TreeMap<Long, BundleArchiveRevision>();
+
+    /**
+     * <p>
+     * This constructor is used for creating new archives when a bundle is
+     * installed into the framework. Each archive receives a logger, a root
+     * directory, its associated bundle identifier, the associated bundle
+     * location string, and an input stream from which to read the bundle
+     * content. The root directory is where any required state can be
+     * stored. The input stream may be null, in which case the location is
+     * used as an URL to the bundle content.
+     * </p>
+     * @param logger the logger to be used by the archive.
+     * @param archiveRootDir the archive root directory for storing state.
+     * @param id the bundle identifier associated with the archive.
+     * @param location the bundle location string associated with the archive.
+     * @param is input stream from which to read the bundle content.
+     * @throws Exception if any error occurs.
+    **/
+    public BundleArchive(Logger logger, Map configMap, WeakZipFileFactory zipFactory,
+        File archiveRootDir, long id, int startLevel, String location, InputStream is)
+        throws Exception
+    {
+        m_logger = logger;
+        m_configMap = configMap;
+        m_zipFactory = zipFactory;
+        m_archiveRootDir = archiveRootDir;
+        m_id = id;
+        if (m_id <= 0)
+        {
+            throw new IllegalArgumentException(
+                "Bundle ID cannot be less than or equal to zero.");
+        }
+        m_originalLocation = location;
+        m_persistentState = Bundle.INSTALLED;
+        m_startLevel = startLevel;
+        m_lastModified = System.currentTimeMillis();
+        m_refreshCount = 0;
+
+        String s = (String) m_configMap.get(BundleCache.CACHE_SINGLEBUNDLEFILE_PROP);
+        m_isSingleBundleFile = ((s == null) || s.equalsIgnoreCase("true")) ? true : false;
+
+        // Save state.
+        initialize();
+
+        // Add a revision for the content.
+        reviseInternal(false, new Long(0), m_originalLocation, is);
+    }
+
+    /**
+     * <p>
+     * This constructor is called when an archive for a bundle is being
+     * reconstructed when the framework is restarted. Each archive receives
+     * a logger, a root directory, and its associated bundle identifier.
+     * The root directory is where any required state can be stored.
+     * </p>
+     * @param logger the logger to be used by the archive.
+     * @param archiveRootDir the archive root directory for storing state.
+     * @param configMap configMap for BundleArchive
+     * @throws Exception if any error occurs.
+    **/
+    public BundleArchive(Logger logger, Map configMap, WeakZipFileFactory zipFactory,
+        File archiveRootDir)
+        throws Exception
+    {
+        m_logger = logger;
+        m_configMap = configMap;
+        m_zipFactory = zipFactory;
+        m_archiveRootDir = archiveRootDir;
+
+        String s = (String) m_configMap.get(BundleCache.CACHE_SINGLEBUNDLEFILE_PROP);
+        m_isSingleBundleFile = ((s == null) || s.equalsIgnoreCase("true")) ? true : false;
+
+        if (m_isSingleBundleFile)
+        {
+            readBundleInfo();
+        }
+
+        // Add a revision number for each revision that exists in the file
+        // system. The file system might contain more than one revision if
+        // the bundle was updated in a previous session, but the framework
+        // was not refreshed; this might happen if the framework did not
+        // exit cleanly. We must add the existing revisions so that
+        // they can be properly purged.
+
+        // Find the existing revision directories, which will be named like:
+        //     "${REVISION_DIRECTORY)${refresh-count}.${revision-number}"
+        File[] children = m_archiveRootDir.listFiles();
+        for (File child : children)
+        {
+            if (child.getName().startsWith(REVISION_DIRECTORY)
+                && child.isDirectory())
+            {
+                // Determine the revision number and add it to the revision map.
+                int idx = child.getName().lastIndexOf('.');
+                if (idx > 0)
+                {
+                    Long revNum = Long.decode(child.getName().substring(idx + 1));
+                    m_revisions.put(revNum, null);
+                }
+            }
+        }
+
+        if (m_revisions.isEmpty())
+        {
+            throw new Exception(
+                "No valid revisions in bundle archive directory: "
+                + archiveRootDir);
+        }
+
+        // Remove the last revision number since the call to reviseInternal()
+        // will properly add the most recent bundle revision.
+        // NOTE: We do not actually need to add a real revision object for the
+        // older revisions since they will be purged immediately on framework
+        // startup.
+        Long currentRevNum = m_revisions.lastKey();
+        m_revisions.remove(currentRevNum);
+
+        // Add the revision object for the most recent revision.
+        reviseInternal(true, currentRevNum, getRevisionLocation(currentRevNum), null);
+    }
+
+    /**
+     * <p>
+     * Returns the bundle identifier associated with this archive.
+     * </p>
+     * @return the bundle identifier associated with this archive.
+     * @throws Exception if any error occurs.
+    **/
+    public synchronized long getId() throws Exception
+    {
+        if (m_id <= 0)
+        {
+            m_id = readId();
+        }
+        return m_id;
+    }
+
+    /**
+     * <p>
+     * Returns the location string associated with this archive.
+     * </p>
+     * @return the location string associated with this archive.
+     * @throws Exception if any error occurs.
+    **/
+    public synchronized String getLocation() throws Exception
+    {
+        if (m_originalLocation == null)
+        {
+            m_originalLocation = readLocation();
+        }
+        return m_originalLocation;
+    }
+
+    /**
+     * <p>
+     * Returns the persistent state of this archive. The value returned is
+     * one of the following: <tt>Bundle.INSTALLED</tt>, <tt>Bundle.ACTIVE</tt>,
+     * or <tt>Bundle.UNINSTALLED</tt>.
+     * </p>
+     * @return the persistent state of this archive.
+     * @throws Exception if any error occurs.
+    **/
+    public synchronized int getPersistentState() throws Exception
+    {
+        if (m_persistentState < 0)
+        {
+            m_persistentState = readPersistentState();
+        }
+        return m_persistentState;
+    }
+
+    /**
+     * <p>
+     * Sets the persistent state of this archive. The value is
+     * one of the following: <tt>Bundle.INSTALLED</tt>, <tt>Bundle.ACTIVE</tt>,
+     * or <tt>Bundle.UNINSTALLED</tt>.
+     * </p>
+     * @param state the persistent state value to set for this archive.
+     * @throws Exception if any error occurs.
+    **/
+    public synchronized void setPersistentState(int state) throws Exception
+    {
+        if (m_persistentState != state)
+        {
+            m_persistentState = state;
+            if (m_isSingleBundleFile)
+            {
+                writeBundleInfo();
+            }
+            else
+            {
+                writePersistentState();
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Returns the start level of this archive.
+     * </p>
+     * @return the start level of this archive.
+     * @throws Exception if any error occurs.
+    **/
+    public synchronized int getStartLevel() throws Exception
+    {
+        if (m_startLevel < 0)
+        {
+            m_startLevel = readStartLevel();
+        }
+        return m_startLevel;
+    }
+
+    /**
+     * <p>
+     * Sets the the start level of this archive this archive.
+     * </p>
+     * @param level the start level to set for this archive.
+     * @throws Exception if any error occurs.
+    **/
+    public synchronized void setStartLevel(int level) throws Exception
+    {
+        if (m_startLevel != level)
+        {
+            m_startLevel = level;
+            if (m_isSingleBundleFile)
+            {
+                writeBundleInfo();
+            }
+            else
+            {
+                writeStartLevel();
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Returns the last modification time of this archive.
+     * </p>
+     * @return the last modification time of this archive.
+     * @throws Exception if any error occurs.
+    **/
+    public synchronized long getLastModified() throws Exception
+    {
+        if (m_lastModified < 0)
+        {
+            m_lastModified = readLastModified();
+        }
+        return m_lastModified;
+    }
+
+    /**
+     * <p>
+     * Sets the the last modification time of this archive.
+     * </p>
+     * @param lastModified The time of the last modification to set for
+     *      this archive. According to the OSGi specification this time is
+     *      set each time a bundle is installed, updated or uninstalled.
+     *
+     * @throws Exception if any error occurs.
+    **/
+    public synchronized void setLastModified(long lastModified) throws Exception
+    {
+        if (m_lastModified != lastModified)
+        {
+            m_lastModified = lastModified;
+            if (m_isSingleBundleFile)
+            {
+                writeBundleInfo();
+            }
+            else
+            {
+                writeLastModified();
+            }
+        }
+    }
+
+    /**
+     * This utility method is used to retrieve the current refresh
+     * counter value for the bundle. This value is used when generating
+     * the bundle revision directory name where native libraries are extracted.
+     * This is necessary because Sun's JVM requires a one-to-one mapping
+     * between native libraries and class loaders where the native library
+     * is uniquely identified by its absolute path in the file system. This
+     * constraint creates a problem when a bundle is refreshed, because it
+     * gets a new class loader. Using the refresh counter to generate the name
+     * of the bundle revision directory resolves this problem because each time
+     * bundle is refresh, the native library will have a unique name.
+     * As a result of the unique name, the JVM will then reload the
+     * native library without a problem.
+    **/
+    private long getRefreshCount() throws Exception
+    {
+        // If the refresh counter is not yet initialized, do so now.
+        if (m_refreshCount < 0)
+        {
+            m_refreshCount = readRefreshCount();
+        }
+
+        return m_refreshCount;
+    }
+
+    /**
+     * This utility method is used to retrieve the current refresh
+     * counter value for the bundle. This value is used when generating
+     * the bundle revision directory name where native libraries are extracted.
+     * This is necessary because Sun's JVM requires a one-to-one mapping
+     * between native libraries and class loaders where the native library
+     * is uniquely identified by its absolute path in the file system. This
+     * constraint creates a problem when a bundle is refreshed, because it
+     * gets a new class loader. Using the refresh counter to generate the name
+     * of the bundle revision directory resolves this problem because each time
+     * bundle is refresh, the native library will have a unique name.
+     * As a result of the unique name, the JVM will then reload the
+     * native library without a problem.
+    **/
+    private void setRefreshCount(long count)
+        throws Exception
+    {
+        if (m_refreshCount != count)
+        {
+            m_refreshCount = count;
+            if (m_isSingleBundleFile)
+            {
+                writeBundleInfo();
+            }
+            else
+            {
+                writeRefreshCount();
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Returns a <tt>File</tt> object corresponding to the data file
+     * of the relative path of the specified string.
+     * </p>
+     * @return a <tt>File</tt> object corresponding to the specified file name.
+     * @throws Exception if any error occurs.
+    **/
+    public synchronized File getDataFile(String fileName) throws Exception
+    {
+        // Do some sanity checking.
+        if ((fileName.length() > 0) && (fileName.charAt(0) == File.separatorChar))
+        {
+            throw new IllegalArgumentException(
+                "The data file path must be relative, not absolute.");
+        }
+        else if (fileName.indexOf("..") >= 0)
+        {
+            throw new IllegalArgumentException(
+                "The data file path cannot contain a reference to the \"..\" directory.");
+        }
+
+        // Get bundle data directory.
+        File dataDir = new File(m_archiveRootDir, DATA_DIRECTORY);
+        // Create the data directory if necessary.
+        if (!BundleCache.getSecureAction().fileExists(dataDir))
+        {
+            if (!BundleCache.getSecureAction().mkdir(dataDir))
+            {
+                throw new IOException("Unable to create bundle data directory.");
+            }
+        }
+
+        // Return the data file.
+        return new File(dataDir, fileName);
+    }
+
+    /**
+     * <p>
+     * Returns the current revision object for the archive.
+     * </p>
+     * @return the current revision object for the archive.
+    **/
+    public synchronized Long getCurrentRevisionNumber()
+    {
+        return (m_revisions.isEmpty()) ? null : m_revisions.lastKey();
+    }
+
+    /**
+     * <p>
+     * Returns the current revision object for the archive.
+     * </p>
+     * @return the current revision object for the archive.
+    **/
+    public synchronized BundleArchiveRevision getCurrentRevision()
+    {
+        return (m_revisions.isEmpty()) ? null : m_revisions.get(m_revisions.lastKey());
+    }
+
+    public synchronized boolean isRemovalPending()
+    {
+        return (m_revisions.size() > 1);
+    }
+
+    /**
+     * <p>
+     * This method adds a revision to the archive using the associated
+     * location and input stream. If the input stream is null, then the
+     * location is used a URL to obtain an input stream.
+     * </p>
+     * @param location the location string associated with the revision.
+     * @param is the input stream from which to read the revision.
+     * @throws Exception if any error occurs.
+    **/
+    public synchronized void revise(String location, InputStream is)
+        throws Exception
+    {
+        Long revNum = (m_revisions.isEmpty())
+            ? new Long(0)
+            : new Long(m_revisions.lastKey().longValue() + 1);
+
+        reviseInternal(false, revNum, location, is);
+    }
+
+    /**
+     * Actually adds a revision to the bundle archive. This method is also
+     * used to reload cached bundles too. The revision is given the specified
+     * revision number and is read from the input stream if supplied or from
+     * the location URL if not.
+     * @param isReload if the bundle is being reloaded or not.
+     * @param revNum the revision number of the revision.
+     * @param location the location associated with the revision.
+     * @param is the input stream from which to read the revision.
+     * @throws Exception if any error occurs.
+     */
+    private void reviseInternal(
+        boolean isReload, Long revNum, String location, InputStream is)
+        throws Exception
+    {
+        // If we have an input stream, then we have to use it
+        // no matter what the update location is, so just ignore
+        // the update location and set the location to be input
+        // stream.
+        if (is != null)
+        {
+            location = "inputstream:";
+        }
+
+        // Create a bundle revision for revision number.
+        BundleArchiveRevision revision = createRevisionFromLocation(location, is, revNum);
+        if (revision == null)
+        {
+            throw new Exception("Unable to revise archive.");
+        }
+
+        if (!isReload)
+        {
+            setRevisionLocation(location, revNum);
+        }
+
+        // Add new revision to revision map.
+        m_revisions.put(revNum, revision);
+    }
+
+    /**
+     * <p>
+     * This method undoes the previous revision to the archive; this method will
+     * remove the latest revision from the archive. This method is only called
+     * when there are problems during an update after the revision has been
+     * created, such as errors in the update bundle's manifest. This method
+     * can only be called if there is more than one revision, otherwise there
+     * is nothing to undo.
+     * </p>
+     * @return true if the undo was a success false if there is no previous revision
+     * @throws Exception if any error occurs.
+     */
+    public synchronized boolean rollbackRevise() throws Exception
+    {
+        // Can only undo the revision if there is more than one.
+        if (m_revisions.size() <= 1)
+        {
+            return false;
+        }
+
+        Long revNum = m_revisions.lastKey();
+        BundleArchiveRevision revision = m_revisions.remove(revNum);
+
+        try
+        {
+            revision.close();
+        }
+        catch(Exception ex)
+        {
+           m_logger.log(Logger.LOG_ERROR, getClass().getName() +
+               ": Unable to dispose latest revision", ex);
+        }
+
+        File revisionDir = new File(m_archiveRootDir, REVISION_DIRECTORY +
+            getRefreshCount() + "." + revNum.toString());
+
+        if (BundleCache.getSecureAction().fileExists(revisionDir))
+        {
+            BundleCache.deleteDirectoryTree(revisionDir);
+        }
+
+        return true;
+    }
+
+    private synchronized String getRevisionLocation(Long revNum) throws Exception
+    {
+        InputStream is = null;
+        BufferedReader br = null;
+        try
+        {
+            is = BundleCache.getSecureAction().getFileInputStream(new File(
+                new File(m_archiveRootDir, REVISION_DIRECTORY +
+                getRefreshCount() + "." + revNum.toString()), REVISION_LOCATION_FILE));
+
+            br = new BufferedReader(new InputStreamReader(is));
+            return br.readLine();
+        }
+        finally
+        {
+            if (br != null) br.close();
+            if (is != null) is.close();
+        }
+    }
+
+    private synchronized void setRevisionLocation(String location, Long revNum)
+        throws Exception
+    {
+        // Save current revision location.
+        OutputStream os = null;
+        BufferedWriter bw = null;
+        try
+        {
+            os = BundleCache.getSecureAction()
+                .getFileOutputStream(new File(
+                    new File(m_archiveRootDir, REVISION_DIRECTORY +
+                    getRefreshCount() + "." + revNum.toString()), REVISION_LOCATION_FILE));
+            bw = new BufferedWriter(new OutputStreamWriter(os));
+            bw.write(location, 0, location.length());
+        }
+        finally
+        {
+            if (bw != null) bw.close();
+            if (os != null) os.close();
+        }
+    }
+
+    public synchronized void close()
+    {
+        // Get the current revision count.
+        for (BundleArchiveRevision revision : m_revisions.values())
+        {
+            // Dispose of the revision, but this might be null in certain
+            // circumstances, such as if this bundle archive was created
+            // for an existing bundle that was updated, but not refreshed
+            // due to a system crash; see the constructor code for details.
+            if (revision != null)
+            {
+                try
+                {
+                    revision.close();
+                }
+                catch (Exception ex)
+                {
+                    m_logger.log(
+                        Logger.LOG_ERROR,
+                            "Unable to close revision - "
+                            + revision.getRevisionRootDir(), ex);
+                }
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * This method closes any revisions and deletes the bundle archive directory.
+     * </p>
+     * @throws Exception if any error occurs.
+    **/
+    public synchronized void closeAndDelete()
+    {
+        // Close the revisions and delete the archive directory.
+        close();
+        if (!BundleCache.deleteDirectoryTree(m_archiveRootDir))
+        {
+            m_logger.log(
+                Logger.LOG_ERROR,
+                "Unable to delete archive directory - " + m_archiveRootDir);
+        }
+    }
+
+    /**
+     * <p>
+     * This method removes all old revisions associated with the archive
+     * and keeps only the current revision.
+     * </p>
+     * @throws Exception if any error occurs.
+    **/
+    public synchronized void purge() throws Exception
+    {
+        // Remember current revision number.
+        Long currentRevNum = getCurrentRevisionNumber();
+
+        // Record whether the current revision has native libraries, which
+        // we'll use later to determine if we need to rename its directory.
+        boolean hasNativeLibs = getCurrentRevision().getManifestHeader()
+            .containsKey(Constants.BUNDLE_NATIVECODE);
+
+        // Close all revisions and then delete all but the current revision.
+        // We don't delete it the current revision, because we want to rename it
+        // to the new refresh level.
+        close();
+
+        // Delete all old revisions.
+        long refreshCount = getRefreshCount();
+        for (Long revNum : m_revisions.keySet())
+        {
+            if (!revNum.equals(currentRevNum))
+            {
+                File revisionDir = new File(
+                    m_archiveRootDir,
+                    REVISION_DIRECTORY + refreshCount + "." + revNum.toString());
+                if (BundleCache.getSecureAction().fileExists(revisionDir))
+                {
+                    BundleCache.deleteDirectoryTree(revisionDir);
+                }
+            }
+        }
+
+        // If the revision has native libraries, then rename its directory
+        // to avoid the issue of being unable to load the same native library
+        // into two different class loaders.
+        if (hasNativeLibs)
+        {
+            // Increment the refresh count.
+            setRefreshCount(refreshCount + 1);
+
+            // Rename the current revision directory to the new refresh level.
+            File currentDir = new File(m_archiveRootDir,
+                REVISION_DIRECTORY + (refreshCount + 1) + "." + currentRevNum.toString());
+            File revisionDir = new File(m_archiveRootDir,
+                REVISION_DIRECTORY + refreshCount + "." + currentRevNum.toString());
+            BundleCache.getSecureAction().renameFile(revisionDir, currentDir);
+        }
+
+        // Clear the revision map since they are all invalid now.
+        m_revisions.clear();
+
+        // Recreate the revision for the current location.
+        BundleArchiveRevision revision = createRevisionFromLocation(
+            getRevisionLocation(currentRevNum), null, currentRevNum);
+        // Add new revision to the revision map.
+        m_revisions.put(currentRevNum, revision);
+    }
+
+    /**
+     * <p>
+     * Initializes the bundle archive object by creating the archive
+     * root directory and saving the initial state.
+     * </p>
+     * @throws Exception if any error occurs.
+    **/
+    private void initialize() throws Exception
+    {
+        OutputStream os = null;
+        BufferedWriter bw = null;
+
+        try
+        {
+            // If the archive directory exists, then we don't
+            // need to initialize since it has already been done.
+            if (BundleCache.getSecureAction().fileExists(m_archiveRootDir))
+            {
+                return;
+            }
+
+            // Create archive directory, if it does not exist.
+            if (!BundleCache.getSecureAction().mkdir(m_archiveRootDir))
+            {
+                m_logger.log(
+                    Logger.LOG_ERROR,
+                    getClass().getName() + ": Unable to create archive directory.");
+                throw new IOException("Unable to create archive directory.");
+            }
+
+            if (m_isSingleBundleFile)
+            {
+                writeBundleInfo();
+            }
+            else
+            {
+                writeId();
+                writeLocation();
+                writePersistentState();
+                writeStartLevel();
+                writeLastModified();
+            }
+        }
+        finally
+        {
+            if (bw != null) bw.close();
+            if (os != null) os.close();
+        }
+    }
+
+    /**
+     * <p>
+     * Creates a revision based on the location string and/or input stream.
+     * </p>
+     * @return the location string associated with this archive.
+    **/
+    private BundleArchiveRevision createRevisionFromLocation(
+        String location, InputStream is, Long revNum)
+        throws Exception
+    {
+        // The revision directory is named using the refresh count and
+        // the revision number. The revision number is an increasing
+        // counter of the number of times the bundle was revised.
+        // The refresh count is necessary due to how native libraries
+        // are handled in Java; needless to say, every time a bundle is
+        // refreshed we must change the name of its native libraries so
+        // that we can reload them. Thus, we use the refresh counter as
+        // a way to change the name of the revision directory to give
+        // native libraries new absolute names.
+        File revisionRootDir = new File(m_archiveRootDir,
+            REVISION_DIRECTORY + getRefreshCount() + "." + revNum.toString());
+
+        BundleArchiveRevision result = null;
+
+        try
+        {
+            // Check if the location string represents a reference URL.
+            if ((location != null) && location.startsWith(REFERENCE_PROTOCOL))
+            {
+                // Reference URLs only support the file protocol.
+                location = location.substring(REFERENCE_PROTOCOL.length());
+                if (!location.startsWith(FILE_PROTOCOL))
+                {
+                    throw new IOException("Reference URLs can only be files: " + location);
+                }
+
+                // Decode any URL escaped sequences.
+                location = decode(location);
+
+                // Make sure the referenced file exists.
+                File file = new File(location.substring(FILE_PROTOCOL.length()));
+                if (!BundleCache.getSecureAction().fileExists(file))
+                {
+                    throw new IOException("Referenced file does not exist: " + file);
+                }
+
+                // If the referenced file is a directory, then create a directory
+                // revision; otherwise, create a JAR revision with the reference
+                // flag set to true.
+                if (BundleCache.getSecureAction().isFileDirectory(file))
+                {
+                    result = new DirectoryRevision(m_logger, m_configMap,
+                        m_zipFactory, revisionRootDir, location);
+                }
+                else
+                {
+                    result = new JarRevision(m_logger, m_configMap,
+                        m_zipFactory, revisionRootDir, location, true, null);
+                }
+            }
+            else if (location.startsWith(INPUTSTREAM_PROTOCOL))
+            {
+                // Assume all input streams point to JAR files.
+                result = new JarRevision(m_logger, m_configMap,
+                    m_zipFactory, revisionRootDir, location, false, is);
+            }
+            else
+            {
+                // Anything else is assumed to be a URL to a JAR file.
+                result = new JarRevision(m_logger, m_configMap,
+                    m_zipFactory, revisionRootDir, location, false, null);
+            }
+        }
+        catch (Exception ex)
+        {
+            if (BundleCache.getSecureAction().fileExists(revisionRootDir))
+            {
+                if (!BundleCache.deleteDirectoryTree(revisionRootDir))
+                {
+                    m_logger.log(
+                        Logger.LOG_ERROR,
+                        getClass().getName()
+                            + ": Unable to delete revision directory - "
+                            + revisionRootDir);
+                }
+            }
+            throw ex;
+        }
+
+        return result;
+    }
+
+    // Method from Harmony java.net.URIEncoderDecoder (luni subproject)
+    // used by URI to decode uri components.
+    private static String decode(String s) throws UnsupportedEncodingException
+    {
+        StringBuffer result = new StringBuffer();
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        for (int i = 0; i < s.length();)
+        {
+            char c = s.charAt(i);
+            if (c == '%')
+            {
+                out.reset();
+                do
+                {
+                    if ((i + 2) >= s.length())
+                    {
+                        throw new IllegalArgumentException(
+                            "Incomplete % sequence at: " + i);
+                    }
+                    int d1 = Character.digit(s.charAt(i + 1), 16);
+                    int d2 = Character.digit(s.charAt(i + 2), 16);
+                    if ((d1 == -1) || (d2 == -1))
+                    {
+                        throw new IllegalArgumentException("Invalid % sequence ("
+                            + s.substring(i, i + 3)
+                            + ") at: " + String.valueOf(i));
+                    }
+                    out.write((byte) ((d1 << 4) + d2));
+                    i += 3;
+                }
+                while ((i < s.length()) && (s.charAt(i) == '%'));
+                result.append(out.toString("UTF-8"));
+                continue;
+            }
+            result.append(c);
+            i++;
+        }
+        return result.toString();
+    }
+
+    private void readBundleInfo() throws Exception
+    {
+        File infoFile = new File(m_archiveRootDir, BUNDLE_INFO_FILE);
+
+        // Read the bundle start level.
+        InputStream is = null;
+        BufferedReader br= null;
+        try
+        {
+            is = BundleCache.getSecureAction()
+                .getFileInputStream(infoFile);
+            br = new BufferedReader(new InputStreamReader(is));
+
+            // Read id.
+            m_id = Long.parseLong(br.readLine());
+            // Read location.
+            m_originalLocation = br.readLine();
+            // Read state.
+            m_persistentState = Integer.parseInt(br.readLine());
+            // Read start level.
+            m_startLevel = Integer.parseInt(br.readLine());
+            // Read last modified.
+            m_lastModified = Long.parseLong(br.readLine());
+            // Read refresh count.
+            m_refreshCount = Long.parseLong(br.readLine());
+        }
+        catch (FileNotFoundException ex)
+        {
+            // If there wasn't an info file, then maybe this is an old-style
+            // bundle cache, so try to read the files individually. We can
+            // delete this eventually.
+            m_id = readId();
+            m_originalLocation = readLocation();
+            m_persistentState = readPersistentState();
+            m_startLevel = readStartLevel();
+            m_lastModified = readLastModified();
+            m_refreshCount = readRefreshCount();
+        }
+        finally
+        {
+            if (br != null) br.close();
+            if (is != null) is.close();
+        }
+    }
+
+    private void writeBundleInfo() throws Exception
+    {
+        // Write the bundle start level.
+        OutputStream os = null;
+        BufferedWriter bw = null;
+        try
+        {
+            os = BundleCache.getSecureAction()
+                .getFileOutputStream(new File(m_archiveRootDir, BUNDLE_INFO_FILE));
+            bw = new BufferedWriter(new OutputStreamWriter(os));
+
+            // Write id.
+            String s = Long.toString(m_id);
+            bw.write(s, 0, s.length());
+            bw.newLine();
+            // Write location.
+            s = (m_originalLocation == null) ? "" : m_originalLocation;
+            bw.write(s, 0, s.length());
+            bw.newLine();
+            // Write state.
+            s = Integer.toString(m_persistentState);
+            bw.write(s, 0, s.length());
+            bw.newLine();
+            // Write start level.
+            s = Integer.toString(m_startLevel);
+            bw.write(s, 0, s.length());
+            bw.newLine();
+            // Write last modified.
+            s = Long.toString(m_lastModified);
+            bw.write(s, 0, s.length());
+            bw.newLine();
+            // Write refresh count.
+            s = Long.toString(m_refreshCount);
+            bw.write(s, 0, s.length());
+            bw.newLine();
+        }
+        catch (IOException ex)
+        {
+            m_logger.log(
+                Logger.LOG_ERROR,
+                getClass().getName() + ": Unable to cache bundle info - " + ex);
+            throw ex;
+        }
+        finally
+        {
+            if (bw != null) bw.close();
+            if (os != null) os.close();
+        }
+    }
+
+    //
+    // Deprecated bundle cache format to be deleted eventually.
+    //
+
+    private static final transient String BUNDLE_ID_FILE = "bundle.id";
+    private static final transient String BUNDLE_LOCATION_FILE = "bundle.location";
+    private static final transient String BUNDLE_STATE_FILE = "bundle.state";
+    private static final transient String BUNDLE_START_LEVEL_FILE = "bundle.startlevel";
+    private static final transient String BUNDLE_LASTMODIFIED_FILE = "bundle.lastmodified";
+    private static final transient String REFRESH_COUNTER_FILE = "refresh.counter";
+
+    private void writeId() throws Exception
+    {
+        OutputStream os = BundleCache.getSecureAction()
+            .getFileOutputStream(new File(m_archiveRootDir, BUNDLE_ID_FILE));
+        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(os));
+        bw.write(Long.toString(m_id), 0, Long.toString(m_id).length());
+        bw.close();
+        os.close();
+    }
+
+    private long readId() throws Exception
+    {
+        long id;
+
+        InputStream is = null;
+        BufferedReader br = null;
+        try
+        {
+            is = BundleCache.getSecureAction()
+                .getFileInputStream(new File(m_archiveRootDir, BUNDLE_ID_FILE));
+            br = new BufferedReader(new InputStreamReader(is));
+            id = Long.parseLong(br.readLine());
+        }
+        catch (FileNotFoundException ex)
+        {
+            // HACK: Get the bundle identifier from the archive root directory
+            // name, which is of the form "bundle<id>" where <id> is the bundle
+            // identifier numbers. This is a hack to deal with old archives that
+            // did not save their bundle identifier, but instead had it passed
+            // into them. Eventually, this can be removed.
+            id = Long.parseLong(
+                m_archiveRootDir.getName().substring(
+                    BundleCache.BUNDLE_DIR_PREFIX.length()));
+        }
+        finally
+        {
+            if (br != null) br.close();
+            if (is != null) is.close();
+        }
+
+        return id;
+    }
+
+    private void writeLocation() throws Exception
+    {
+        OutputStream os = BundleCache.getSecureAction()
+            .getFileOutputStream(new File(m_archiveRootDir, BUNDLE_LOCATION_FILE));
+        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(os));
+        bw.write(m_originalLocation, 0, m_originalLocation.length());
+        bw.close();
+        os.close();
+    }
+
+    private String readLocation() throws Exception
+    {
+        InputStream is = null;
+        BufferedReader br = null;
+        try
+        {
+            is = BundleCache.getSecureAction()
+                .getFileInputStream(new File(m_archiveRootDir, BUNDLE_LOCATION_FILE));
+            br = new BufferedReader(new InputStreamReader(is));
+            return br.readLine();
+        }
+        finally
+        {
+            if (br != null) br.close();
+            if (is != null) is.close();
+        }
+    }
+
+    private static final transient String ACTIVE_STATE = "active";
+    private static final transient String STARTING_STATE = "starting";
+    private static final transient String INSTALLED_STATE = "installed";
+    private static final transient String UNINSTALLED_STATE = "uninstalled";
+
+    private void writePersistentState() throws Exception
+    {
+        OutputStream os = null;
+        BufferedWriter bw = null;
+        try
+        {
+            os = BundleCache.getSecureAction()
+                .getFileOutputStream(new File(m_archiveRootDir, BUNDLE_STATE_FILE));
+            bw = new BufferedWriter(new OutputStreamWriter(os));
+            String s = null;
+            switch (m_persistentState)
+            {
+                case Bundle.ACTIVE:
+                    s = ACTIVE_STATE;
+                    break;
+                case Bundle.STARTING:
+                    s = STARTING_STATE;
+                    break;
+                case Bundle.UNINSTALLED:
+                    s = UNINSTALLED_STATE;
+                    break;
+                default:
+                    s = INSTALLED_STATE;
+                    break;
+            }
+            bw.write(s, 0, s.length());
+        }
+        catch (IOException ex)
+        {
+            m_logger.log(
+                Logger.LOG_ERROR,
+                getClass().getName() + ": Unable to record state - " + ex);
+            throw ex;
+        }
+        finally
+        {
+            if (bw != null) bw.close();
+            if (os != null) os.close();
+        }
+    }
+
+    private int readPersistentState() throws Exception
+    {
+        int state = Bundle.INSTALLED;
+
+        // Get bundle state file.
+        File stateFile = new File(m_archiveRootDir, BUNDLE_STATE_FILE);
+
+        // If the state file doesn't exist, then
+        // assume the bundle was installed.
+        if (BundleCache.getSecureAction().fileExists(stateFile))
+        {
+            // Read the bundle state.
+            InputStream is = null;
+            BufferedReader br = null;
+            try
+            {
+                is = BundleCache.getSecureAction()
+                    .getFileInputStream(stateFile);
+                br = new BufferedReader(new InputStreamReader(is));
+                String s = br.readLine();
+                if ((s != null) && s.equals(ACTIVE_STATE))
+                {
+                    state = Bundle.ACTIVE;
+                }
+                else if ((s != null) && s.equals(STARTING_STATE))
+                {
+                    state = Bundle.STARTING;
+                }
+                else if ((s != null) && s.equals(UNINSTALLED_STATE))
+                {
+                    state = Bundle.UNINSTALLED;
+                }
+                else
+                {
+                    state = Bundle.INSTALLED;
+                }
+            }
+            catch (Exception ex)
+            {
+                state = Bundle.INSTALLED;
+            }
+            finally
+            {
+                if (br != null) br.close();
+                if (is != null) is.close();
+            }
+        }
+
+        return state;
+    }
+
+    private void writeStartLevel() throws Exception
+    {
+        OutputStream os = null;
+        BufferedWriter bw = null;
+        try
+        {
+            os = BundleCache.getSecureAction()
+                .getFileOutputStream(new File(m_archiveRootDir, BUNDLE_START_LEVEL_FILE));
+            bw = new BufferedWriter(new OutputStreamWriter(os));
+            String s = Integer.toString(m_startLevel);
+            bw.write(s, 0, s.length());
+        }
+        catch (IOException ex)
+        {
+            m_logger.log(
+                Logger.LOG_ERROR,
+                getClass().getName() + ": Unable to record start level - " + ex);
+            throw ex;
+        }
+        finally
+        {
+            if (bw != null) bw.close();
+            if (os != null) os.close();
+        }
+    }
+
+    private int readStartLevel() throws Exception
+    {
+        int level = -1;
+
+        // Get bundle start level file.
+        File levelFile = new File(m_archiveRootDir, BUNDLE_START_LEVEL_FILE);
+
+        // If the start level file doesn't exist, then
+        // return an error.
+        if (!BundleCache.getSecureAction().fileExists(levelFile))
+        {
+            level = -1;
+        }
+        else
+        {
+            // Read the bundle start level.
+            InputStream is = null;
+            BufferedReader br= null;
+            try
+            {
+                is = BundleCache.getSecureAction()
+                    .getFileInputStream(levelFile);
+                br = new BufferedReader(new InputStreamReader(is));
+                level = Integer.parseInt(br.readLine());
+            }
+            finally
+            {
+                if (br != null) br.close();
+                if (is != null) is.close();
+            }
+        }
+        return level;
+    }
+
+    private void writeLastModified() throws Exception
+    {
+        OutputStream os = null;
+        BufferedWriter bw = null;
+        try
+        {
+            os = BundleCache.getSecureAction()
+                .getFileOutputStream(new File(m_archiveRootDir, BUNDLE_LASTMODIFIED_FILE));
+            bw = new BufferedWriter(new OutputStreamWriter(os));
+            String s = Long.toString(m_lastModified);
+            bw.write(s, 0, s.length());
+        }
+        catch (IOException ex)
+        {
+            m_logger.log(
+                Logger.LOG_ERROR,
+                getClass().getName() + ": Unable to record start level - " + ex);
+            throw ex;
+        }
+        finally
+        {
+            if (bw != null) bw.close();
+            if (os != null) os.close();
+        }
+    }
+
+    private long readLastModified() throws Exception
+    {
+        long last = 0;
+
+        InputStream is = null;
+        BufferedReader br = null;
+        try
+        {
+            is = BundleCache.getSecureAction()
+                .getFileInputStream(new File(m_archiveRootDir, BUNDLE_LASTMODIFIED_FILE));
+            br = new BufferedReader(new InputStreamReader(is));
+            last = Long.parseLong(br.readLine());
+        }
+        catch (Exception ex)
+        {
+            last = 0;
+        }
+        finally
+        {
+            if (br != null) br.close();
+            if (is != null) is.close();
+        }
+
+        return last;
+    }
+
+    private void writeRefreshCount() throws Exception
+    {
+        OutputStream os = null;
+        BufferedWriter bw = null;
+        try
+        {
+            os = BundleCache.getSecureAction()
+                .getFileOutputStream(new File(m_archiveRootDir, REFRESH_COUNTER_FILE));
+            bw = new BufferedWriter(new OutputStreamWriter(os));
+            String s = Long.toString(m_refreshCount);
+            bw.write(s, 0, s.length());
+        }
+        catch (IOException ex)
+        {
+            m_logger.log(
+                Logger.LOG_ERROR,
+                getClass().getName() + ": Unable to write refresh count: " + ex);
+            throw ex;
+        }
+        finally
+        {
+            if (bw != null) bw.close();
+            if (os != null) os.close();
+        }
+    }
+
+    private long readRefreshCount() throws Exception
+    {
+        long count = 0;
+
+        InputStream is = null;
+        BufferedReader br = null;
+        try
+        {
+            is = BundleCache.getSecureAction()
+                .getFileInputStream(new File(m_archiveRootDir, REFRESH_COUNTER_FILE));
+            br = new BufferedReader(new InputStreamReader(is));
+            count = Long.parseLong(br.readLine());
+        }
+        catch (Exception ex)
+        {
+            count = 0;
+        }
+        finally
+        {
+            if (br != null) br.close();
+            if (is != null) is.close();
+        }
+
+        return count;
+    }
+}
diff --git a/src/main/java/org/apache/felix/framework/cache/BundleArchiveRevision.java b/src/main/java/org/apache/felix/framework/cache/BundleArchiveRevision.java
new file mode 100644
index 0000000..d1a5664
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/cache/BundleArchiveRevision.java
@@ -0,0 +1,142 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.cache;
+
+import java.io.File;
+import java.util.Map;
+
+import org.apache.felix.framework.Logger;
+
+/**
+ * <p>
+ * This class implements an abstract revision of a bundle archive. A revision
+ * is an abstraction of a bundle's actual content and is associated with a
+ * parent bundle archive. A bundle archive may have multiple revisions assocaited
+ * with it at one time, since updating a bundle results in a new version of the
+ * bundle's content until the bundle is refreshed. Upon a refresh, then old
+ * revisions are then purged. This abstract class is the base class for all
+ * concrete types of revisions, such as ones for a JAR file or directories. All
+ * revisions are assigned a root directory into which all of their state should
+ * be stored, if necessary. Clean up of this directory is the responsibility
+ * of the parent bundle archive and not of the revision itself.
+ * </p>
+ * @see org.apache.felix.framework.cache.BundleCache
+ * @see org.apache.felix.framework.cache.BundleArchive
+**/
+public abstract class BundleArchiveRevision
+{
+    private final Logger m_logger;
+    private final Map m_configMap;
+    private final File m_revisionRootDir;
+    private final String m_location;
+
+    /**
+     * <p>
+     * This class is abstract and cannot be created. It represents a revision
+     * of a bundle, i.e., its content. A revision is associated with a particular
+     * location string, which is typically in URL format. Subclasses of this
+     * class provide particular functionality, such as a revision in the form
+     * of a JAR file or a directory. Each revision subclass is expected to use
+     * the root directory associated with the abstract revision instance to
+     * store any state; this will ensure that resources used by the revision are
+     * properly freed when the revision is no longer needed.
+     * </p>
+     * @param logger a logger for use by the revision.
+     * @param revisionRootDir the root directory to be used by the revision
+     *        subclass for storing any state.
+     * @param location the location string associated with the revision.
+     * @param trustedCaCerts the trusted CA certificates if any.
+     * @throws Exception if any errors occur.
+    **/
+    public BundleArchiveRevision(Logger logger, Map configMap, File revisionRootDir, String location)
+        throws Exception
+    {
+        m_logger = logger;
+        m_configMap = configMap;
+        m_revisionRootDir = revisionRootDir;
+        m_location = location;
+    }
+
+    /**
+     * <p>
+     * Returns the logger for this revision.
+     * </p>
+     * @return the logger instance for this revision.
+    **/
+    public Logger getLogger()
+    {
+        return m_logger;
+    }
+
+    /**
+     * <p>
+     * Returns the configuration map for this revision.
+     * </p>
+     * @return the configuration map for this revision.
+    **/
+    public Map getConfig()
+    {
+        return m_configMap;
+    }
+
+    /**
+     * <p>
+     * Returns the root directory for this revision.
+     * </p>
+     * @return the root directory for this revision.
+    **/
+    public File getRevisionRootDir()
+    {
+        return m_revisionRootDir;
+    }
+
+    /**
+     * <p>
+     * Returns the location string this revision.
+     * </p>
+     * @return the location string for this revision.
+    **/
+    public String getLocation()
+    {
+        return m_location;
+    }
+
+    /**
+     * <p>
+     * Returns the main attributes of the JAR file manifest header of the
+     * revision. The returned map is case insensitive.
+     * </p>
+     * @return the case-insensitive JAR file manifest header of the revision.
+     * @throws java.lang.Exception if any error occurs.
+    **/
+    public abstract Map getManifestHeader() throws Exception;
+
+    public abstract Content getContent() throws Exception;
+
+    /**
+     * <p>
+     * This method is called when the revision is no longer needed. The directory
+     * associated with the revision will automatically be removed for each
+     * revision, so this method only needs to be concerned with other issues,
+     * such as open files.
+     * </p>
+     * @throws Exception if any error occurs.
+    **/
+    protected abstract void close() throws Exception;
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/felix/framework/cache/BundleCache.java b/src/main/java/org/apache/felix/framework/cache/BundleCache.java
new file mode 100644
index 0000000..ee44091
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/cache/BundleCache.java
@@ -0,0 +1,440 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.cache;
+
+import java.io.*;
+import java.nio.channels.FileChannel;
+import java.nio.channels.FileLock;
+import java.util.*;
+
+import org.apache.felix.framework.Logger;
+import org.apache.felix.framework.util.SecureAction;
+import org.apache.felix.framework.util.WeakZipFileFactory;
+import org.osgi.framework.Constants;
+
+/**
+ * <p>
+ * This class, combined with <tt>BundleArchive</tt>, and concrete
+ * <tt>BundleRevision</tt> subclasses, implement the Felix bundle cache.
+ * It is possible to configure the default behavior of this class by
+ * passing properties into Felix' constructor. The configuration properties
+ * for this class are (properties starting with "<tt>felix</tt>" are specific
+ * to Felix, while those starting with "<tt>org.osgi</tt>" are standard OSGi
+ * properties):
+ * </p>
+ * <ul>
+ *   <li><tt>felix.cache.filelimit</tt> - The integer value of this string
+ *       sets an upper limit on how many files the cache will open. The default
+ *       value is zero, which means there is no limit.
+ *   </li>
+ *   <li><tt>org.osgi.framework.storage</tt> - Sets the directory to use as
+ *       the bundle cache; by default bundle cache directory is
+ *       <tt>felix-cache</tt> in the current working directory. The value
+ *       should be a valid directory name. The directory name can be either absolute
+ *       or relative. Relative directory names are relative to the current working
+ *       directory. The specified directory will be created if it does
+ *       not exist.
+ *   </li>
+ *   <li><tt>felix.cache.rootdir</tt> - Sets the root directory to use to
+ *       calculate the bundle cache directory for relative directory names. If
+ *       <tt>org.osgi.framework.storage</tt> is set to a relative name, by
+ *       default it is relative to the current working directory. If this
+ *       property is set, then it will be calculated as being relative to
+ *       the specified root directory.
+ *   </li>
+ *   <li><tt>felix.cache.locking</tt> - Enables or disables bundle cache locking,
+ *       which is used to prevent concurrent access to the bundle cache. This is
+ *       enabled by default, but on older/smaller JVMs file channel locking is
+ *       not available; set this property to <tt>false</tt> to disable it.
+ *   </li>
+ *   <li><tt>felix.cache.bufsize</tt> - Sets the buffer size to be used by
+ *       the cache; the default value is 4096. The integer value of this
+ *       string provides control over the size of the internal buffer of the
+ *       disk cache for performance reasons.
+ *   </li>
+ * <p>
+ * For specific information on how to configure the Felix framework, refer
+ * to the Felix framework usage documentation.
+ * </p>
+ * @see org.apache.felix.framework.cache.BundleArchive
+**/
+public class BundleCache
+{
+    public static final String CACHE_BUFSIZE_PROP = "felix.cache.bufsize";
+    public static final String CACHE_ROOTDIR_PROP = "felix.cache.rootdir";
+    public static final String CACHE_LOCKING_PROP = "felix.cache.locking";
+    public static final String CACHE_FILELIMIT_PROP = "felix.cache.filelimit";
+    // TODO: CACHE - This should eventually be removed along with the code
+    //       supporting the old multi-file bundle cache format.
+    public static final String CACHE_SINGLEBUNDLEFILE_PROP = "felix.cache.singlebundlefile";
+
+    protected static transient int BUFSIZE = 4096;
+
+    private static transient final String CACHE_DIR_NAME = "felix-cache";
+    private static transient final String CACHE_ROOTDIR_DEFAULT = ".";
+    private static transient final String CACHE_LOCK_NAME = "cache.lock";
+    static transient final String BUNDLE_DIR_PREFIX = "bundle";
+
+    private static final SecureAction m_secureAction = new SecureAction();
+
+    private final Logger m_logger;
+    private final Map m_configMap;
+    private final WeakZipFileFactory m_zipFactory;
+    private final Object m_lock;
+
+    public BundleCache(Logger logger, Map configMap)
+        throws Exception
+    {
+        m_logger = logger;
+        m_configMap = configMap;
+
+        int limit = 0;
+        String limitStr = (String) m_configMap.get(CACHE_FILELIMIT_PROP);
+        if (limitStr != null)
+        {
+            try
+            {
+                limit = Integer.parseInt(limitStr);
+            }
+            catch (NumberFormatException ex)
+            {
+                limit = 0;
+            }
+        }
+        m_zipFactory = new WeakZipFileFactory(limit);
+
+        // Create the cache directory, if it does not exist.
+        File cacheDir = determineCacheDir(m_configMap);
+        if (!getSecureAction().fileExists(cacheDir))
+        {
+            if (!getSecureAction().mkdirs(cacheDir))
+            {
+                m_logger.log(
+                    Logger.LOG_ERROR,
+                    "Unable to create cache directory: " + cacheDir);
+                throw new RuntimeException("Unable to create cache directory.");
+            }
+        }
+
+        Object locking = m_configMap.get(CACHE_LOCKING_PROP);
+        locking = (locking == null)
+            ? Boolean.TRUE.toString()
+            : locking.toString().toLowerCase();
+        if (((String) locking).equals(Boolean.TRUE.toString()))
+        {
+            File lockFile = new File(cacheDir, CACHE_LOCK_NAME);
+            FileChannel fc = null;
+            FileOutputStream fos = null;
+            try
+            {
+                if (!getSecureAction().fileExists(lockFile))
+                {
+                    fos = getSecureAction().getFileOutputStream(lockFile);
+                    fc = fos.getChannel();
+                }
+                else
+                {
+                    fos = getSecureAction().getFileOutputStream(lockFile);
+                    fc = fos.getChannel();
+                }
+            }
+            catch (Exception ex)
+            {
+                try
+                {
+                    if (fos != null) fos.close();
+                    if (fc != null) fc.close();
+                }
+                catch (Exception ex2)
+                {
+                    // Ignore.
+                }
+                throw new Exception("Unable to create bundle cache lock file: " + ex);
+            }
+            try
+            {
+                m_lock = fc.tryLock();
+            }
+            catch (Exception ex)
+            {
+                throw new Exception("Unable to lock bundle cache: " + ex);
+            }
+        }
+        else
+        {
+            m_lock = null;
+        }
+    }
+
+    public synchronized void release()
+    {
+        if (m_lock != null)
+        {
+            try
+            {
+                ((FileLock) m_lock).release();
+                ((FileLock) m_lock).channel().close();
+            }
+            catch (Exception ex)
+            {
+                // Not much we can do here, just log it.
+                m_logger.log(
+                    Logger.LOG_WARNING,
+                    "Exception releasing bundle cache.", ex);
+            }
+        }
+    }
+
+    /* package */ static SecureAction getSecureAction()
+    {
+        return m_secureAction;
+    }
+
+    public synchronized void delete() throws Exception
+    {
+        // Delete the cache directory.
+        File cacheDir = determineCacheDir(m_configMap);
+        deleteDirectoryTree(cacheDir);
+    }
+
+    public BundleArchive[] getArchives()
+        throws Exception
+    {
+        // Get buffer size value.
+        try
+        {
+            String sBufSize = (String) m_configMap.get(CACHE_BUFSIZE_PROP);
+            if (sBufSize != null)
+            {
+                BUFSIZE = Integer.parseInt(sBufSize);
+            }
+        }
+        catch (NumberFormatException ne)
+        {
+            // Use the default value.
+        }
+
+        // Create the existing bundle archives in the directory, if any exist.
+        File cacheDir = determineCacheDir(m_configMap);
+        List archiveList = new ArrayList();
+        File[] children = getSecureAction().listDirectory(cacheDir);
+        for (int i = 0; (children != null) && (i < children.length); i++)
+        {
+            // Ignore directories that aren't bundle directories or
+            // is the system bundle directory.
+            if (children[i].getName().startsWith(BUNDLE_DIR_PREFIX) &&
+                !children[i].getName().equals(BUNDLE_DIR_PREFIX + Long.toString(0)))
+            {
+                // Recreate the bundle archive.
+                try
+                {
+                    archiveList.add(
+                        new BundleArchive(
+                            m_logger, m_configMap, m_zipFactory, children[i]));
+                }
+                catch (Exception ex)
+                {
+                    // Log exception and remove bundle archive directory.
+                    m_logger.log(Logger.LOG_ERROR,
+                        "Error reloading cached bundle, removing it: " + children[i], ex);
+                    deleteDirectoryTree(children[i]);
+                }
+            }
+        }
+
+        return (BundleArchive[])
+            archiveList.toArray(new BundleArchive[archiveList.size()]);
+    }
+
+    public BundleArchive create(long id, int startLevel, String location, InputStream is)
+        throws Exception
+    {
+        File cacheDir = determineCacheDir(m_configMap);
+
+        // Construct archive root directory.
+        File archiveRootDir =
+            new File(cacheDir, BUNDLE_DIR_PREFIX + Long.toString(id));
+
+        try
+        {
+            // Create the archive and add it to the list of archives.
+            BundleArchive ba =
+                new BundleArchive(
+                    m_logger, m_configMap, m_zipFactory, archiveRootDir,
+                    id, startLevel, location, is);
+            return ba;
+        }
+        catch (Exception ex)
+        {
+            if (m_secureAction.fileExists(archiveRootDir))
+            {
+                if (!BundleCache.deleteDirectoryTree(archiveRootDir))
+                {
+                    m_logger.log(
+                        Logger.LOG_ERROR,
+                        "Unable to delete the archive directory: "
+                            + archiveRootDir);
+                }
+            }
+            throw ex;
+        }
+    }
+
+    /**
+     * Provides the system bundle access to its private storage area; this
+     * special case is necessary since the system bundle is not really a
+     * bundle and therefore must be treated in a special way.
+     * @param fileName the name of the file in the system bundle's private area.
+     * @return a <tt>File</tt> object corresponding to the specified file name.
+     * @throws Exception if any error occurs.
+    **/
+    public File getSystemBundleDataFile(String fileName)
+        throws Exception
+    {
+        // Make sure system bundle directory exists.
+        File sbDir = new File(determineCacheDir(m_configMap), BUNDLE_DIR_PREFIX + Long.toString(0));
+
+        // If the system bundle directory exists, then we don't
+        // need to initialize since it has already been done.
+        if (!getSecureAction().fileExists(sbDir))
+        {
+            // Create system bundle directory, if it does not exist.
+            if (!getSecureAction().mkdirs(sbDir))
+            {
+                m_logger.log(
+                    Logger.LOG_ERROR,
+                    "Unable to create system bundle directory.");
+                throw new IOException("Unable to create system bundle directory.");
+            }
+        }
+
+        // Do some sanity checking.
+        if ((fileName.length() > 0) && (fileName.charAt(0) == File.separatorChar))
+            throw new IllegalArgumentException("The data file path must be relative, not absolute.");
+        else if (fileName.indexOf("..") >= 0)
+            throw new IllegalArgumentException("The data file path cannot contain a reference to the \"..\" directory.");
+
+        // Return the data file.
+        return new File(sbDir, fileName);
+    }
+
+    //
+    // Static file-related utility methods.
+    //
+
+    /**
+     * This method copies an input stream to the specified file.
+     * @param is the input stream to copy.
+     * @param outputFile the file to which the input stream should be copied.
+    **/
+    static void copyStreamToFile(InputStream is, File outputFile)
+        throws IOException
+    {
+        OutputStream os = null;
+
+        try
+        {
+            os = getSecureAction().getFileOutputStream(outputFile);
+            os = new BufferedOutputStream(os, BUFSIZE);
+            byte[] b = new byte[BUFSIZE];
+            int len = 0;
+            while ((len = is.read(b)) != -1)
+            {
+                os.write(b, 0, len);
+            }
+        }
+        finally
+        {
+            if (is != null) is.close();
+            if (os != null) os.close();
+        }
+    }
+
+    static boolean deleteDirectoryTree(File target)
+    {
+        if (!deleteDirectoryTreeRecursive(target))
+        {
+            // We might be talking windows and native libs -- hence,
+            // try to trigger a gc and try again. The hope is that
+            // this releases the classloader that loaded the native
+            // lib and allows us to delete it because it then
+            // would not be used anymore.
+            System.gc();
+            System.gc();
+            return deleteDirectoryTreeRecursive(target);
+        }
+        return true;
+    }
+
+    //
+    // Private methods.
+    //
+
+    private static File determineCacheDir(Map configMap)
+    {
+        File cacheDir;
+
+        // Check to see if the cache directory is specified in the storage
+        // configuration property.
+        String cacheDirStr = (String) configMap.get(Constants.FRAMEWORK_STORAGE);
+        // Get the cache root directory for relative paths; the default is ".".
+        String rootDirStr = (String) configMap.get(CACHE_ROOTDIR_PROP);
+        rootDirStr = (rootDirStr == null) ? CACHE_ROOTDIR_DEFAULT : rootDirStr;
+        if (cacheDirStr != null)
+        {
+            // If the specified cache directory is relative, then use the
+            // root directory to calculate the absolute path.
+            cacheDir = new File(cacheDirStr);
+            if (!cacheDir.isAbsolute())
+            {
+                cacheDir = new File(rootDirStr, cacheDirStr);
+            }
+        }
+        else
+        {
+            // If no cache directory was specified, then use the default name
+            // in the root directory.
+            cacheDir = new File(rootDirStr, CACHE_DIR_NAME);
+        }
+
+        return cacheDir;
+    }
+
+    private static boolean deleteDirectoryTreeRecursive(File target)
+    {
+    	if (!getSecureAction().fileExists(target))
+        {
+            return true;
+        }
+
+        if (getSecureAction().isFileDirectory(target))
+        {
+            File[] files = getSecureAction().listDirectory(target);
+            if (files != null)
+            {
+                for (int i = 0; i < files.length; i++)
+                {
+                    deleteDirectoryTreeRecursive(files[i]);
+                }
+            }
+        }
+
+        return getSecureAction().deleteFile(target);
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/felix/framework/cache/Content.java b/src/main/java/org/apache/felix/framework/cache/Content.java
new file mode 100644
index 0000000..09ace44
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/cache/Content.java
@@ -0,0 +1,124 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.cache;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Enumeration;
+
+public interface Content
+{
+    /**
+     * <p>
+     * This method must be called when the content is no longer needed so
+     * that any resourses being used (e.g., open files) can be closed. Once
+     * this method is called, the content is no longer usable. If the content
+     * is already closed, then calls on this method should have no effect.
+     * </p>
+    **/
+    void close();
+
+    /**
+     * <p>
+     * This method determines if the specified named entry is contained in
+     * the associated content. The entry name is a relative path with '/'
+     * separators.
+     * </p>
+     * @param name The name of the entry to find.
+     * @return <tt>true</tt> if a corresponding entry was found, <tt>false</tt>
+     *         otherwise.
+    **/
+    boolean hasEntry(String name);
+
+    /**
+     * <p>
+     * Returns an enumeration of entry names as <tt>String</tt> objects.
+     * An entry name is a path constructed with '/' as path element
+     * separators and is relative to the root of the content. Entry names
+     * for entries that represent directories should end with the '/'
+     * character.
+     * </p>
+     * @returns An enumeration of entry names or <tt>null</tt>.
+    **/
+    Enumeration<String> getEntries();
+
+    /**
+     * <p>
+     * This method returns the named entry as an array of bytes.
+     * </p>
+     * @param name The name of the entry to retrieve as a byte array.
+     * @return An array of bytes if the corresponding entry was found, <tt>null</tt>
+     *         otherwise.
+    **/
+    byte[] getEntryAsBytes(String name);
+
+    /**
+     * <p>
+     * This method returns the named entry as an input stream.
+     * </p>
+     * @param name The name of the entry to retrieve as an input stream.
+     * @return An input stream if the corresponding entry was found, <tt>null</tt>
+     *         otherwise.
+     * @throws <tt>java.io.IOException</tt> if any error occurs.
+    **/
+    InputStream getEntryAsStream(String name) throws IOException;
+
+    /**
+     * <p>
+     * This method returns the named entry as an <tt>IContent</tt> Typically,
+     * this method only makes sense for entries that correspond to some form
+     * of aggregated resource (e.g., an embedded JAR file or directory), but
+     * implementations are free to interpret this however makes sense. This method
+     * should return a new <tt>IContent</tt> instance for every invocation and
+     * the caller is responsible for opening and closing the returned content
+     * object.
+     * </p>
+     * @param name The name of the entry to retrieve as an <tt>IContent</tt>.
+     * @return An <tt>IContent</tt> instance if a corresponding entry was found,
+     *         <tt>null</tt> otherwise.
+    **/
+    Content getEntryAsContent(String name);
+
+    /**
+     * <p>
+     * This method returns the named entry as a file in the file system for
+     * use as a native library. It may not be possible for all content
+     * implementations (e.g., memory only) to implement this method, in which
+     * case it is acceptable to return <tt>null</tt>. Since native libraries
+     * can only be associated with a single class loader, this method should
+     * return a unique file per request.
+     * </p>
+     * @param name The name of the entry to retrieve as a file.
+     * @return A string corresponding to the absolute path of the file if a
+     *         corresponding entry was found, <tt>null</tt> otherwise.
+    **/
+    String getEntryAsNativeLibrary(String name);
+
+    /**
+     * <p>
+     *  This method allows retrieving an entry as a local URL.
+     * </p>
+     *
+     * @param name The name of the entry to retrieve as a URL
+     * @return A URL using a local protocol such as file, jar
+     *           or null if not possible.
+     */
+    URL getEntryAsURL(String name);
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/felix/framework/cache/ContentDirectoryContent.java b/src/main/java/org/apache/felix/framework/cache/ContentDirectoryContent.java
new file mode 100644
index 0000000..892ddf2
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/cache/ContentDirectoryContent.java
@@ -0,0 +1,158 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.cache;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.NoSuchElementException;
+
+public class ContentDirectoryContent implements Content
+{
+    private final Content m_content;
+    private final String m_rootPath;
+
+    public ContentDirectoryContent(Content content, String path)
+    {
+        m_content = content;
+        // Add a '/' to the end if not present.
+        m_rootPath = (path.length() > 0) && (path.charAt(path.length() - 1) != '/')
+            ? path + "/" : path;
+    }
+
+    public void close()
+    {
+        // We do not actually close the associated content
+        // from which we are filtering our directory because
+        // we assume that this will be close manually by
+        // the owner of that content.
+    }
+
+    public boolean hasEntry(String name) throws IllegalStateException
+    {
+        if ((name.length() > 0) && (name.charAt(0) == '/'))
+        {
+            name = name.substring(1);
+        }
+
+        return m_content.hasEntry(m_rootPath + name);
+    }
+
+    public Enumeration<String> getEntries()
+    {
+        return new EntriesEnumeration(m_content.getEntries(), m_rootPath);
+    }
+
+    public byte[] getEntryAsBytes(String name) throws IllegalStateException
+    {
+        if ((name.length() > 0) && (name.charAt(0) == '/'))
+        {
+            name = name.substring(1);
+        }
+
+        return m_content.getEntryAsBytes(m_rootPath + name);
+    }
+
+    public InputStream getEntryAsStream(String name)
+        throws IllegalStateException, IOException
+    {
+        if ((name.length() > 0) && (name.charAt(0) == '/'))
+        {
+            name = name.substring(1);
+        }
+
+        return m_content.getEntryAsStream(m_rootPath + name);
+    }
+
+    public URL getEntryAsURL(String name)
+    {
+        return m_content.getEntryAsURL(m_rootPath + name);
+    }
+
+    public Content getEntryAsContent(String name)
+    {
+        if ((name.length() > 0) && (name.charAt(0) == '/'))
+        {
+            name = name.substring(1);
+        }
+
+        return m_content.getEntryAsContent(m_rootPath + name);
+    }
+
+    public String getEntryAsNativeLibrary(String name)
+    {
+        if ((name.length() > 0) && (name.charAt(0) == '/'))
+        {
+            name = name.substring(1);
+        }
+
+        return m_content.getEntryAsNativeLibrary(m_rootPath + name);
+    }
+
+    public String toString()
+    {
+        return "CONTENT DIR " + m_rootPath + " (" + m_content + ")";
+    }
+
+    private static class EntriesEnumeration implements Enumeration
+    {
+        private final Enumeration m_enumeration;
+        private final String m_rootPath;
+        private String m_nextEntry = null;
+
+        public EntriesEnumeration(Enumeration enumeration, String rootPath)
+        {
+            m_enumeration = enumeration;
+            m_rootPath = rootPath;
+            m_nextEntry = findNextEntry();
+        }
+
+        public synchronized boolean hasMoreElements()
+        {
+            return (m_nextEntry != null);
+        }
+
+        public synchronized Object nextElement()
+        {
+            if (m_nextEntry == null)
+            {
+                throw new NoSuchElementException("No more elements.");
+            }
+            String currentEntry = m_nextEntry;
+            m_nextEntry = findNextEntry();
+            return currentEntry;
+        }
+
+        private String findNextEntry()
+        {
+            // Find next entry that is inside the root directory.
+            while (m_enumeration.hasMoreElements())
+            {
+                String next = (String) m_enumeration.nextElement();
+                if (next.startsWith(m_rootPath) && !next.equals(m_rootPath))
+                {
+                    // Strip off the root directory.
+                    return next.substring(m_rootPath.length());
+                }
+            }
+            return null;
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/felix/framework/cache/DirectoryContent.java b/src/main/java/org/apache/felix/framework/cache/DirectoryContent.java
new file mode 100644
index 0000000..b6b5b54
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/cache/DirectoryContent.java
@@ -0,0 +1,395 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.cache;
+
+import java.io.*;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.*;
+import org.apache.felix.framework.Logger;
+import org.apache.felix.framework.util.FelixConstants;
+import org.apache.felix.framework.util.Util;
+import org.apache.felix.framework.util.WeakZipFileFactory;
+import org.osgi.framework.Constants;
+
+public class DirectoryContent implements Content
+{
+    private static final int BUFSIZE = 4096;
+    private static final transient String EMBEDDED_DIRECTORY = "-embedded";
+    private static final transient String LIBRARY_DIRECTORY = "-lib";
+
+    private final Logger m_logger;
+    private final Map m_configMap;
+    private final WeakZipFileFactory m_zipFactory;
+    private final Object m_revisionLock;
+    private final File m_rootDir;
+    private final File m_dir;
+    private Map m_nativeLibMap;
+
+    public DirectoryContent(Logger logger, Map configMap,
+        WeakZipFileFactory zipFactory, Object revisionLock, File rootDir, File dir)
+    {
+        m_logger = logger;
+        m_configMap = configMap;
+        m_zipFactory = zipFactory;
+        m_revisionLock = revisionLock;
+        m_rootDir = rootDir;
+        m_dir = dir;
+    }
+
+    public void close()
+    {
+        // Nothing to clean up.
+    }
+
+    public boolean hasEntry(String name) throws IllegalStateException
+    {
+        if ((name.length() > 0) && (name.charAt(0) == '/'))
+        {
+            name = name.substring(1);
+        }
+
+        // Return true if the file associated with the entry exists,
+        // unless the entry name ends with "/", in which case only
+        // return true if the file is really a directory.
+        File file = new File(m_dir, name);
+        return BundleCache.getSecureAction().fileExists(file)
+            && (name.endsWith("/")
+                ? BundleCache.getSecureAction().isFileDirectory(file) : true);
+    }
+
+    public Enumeration<String> getEntries()
+    {
+        // Wrap entries enumeration to filter non-matching entries.
+        Enumeration<String> e = new EntriesEnumeration(m_dir);
+
+        // Spec says to return null if there are no entries.
+        return (e.hasMoreElements()) ? e : null;
+    }
+
+    public byte[] getEntryAsBytes(String name) throws IllegalStateException
+    {
+        if ((name.length() > 0) && (name.charAt(0) == '/'))
+        {
+            name = name.substring(1);
+        }
+
+        // Get the embedded resource.
+        InputStream is = null;
+        ByteArrayOutputStream baos = null;
+
+        try
+        {
+            is = new BufferedInputStream(
+                BundleCache.getSecureAction().getFileInputStream(new File(m_dir, name)));
+            baos = new ByteArrayOutputStream(BUFSIZE);
+            byte[] buf = new byte[BUFSIZE];
+            int n = 0;
+            while ((n = is.read(buf, 0, buf.length)) >= 0)
+            {
+                baos.write(buf, 0, n);
+            }
+            return baos.toByteArray();
+
+        }
+        catch (Exception ex)
+        {
+            return null;
+        }
+        finally
+        {
+            try
+            {
+                if (baos != null) baos.close();
+            }
+            catch (Exception ex)
+            {
+            }
+            try
+            {
+                if (is != null) is.close();
+            }
+            catch (Exception ex)
+            {
+            }
+        }
+    }
+
+    public InputStream getEntryAsStream(String name)
+        throws IllegalStateException, IOException
+    {
+        if ((name.length() > 0) && (name.charAt(0) == '/'))
+        {
+            name = name.substring(1);
+        }
+
+        return BundleCache.getSecureAction().getFileInputStream(new File(m_dir, name));
+    }
+
+    public URL getEntryAsURL(String name)
+    {
+        if ((name.length() > 0) && (name.charAt(0) == '/'))
+        {
+            name = name.substring(1);
+        }
+
+        try
+        {
+            return BundleCache.getSecureAction().toURI(new File(m_dir, name)).toURL();
+        }
+        catch (MalformedURLException e)
+        {
+            return null;
+        }
+    }
+
+    public Content getEntryAsContent(String entryName)
+    {
+        // If the entry name refers to the content itself, then
+        // just return it immediately.
+        if (entryName.equals(FelixConstants.CLASS_PATH_DOT))
+        {
+            return new DirectoryContent(
+                m_logger, m_configMap, m_zipFactory, m_revisionLock, m_rootDir, m_dir);
+        }
+
+        // Remove any leading slash, since all bundle class path
+        // entries are relative to the root of the bundle.
+        entryName = (entryName.startsWith("/")) ? entryName.substring(1) : entryName;
+
+        // Any embedded JAR files will be extracted to the embedded directory.
+        File embedDir = new File(m_rootDir, m_dir.getName() + EMBEDDED_DIRECTORY);
+
+        // Determine if the entry is an emdedded JAR file or
+        // directory in the bundle JAR file. Ignore any entries
+        // that do not exist per the spec.
+        File file = new File(m_dir, entryName);
+        if (BundleCache.getSecureAction().isFileDirectory(file))
+        {
+            return new DirectoryContent(
+                m_logger, m_configMap, m_zipFactory, m_revisionLock, m_rootDir, file);
+        }
+        else if (BundleCache.getSecureAction().fileExists(file)
+            && entryName.endsWith(".jar"))
+        {
+            File extractDir = new File(embedDir,
+                (entryName.lastIndexOf('/') >= 0)
+                    ? entryName.substring(0, entryName.lastIndexOf('/'))
+                    : entryName);
+            synchronized (m_revisionLock)
+            {
+                if (!BundleCache.getSecureAction().fileExists(extractDir))
+                {
+                    if (!BundleCache.getSecureAction().mkdirs(extractDir))
+                    {
+                        m_logger.log(
+                            Logger.LOG_ERROR,
+                            "Unable to extract embedded directory.");
+                    }
+                }
+            }
+            return new JarContent(
+                m_logger, m_configMap, m_zipFactory, m_revisionLock,
+                extractDir, file, null);
+        }
+
+        // The entry could not be found, so return null.
+        return null;
+    }
+
+// TODO: SECURITY - This will need to consider security.
+    public String getEntryAsNativeLibrary(String entryName)
+    {
+        // Return result.
+        String result = null;
+
+        // Remove any leading slash, since all bundle class path
+        // entries are relative to the root of the bundle.
+        entryName = (entryName.startsWith("/")) ? entryName.substring(1) : entryName;
+
+        // Any embedded native library files will be extracted to the lib directory.
+        File libDir = new File(m_rootDir, m_dir.getName() + LIBRARY_DIRECTORY);
+
+        // The entry must exist and refer to a file, not a directory,
+        // since we are expecting it to be a native library.
+        File entryFile = new File(m_dir, entryName);
+        if (BundleCache.getSecureAction().fileExists(entryFile)
+            && !BundleCache.getSecureAction().isFileDirectory(entryFile))
+        {
+            // Extracting the embedded native library file impacts all other
+            // existing contents for this revision, so we have to grab the
+            // revision lock first before trying to extract the embedded JAR
+            // file to avoid a race condition.
+            synchronized (m_revisionLock)
+            {
+                // Since native libraries cannot be shared, we must extract a
+                // separate copy per request, so use the request library counter
+                // as part of the extracted path.
+                if (m_nativeLibMap == null)
+                {
+                    m_nativeLibMap = new HashMap();
+                }
+                Integer libCount = (Integer) m_nativeLibMap.get(entryName);
+                // Either set or increment the library count.
+                libCount = (libCount == null) ? new Integer(0) : new Integer(libCount.intValue() + 1);
+                m_nativeLibMap.put(entryName, libCount);
+                File libFile = new File(
+                    libDir, libCount.toString() + File.separatorChar + entryName);
+
+                if (!BundleCache.getSecureAction().fileExists(libFile))
+                {
+                    if (!BundleCache.getSecureAction().fileExists(libFile.getParentFile())
+                        && !BundleCache.getSecureAction().mkdirs(libFile.getParentFile()))
+                    {
+                        m_logger.log(
+                            Logger.LOG_ERROR,
+                            "Unable to create library directory.");
+                    }
+                    else
+                    {
+                        InputStream is = null;
+
+                        try
+                        {
+                            is = new BufferedInputStream(
+                                BundleCache.getSecureAction().getFileInputStream(entryFile),
+                                BundleCache.BUFSIZE);
+                            if (is == null)
+                            {
+                                throw new IOException("No input stream: " + entryName);
+                            }
+
+                            // Create the file.
+                            BundleCache.copyStreamToFile(is, libFile);
+
+                            // Perform exec permission command on extracted library
+                            // if one is configured.
+                            String command = (String) m_configMap.get(
+                                Constants.FRAMEWORK_EXECPERMISSION);
+                            if (command != null)
+                            {
+                                Properties props = new Properties();
+                                props.setProperty("abspath", libFile.toString());
+                                command = Util.substVars(command, "command", null, props);
+                                Process p = BundleCache.getSecureAction().exec(command);
+                                p.waitFor();
+                            }
+
+                            // Return the path to the extracted native library.
+                            result = BundleCache.getSecureAction().getAbsolutePath(libFile);
+                        }
+                        catch (Exception ex)
+                        {
+                            m_logger.log(
+                                Logger.LOG_ERROR,
+                                "Extracting native library.", ex);
+                        }
+                        finally
+                        {
+                            try
+                            {
+                                if (is != null) is.close();
+                            }
+                            catch (IOException ex)
+                            {
+                                // Not much we can do.
+                            }
+                        }
+                    }
+                }
+                else
+                {
+                    // Return the path to the extracted native library.
+                    result = BundleCache.getSecureAction().getAbsolutePath(libFile);
+                }
+            }
+        }
+
+        return result;
+    }
+
+    public String toString()
+    {
+        return "DIRECTORY " + m_dir;
+    }
+
+    private static class EntriesEnumeration implements Enumeration
+    {
+        private final File m_dir;
+        private final File[] m_children;
+        private int m_counter = 0;
+
+        public EntriesEnumeration(File dir)
+        {
+            m_dir = dir;
+            m_children = listFilesRecursive(m_dir);
+        }
+
+        public synchronized boolean hasMoreElements()
+        {
+            return (m_children != null) && (m_counter < m_children.length);
+        }
+
+        public synchronized Object nextElement()
+        {
+            if ((m_children == null) || (m_counter >= m_children.length))
+            {
+                throw new NoSuchElementException("No more entry paths.");
+            }
+
+            // Convert the file separator character to slashes.
+            String abs = BundleCache.getSecureAction()
+                .getAbsolutePath(m_children[m_counter]).replace(File.separatorChar, '/');
+
+            // Remove the leading path of the reference directory, since the
+            // entry paths are supposed to be relative to the root.
+            StringBuffer sb = new StringBuffer(abs);
+            sb.delete(0, BundleCache.getSecureAction().getAbsolutePath(m_dir).length() + 1);
+            // Add a '/' to the end of directory entries.
+            if (BundleCache.getSecureAction().isFileDirectory(m_children[m_counter]))
+            {
+                sb.append('/');
+            }
+            m_counter++;
+            return sb.toString();
+        }
+
+        private File[] listFilesRecursive(File dir)
+        {
+            File[] children = BundleCache.getSecureAction().listDirectory(dir);
+            File[] combined = children;
+            for (int i = 0; i < children.length; i++)
+            {
+                if (BundleCache.getSecureAction().isFileDirectory(children[i]))
+                {
+                    File[] grandchildren = listFilesRecursive(children[i]);
+                    if (grandchildren.length > 0)
+                    {
+                        File[] tmp = new File[combined.length + grandchildren.length];
+                        System.arraycopy(combined, 0, tmp, 0, combined.length);
+                        System.arraycopy(
+                            grandchildren, 0, tmp, combined.length, grandchildren.length);
+                        combined = tmp;
+                    }
+                }
+            }
+            return combined;
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/felix/framework/cache/DirectoryRevision.java b/src/main/java/org/apache/felix/framework/cache/DirectoryRevision.java
new file mode 100644
index 0000000..baebe1d
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/cache/DirectoryRevision.java
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.cache;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Map;
+import java.util.jar.Manifest;
+
+import org.apache.felix.framework.Logger;
+import org.apache.felix.framework.util.StringMap;
+import org.apache.felix.framework.util.WeakZipFileFactory;
+
+/**
+ * <p>
+ * This class implements a bundle archive revision for exploded bundle
+ * JAR files. It uses the specified location directory "in-place" to
+ * execute the bundle and does not copy the bundle content at all.
+ * </p>
+**/
+class DirectoryRevision extends BundleArchiveRevision
+{
+    private final WeakZipFileFactory m_zipFactory;
+    private final File m_refDir;
+
+    public DirectoryRevision(
+        Logger logger, Map configMap, WeakZipFileFactory zipFactory,
+        File revisionRootDir, String location) throws Exception
+    {
+        super(logger, configMap, revisionRootDir, location);
+        m_zipFactory = zipFactory;
+        m_refDir = new File(location.substring(
+            location.indexOf(BundleArchive.FILE_PROTOCOL)
+                + BundleArchive.FILE_PROTOCOL.length()));
+
+        // If the revision directory exists, then we don't
+        // need to initialize since it has already been done.
+        if (BundleCache.getSecureAction().fileExists(getRevisionRootDir()))
+        {
+            return;
+        }
+
+        // Create revision directory, we only need this to store the
+        // revision location, since nothing else needs to be extracted
+        // since we are referencing a read directory already.
+        if (!BundleCache.getSecureAction().mkdir(getRevisionRootDir()))
+        {
+            getLogger().log(
+                Logger.LOG_ERROR,
+                getClass().getName() + ": Unable to create revision directory.");
+            throw new IOException("Unable to create archive directory.");
+        }
+    }
+
+    public synchronized Map getManifestHeader()
+        throws Exception
+    {
+        // Read the header file from the reference directory.
+        InputStream is = null;
+
+        try
+        {
+            // Open manifest file.
+            is = BundleCache.getSecureAction()
+                .getFileInputStream(new File(m_refDir, "META-INF/MANIFEST.MF"));
+            // Error if no jar file.
+            if (is == null)
+            {
+                throw new IOException("No manifest file found.");
+            }
+
+            // Get manifest.
+            Manifest mf = new Manifest(is);
+            // Create a case insensitive map of manifest attributes.
+            return new StringMap(mf.getMainAttributes());
+        }
+        finally
+        {
+            if (is != null) is.close();
+        }
+    }
+
+    public synchronized Content getContent() throws Exception
+    {
+        return new DirectoryContent(getLogger(), getConfig(), m_zipFactory,
+            this, getRevisionRootDir(), m_refDir);
+    }
+
+    protected void close() throws Exception
+    {
+        // Nothing to close since we don't maintain any state outside
+        // of the revision directory, which will be automatically deleted
+        // by the parent bundle archive.
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/felix/framework/cache/JarContent.java b/src/main/java/org/apache/felix/framework/cache/JarContent.java
new file mode 100644
index 0000000..409f12e
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/cache/JarContent.java
@@ -0,0 +1,546 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.cache;
+
+import java.io.BufferedInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+import java.util.zip.ZipEntry;
+import org.apache.felix.framework.Logger;
+import org.apache.felix.framework.util.FelixConstants;
+import org.apache.felix.framework.util.Util;
+import org.apache.felix.framework.util.WeakZipFileFactory;
+import org.apache.felix.framework.util.WeakZipFileFactory.WeakZipFile;
+import org.osgi.framework.Constants;
+
+public class JarContent implements Content
+{
+    private static final int BUFSIZE = 4096;
+    private static final transient String EMBEDDED_DIRECTORY = "-embedded";
+    private static final transient String LIBRARY_DIRECTORY = "-lib";
+
+    private final Logger m_logger;
+    private final Map m_configMap;
+    private final WeakZipFileFactory m_zipFactory;
+    private final Object m_revisionLock;
+    private final File m_rootDir;
+    private final File m_file;
+    private final WeakZipFile m_zipFile;
+    private final boolean m_isZipFileOwner;
+    private Map m_nativeLibMap;
+
+    public JarContent(Logger logger, Map configMap, WeakZipFileFactory zipFactory,
+        Object revisionLock, File rootDir, File file, WeakZipFile zipFile)
+    {
+        m_logger = logger;
+        m_configMap = configMap;
+        m_zipFactory = zipFactory;
+        m_revisionLock = revisionLock;
+        m_rootDir = rootDir;
+        m_file = file;
+        if (zipFile == null)
+        {
+            try
+            {
+                m_zipFile = m_zipFactory.create(m_file);
+            }
+            catch (IOException ex)
+            {
+                throw new RuntimeException(
+                    "Unable to open JAR file, probably deleted: " + ex.getMessage());
+            }
+        }
+        else
+        {
+            m_zipFile = zipFile;
+        }
+        m_isZipFileOwner = (zipFile == null);
+    }
+
+    protected void finalize()
+    {
+        close();
+    }
+
+    public void close()
+    {
+        try
+        {
+            if (m_isZipFileOwner)
+            {
+                m_zipFile.close();
+            }
+        }
+        catch (Exception ex)
+        {
+            m_logger.log(
+                Logger.LOG_ERROR,
+                "JarContent: Unable to close JAR file.", ex);
+        }
+    }
+
+    public boolean hasEntry(String name) throws IllegalStateException
+    {
+        try
+        {
+            ZipEntry ze = m_zipFile.getEntry(name);
+            return ze != null;
+        }
+        catch (Exception ex)
+        {
+            return false;
+        }
+        finally
+        {
+        }
+    }
+
+    public Enumeration<String> getEntries()
+    {
+        // Wrap entries enumeration to filter non-matching entries.
+        Enumeration<String> e = new EntriesEnumeration(m_zipFile.entries());
+
+        // Spec says to return null if there are no entries.
+        return (e.hasMoreElements()) ? e : null;
+    }
+
+    public byte[] getEntryAsBytes(String name) throws IllegalStateException
+    {
+        // Get the embedded resource.
+        InputStream is = null;
+        ByteArrayOutputStream baos = null;
+
+        try
+        {
+            ZipEntry ze = m_zipFile.getEntry(name);
+            if (ze == null)
+            {
+                return null;
+            }
+            is = m_zipFile.getInputStream(ze);
+            if (is == null)
+            {
+                return null;
+            }
+            baos = new ByteArrayOutputStream(BUFSIZE);
+            byte[] buf = new byte[BUFSIZE];
+            int n = 0;
+            while ((n = is.read(buf, 0, buf.length)) >= 0)
+            {
+                baos.write(buf, 0, n);
+            }
+            return baos.toByteArray();
+
+        }
+        catch (Exception ex)
+        {
+            m_logger.log(
+                Logger.LOG_ERROR,
+                "JarContent: Unable to read bytes.", ex);
+            return null;
+        }
+        finally
+        {
+            try
+            {
+                if (baos != null) baos.close();
+            }
+            catch (Exception ex)
+            {
+            }
+            try
+            {
+                if (is != null) is.close();
+            }
+            catch (Exception ex)
+            {
+            }
+        }
+    }
+
+    public InputStream getEntryAsStream(String name)
+        throws IllegalStateException, IOException
+    {
+        // Get the embedded resource.
+        InputStream is = null;
+
+        try
+        {
+            ZipEntry ze = m_zipFile.getEntry(name);
+            if (ze == null)
+            {
+                return null;
+            }
+            is = m_zipFile.getInputStream(ze);
+            if (is == null)
+            {
+                return null;
+            }
+        }
+        catch (Exception ex)
+        {
+            return null;
+        }
+
+        return is;
+    }
+
+    public URL getEntryAsURL(String name)
+    {
+        try
+        {
+            return new URL("jar:" + m_file.toURI().toURL().toExternalForm() + "!/" + name);
+        }
+        catch (MalformedURLException e)
+        {
+            return null;
+        }
+    }
+
+    public Content getEntryAsContent(String entryName)
+    {
+        // If the entry name refers to the content itself, then
+        // just return it immediately.
+        if (entryName.equals(FelixConstants.CLASS_PATH_DOT))
+        {
+            return new JarContent(m_logger, m_configMap, m_zipFactory, m_revisionLock,
+                m_rootDir, m_file, m_zipFile);
+        }
+
+        // Remove any leading slash.
+        entryName = (entryName.startsWith("/")) ? entryName.substring(1) : entryName;
+
+        // Any embedded JAR files will be extracted to the embedded directory.
+        // Since embedded JAR file names may clash when extracting from multiple
+        // embedded JAR files, the embedded directory is per embedded JAR file.
+        File embedDir = new File(m_rootDir, m_file.getName() + EMBEDDED_DIRECTORY);
+
+        // Find the entry in the JAR file and create the
+        // appropriate content type for it.
+
+        // Determine if the entry is an emdedded JAR file or
+        // directory in the bundle JAR file. Ignore any entries
+        // that do not exist per the spec.
+        ZipEntry ze = m_zipFile.getEntry(entryName);
+        if ((ze != null) && ze.isDirectory())
+        {
+            File extractDir = new File(embedDir, entryName);
+
+            // Extracting an embedded directory file impacts all other existing
+            // contents for this revision, so we have to grab the revision
+            // lock first before trying to create a directory for an embedded
+            // directory to avoid a race condition.
+            synchronized (m_revisionLock)
+            {
+                if (!BundleCache.getSecureAction().fileExists(extractDir))
+                {
+                    if (!BundleCache.getSecureAction().mkdirs(extractDir))
+                    {
+                        m_logger.log(
+                            Logger.LOG_ERROR,
+                            "Unable to extract embedded directory.");
+                    }
+                }
+            }
+            return new ContentDirectoryContent(this, entryName);
+        }
+        else if ((ze != null) && ze.getName().endsWith(".jar"))
+        {
+            File extractJar = new File(embedDir, entryName);
+
+            // Extracting the embedded JAR file impacts all other existing
+            // contents for this revision, so we have to grab the revision
+            // lock first before trying to extract the embedded JAR file
+            // to avoid a race condition.
+            synchronized (m_revisionLock)
+            {
+                try
+                {
+                    extractEmbeddedJar(entryName);
+                }
+                catch (Exception ex)
+                {
+                    m_logger.log(
+                        Logger.LOG_ERROR,
+                        "Unable to extract embedded JAR file.", ex);
+                }
+            }
+            return new JarContent(
+                m_logger, m_configMap, m_zipFactory, m_revisionLock,
+                extractJar.getParentFile(), extractJar, null);
+        }
+
+        // The entry could not be found, so return null.
+        return null;
+    }
+
+// TODO: SECURITY - This will need to consider security.
+    public String getEntryAsNativeLibrary(String entryName)
+    {
+        // Return result.
+        String result = null;
+
+        // Remove any leading slash.
+        entryName = (entryName.startsWith("/")) ? entryName.substring(1) : entryName;
+
+        // Any embedded native libraries will be extracted to the lib directory.
+        // Since embedded library file names may clash when extracting from multiple
+        // embedded JAR files, the embedded lib directory is per embedded JAR file.
+        File libDir = new File(m_rootDir, m_file.getName() + LIBRARY_DIRECTORY);
+
+        // The entry name must refer to a file type, since it is
+        // a native library, not a directory.
+        ZipEntry ze = m_zipFile.getEntry(entryName);
+        if ((ze != null) && !ze.isDirectory())
+        {
+            // Extracting the embedded native library file impacts all other
+            // existing contents for this revision, so we have to grab the
+            // revision lock first before trying to extract the embedded JAR
+            // file to avoid a race condition.
+            synchronized (m_revisionLock)
+            {
+                // Since native libraries cannot be shared, we must extract a
+                // separate copy per request, so use the request library counter
+                // as part of the extracted path.
+                if (m_nativeLibMap == null)
+                {
+                    m_nativeLibMap = new HashMap();
+                }
+                Integer libCount = (Integer) m_nativeLibMap.get(entryName);
+                // Either set or increment the library count.
+                libCount = (libCount == null) ? new Integer(0) : new Integer(libCount.intValue() + 1);
+                m_nativeLibMap.put(entryName, libCount);
+                File libFile = new File(
+                    libDir, libCount.toString() + File.separatorChar + entryName);
+
+                if (!BundleCache.getSecureAction().fileExists(libFile))
+                {
+                    if (!BundleCache.getSecureAction().fileExists(libFile.getParentFile())
+                        && !BundleCache.getSecureAction().mkdirs(libFile.getParentFile()))
+                    {
+                        m_logger.log(
+                            Logger.LOG_ERROR,
+                            "Unable to create library directory.");
+                    }
+                    else
+                    {
+                        InputStream is = null;
+
+                        try
+                        {
+                            is = new BufferedInputStream(
+                                m_zipFile.getInputStream(ze),
+                                BundleCache.BUFSIZE);
+                            if (is == null)
+                            {
+                                throw new IOException("No input stream: " + entryName);
+                            }
+
+                            // Create the file.
+                            BundleCache.copyStreamToFile(is, libFile);
+
+                            // Perform exec permission command on extracted library
+                            // if one is configured.
+                            String command = (String) m_configMap.get(
+                                Constants.FRAMEWORK_EXECPERMISSION);
+                            if (command != null)
+                            {
+                                Properties props = new Properties();
+                                props.setProperty("abspath", libFile.toString());
+                                command = Util.substVars(command, "command", null, props);
+                                Process p = BundleCache.getSecureAction().exec(command);
+                                // We have to make sure we read stdout and stderr because
+                                // otherwise we will block on certain unbuffered os's
+                                // (like eg. windows)
+                                Thread stdOut = new Thread(
+                                    new DevNullRunnable(p.getInputStream()));
+                                Thread stdErr = new Thread(
+                                    new DevNullRunnable(p.getErrorStream()));
+                                stdOut.setDaemon(true);
+                                stdErr.setDaemon(true);
+                                stdOut.start();
+                                stdErr.start();
+                                p.waitFor();
+                                stdOut.join();
+                                stdErr.join();
+                            }
+
+                            // Return the path to the extracted native library.
+                            result = BundleCache.getSecureAction().getAbsolutePath(libFile);
+                        }
+                        catch (Exception ex)
+                        {
+                            m_logger.log(
+                                Logger.LOG_ERROR,
+                                "Extracting native library.", ex);
+                        }
+                        finally
+                        {
+                            try
+                            {
+                                if (is != null) is.close();
+                            }
+                            catch (IOException ex)
+                            {
+                                // Not much we can do.
+                            }
+                        }
+                    }
+                }
+                else
+                {
+                    // Return the path to the extracted native library.
+                    result = BundleCache.getSecureAction().getAbsolutePath(libFile);
+                }
+            }
+        }
+
+        return result;
+    }
+
+    public String toString()
+    {
+        return "JAR " + m_file.getPath();
+    }
+
+    public File getFile()
+    {
+        return m_file;
+    }
+
+    /**
+     * This method extracts an embedded JAR file from the bundle's
+     * JAR file.
+     * @param id the identifier of the bundle that owns the embedded JAR file.
+     * @param jarPath the path to the embedded JAR file inside the bundle JAR file.
+    **/
+    private void extractEmbeddedJar(String jarPath)
+        throws Exception
+    {
+        // Remove leading slash if present.
+        jarPath = (jarPath.length() > 0) && (jarPath.charAt(0) == '/')
+            ? jarPath.substring(1) : jarPath;
+
+        // Any embedded JAR files will be extracted to the embedded directory.
+        // Since embedded JAR file names may clash when extracting from multiple
+        // embedded JAR files, the embedded directory is per embedded JAR file.
+        File embedDir = new File(m_rootDir, m_file.getName() + EMBEDDED_DIRECTORY);
+        File jarFile = new File(embedDir, jarPath);
+
+        if (!BundleCache.getSecureAction().fileExists(jarFile))
+        {
+            InputStream is = null;
+            try
+            {
+                // Make sure class path entry is a JAR file.
+                ZipEntry ze = m_zipFile.getEntry(jarPath);
+                if (ze == null)
+                {
+                    return;
+                }
+                // If the zip entry is a directory, then ignore it since
+                // we don't need to extact it; otherwise, it points to an
+                // embedded JAR file, so extract it.
+                else if (!ze.isDirectory())
+                {
+                    // Make sure that the embedded JAR's parent directory exists;
+                    // it may be in a sub-directory.
+                    File jarDir = jarFile.getParentFile();
+                    if (!BundleCache.getSecureAction().fileExists(jarDir))
+                    {
+                        if (!BundleCache.getSecureAction().mkdirs(jarDir))
+                        {
+                            throw new IOException("Unable to create embedded JAR directory.");
+                        }
+                    }
+
+                    // Extract embedded JAR into its directory.
+                    is = new BufferedInputStream(m_zipFile.getInputStream(ze), BundleCache.BUFSIZE);
+                    if (is == null)
+                    {
+                        throw new IOException("No input stream: " + jarPath);
+                    }
+                    // Copy the file.
+                    BundleCache.copyStreamToFile(is, jarFile);
+                }
+            }
+            finally
+            {
+                if (is != null) is.close();
+            }
+        }
+    }
+
+    private static class EntriesEnumeration implements Enumeration<String>
+    {
+        private final Enumeration m_enumeration;
+
+        public EntriesEnumeration(Enumeration enumeration)
+        {
+            m_enumeration = enumeration;
+        }
+
+        public boolean hasMoreElements()
+        {
+            return m_enumeration.hasMoreElements();
+        }
+
+        public String nextElement()
+        {
+            return ((ZipEntry) m_enumeration.nextElement()).getName();
+        }
+    }
+
+    private static class DevNullRunnable implements Runnable
+    {
+        private final InputStream m_in;
+
+        public DevNullRunnable(InputStream in)
+        {
+            m_in = in;
+        }
+
+        public void run()
+        {
+            try
+            {
+                try
+                {
+                    while (m_in.read() != -1){}
+                }
+                finally
+                {
+                    m_in.close();
+                }
+            }
+            catch (Exception ex)
+            {
+                // Not much we can do - maybe we should log it?
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/felix/framework/cache/JarRevision.java b/src/main/java/org/apache/felix/framework/cache/JarRevision.java
new file mode 100644
index 0000000..99d9679
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/cache/JarRevision.java
@@ -0,0 +1,314 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.cache;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.ref.SoftReference;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.Map;
+import java.util.zip.ZipEntry;
+
+import org.apache.felix.framework.Logger;
+import org.apache.felix.framework.util.StringMap;
+import org.apache.felix.framework.util.Util;
+import org.apache.felix.framework.util.WeakZipFileFactory;
+import org.apache.felix.framework.util.WeakZipFileFactory.WeakZipFile;
+
+/**
+ * <p>
+ * This class implements a bundle archive revision for a standard bundle
+ * JAR file. The specified location is the URL of the JAR file. By default,
+ * the associated JAR file is copied into the revision's directory on the
+ * file system, but it is possible to mark the JAR as 'by reference', which
+ * will result in the bundle JAR be used 'in place' and not being copied. In
+ * either case, some of the contents may be extracted into the revision
+ * directory, such as embedded JAR files and native libraries.
+ * </p>
+**/
+class JarRevision extends BundleArchiveRevision
+{
+    private static final transient String BUNDLE_JAR_FILE = "bundle.jar";
+
+    private final WeakZipFileFactory m_zipFactory;
+    private final File m_bundleFile;
+    private final WeakZipFile m_zipFile;
+
+    public JarRevision(
+        Logger logger, Map configMap, WeakZipFileFactory zipFactory,
+        File revisionRootDir, String location, boolean byReference, InputStream is)
+        throws Exception
+    {
+        super(logger, configMap, revisionRootDir, location);
+
+        m_zipFactory = zipFactory;
+
+        if (byReference)
+        {
+            m_bundleFile = new File(location.substring(
+                location.indexOf(BundleArchive.FILE_PROTOCOL)
+                    + BundleArchive.FILE_PROTOCOL.length()));
+        }
+        else
+        {
+            m_bundleFile = new File(getRevisionRootDir(), BUNDLE_JAR_FILE);
+        }
+
+        // Save and process the bundle JAR.
+        initialize(byReference, is);
+
+        // Open shared copy of the JAR file.
+        WeakZipFile zipFile = null;
+        try
+        {
+            // Open bundle JAR file.
+            zipFile = m_zipFactory.create(m_bundleFile);
+            // Error if no jar file.
+            if (zipFile == null)
+            {
+                throw new IOException("No JAR file found.");
+            }
+            m_zipFile = zipFile;
+        }
+        catch (Exception ex)
+        {
+            if (zipFile != null) zipFile.close();
+            throw ex;
+        }
+    }
+
+    public Map getManifestHeader() throws Exception
+    {
+        // Create a case insensitive map of manifest attributes.
+        Map headers = new StringMap();
+        // Read and parse headers.
+        getMainAttributes(headers, m_zipFile);
+        return headers;
+    }
+
+    public synchronized Content getContent() throws Exception
+    {
+        return new JarContent(getLogger(), getConfig(), m_zipFactory,
+            this, getRevisionRootDir(), m_bundleFile, m_zipFile);
+    }
+
+    protected void close() throws Exception
+    {
+        m_zipFile.close();
+    }
+
+    //
+    // Private methods.
+    //
+
+    private void initialize(boolean byReference, InputStream is)
+        throws Exception
+    {
+        try
+        {
+            // If the revision directory does not exist, then create it.
+            if (!BundleCache.getSecureAction().fileExists(getRevisionRootDir()))
+            {
+                if (!BundleCache.getSecureAction().mkdir(getRevisionRootDir()))
+                {
+                    getLogger().log(
+                        Logger.LOG_ERROR,
+                        getClass().getName() + ": Unable to create revision directory.");
+                    throw new IOException("Unable to create archive directory.");
+                }
+
+                if (!byReference)
+                {
+                    URLConnection conn = null;
+                    try
+                    {
+                        if (is == null)
+                        {
+                            // Do it the manual way to have a chance to
+                            // set request properties such as proxy auth.
+                            URL url = BundleCache.getSecureAction().createURL(
+                                null, getLocation(), null);
+                            conn = url.openConnection();
+
+                            // Support for http proxy authentication.
+                            String auth = BundleCache.getSecureAction()
+                                .getSystemProperty("http.proxyAuth", null);
+                            if ((auth != null) && (auth.length() > 0))
+                            {
+                                if ("http".equals(url.getProtocol()) ||
+                                    "https".equals(url.getProtocol()))
+                                {
+                                    String base64 = Util.base64Encode(auth);
+                                    conn.setRequestProperty(
+                                        "Proxy-Authorization", "Basic " + base64);
+                                }
+                            }
+                            is = BundleCache.getSecureAction()
+                                .getURLConnectionInputStream(conn);
+                        }
+
+                        // Save the bundle jar file.
+                        BundleCache.copyStreamToFile(is, m_bundleFile);
+                    }
+                    finally
+                    {
+                        // This is a hack to fix an issue on Android, where
+                        // HttpURLConnections are not properly closed. (FELIX-2728)
+                        if ((conn != null) && (conn instanceof HttpURLConnection))
+                        {
+                            ((HttpURLConnection) conn).disconnect();
+                        }
+                    }
+                }
+            }
+        }
+        finally
+        {
+            if (is != null) is.close();
+        }
+    }
+
+    private static final ThreadLocal m_defaultBuffer = new ThreadLocal();
+    private static final int DEFAULT_BUFFER = 1024 * 64;
+
+    // Parse the main attributes of the manifest of the given jarfile.
+    // The idea is to not open the jar file as a java.util.jarfile but
+    // read the mainfest from the zipfile directly and parse it manually
+    // to use less memory and be faster.
+    private static void getMainAttributes(Map result, WeakZipFile zipFile) throws Exception
+    {
+        ZipEntry entry = zipFile.getEntry("META-INF/MANIFEST.MF");
+
+        // Get a buffer for this thread if there is one already otherwise,
+        // create one of size DEFAULT_BUFFER (64K) if the manifest is less
+        // than 64k or of the size of the manifest.
+        SoftReference ref = (SoftReference) m_defaultBuffer.get();
+        byte[] bytes = null;
+        if (ref != null)
+        {
+            bytes = (byte[]) ref.get();
+        }
+        int size = (int) entry.getSize();
+        if (bytes == null)
+        {
+            bytes = new byte[size > DEFAULT_BUFFER ? size : DEFAULT_BUFFER];
+            m_defaultBuffer.set(new SoftReference(bytes));
+        }
+        else if (size > bytes.length)
+        {
+            bytes = new byte[size];
+            m_defaultBuffer.set(new SoftReference(bytes));
+        }
+
+        // Now read in the manifest in one go into the bytes array.
+        // The InputStream is already
+        // buffered and can handle up to 64K buffers in one go.
+        InputStream is = null;
+        try
+        {
+            is = zipFile.getInputStream(entry);
+            int i = is.read(bytes);
+            while (i < size)
+            {
+                i += is.read(bytes, i, bytes.length - i);
+            }
+        }
+        finally
+        {
+            is.close();
+        }
+
+        // Now parse the main attributes. The idea is to do that
+        // without creating new byte arrays. Therefore, we read through
+        // the manifest bytes inside the bytes array and write them back into
+        // the same array unless we don't need them (e.g., \r\n and \n are skipped).
+        // That allows us to create the strings from the bytes array without the skipped
+        // chars. We stopp as soon as we see a blankline as that denotes that the main
+        //attributes part is finished.
+        String key = null;
+        int last = 0;
+        int current = 0;
+        for (int i = 0; i < size; i++)
+        {
+            // skip \r and \n if it is follows by another \n
+            // (we catch the blank line case in the next iteration)
+            if (bytes[i] == '\r')
+            {
+                if ((i + 1 < size) && (bytes[i + 1] == '\n'))
+                {
+                    continue;
+                }
+            }
+            if (bytes[i] == '\n')
+            {
+                if ((i + 1 < size) && (bytes[i + 1] == ' '))
+                {
+                    i++;
+                    continue;
+                }
+            }
+            // If we don't have a key yet and see the first : we parse it as the key
+            // and skip the :<blank> that follows it.
+            if ((key == null) && (bytes[i] == ':'))
+            {
+                key = new String(bytes, last, (current - last), "UTF-8");
+                if ((i + 1 < size) && (bytes[i + 1] == ' '))
+                {
+                    last = current + 1;
+                    continue;
+                }
+                else
+                {
+                    throw new Exception(
+                        "Manifest error: Missing space separator - " + key);
+                }
+            }
+            // if we are at the end of a line
+            if (bytes[i] == '\n')
+            {
+                // and it is a blank line stop parsing (main attributes are done)
+                if ((last == current) && (key == null))
+                {
+                    break;
+                }
+                // Otherwise, parse the value and add it to the map (we throw an
+                // exception if we don't have a key or the key already exist.
+                String value = new String(bytes, last, (current - last), "UTF-8");
+                if (key == null)
+                {
+                    throw new Exception("Manifst error: Missing attribute name - " + value);
+                }
+                else if (result.put(key, value) != null)
+                {
+                    throw new Exception("Manifst error: Duplicate attribute name - " + key);
+                }
+                last = current;
+                key = null;
+            }
+            else
+            {
+                // write back the byte if it needs to be included in the key or the value.
+                bytes[current++] = bytes[i];
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/felix/framework/capabilityset/CapabilitySet.java b/src/main/java/org/apache/felix/framework/capabilityset/CapabilitySet.java
new file mode 100644
index 0000000..738652e
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/capabilityset/CapabilitySet.java
@@ -0,0 +1,631 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.capabilityset;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.TreeMap;
+import org.apache.felix.framework.util.SecureAction;
+import org.apache.felix.framework.util.StringComparator;
+import org.apache.felix.framework.wiring.BundleCapabilityImpl;
+import org.osgi.framework.wiring.BundleCapability;
+
+public class CapabilitySet
+{
+    private final Map<String, Map<Object, Set<BundleCapability>>> m_indices;
+    private final Set<BundleCapability> m_capSet = new HashSet<BundleCapability>();
+    private final static SecureAction m_secureAction = new SecureAction();
+
+    public void dump()
+    {
+        for (Entry<String, Map<Object, Set<BundleCapability>>> entry : m_indices.entrySet())
+        {
+            boolean header1 = false;
+            for (Entry<Object, Set<BundleCapability>> entry2 : entry.getValue().entrySet())
+            {
+                boolean header2 = false;
+                for (BundleCapability cap : entry2.getValue())
+                {
+                    if (cap.getRevision().getBundle().getBundleId() != 0)
+                    {
+                        if (!header1)
+                        {
+                            System.out.println(entry.getKey() + ":");
+                            header1 = true;
+                        }
+                        if (!header2)
+                        {
+                            System.out.println("   " + entry2.getKey());
+                            header2 = true;
+                        }
+                        System.out.println("      " + cap);
+                    }
+                }
+            }
+        }
+    }
+
+    public CapabilitySet(List<String> indexProps, boolean caseSensitive)
+    {
+        m_indices = (caseSensitive)
+            ? new TreeMap<String, Map<Object, Set<BundleCapability>>>()
+            : new TreeMap<String, Map<Object, Set<BundleCapability>>>(
+                new StringComparator(false));
+        for (int i = 0; (indexProps != null) && (i < indexProps.size()); i++)
+        {
+            m_indices.put(
+                indexProps.get(i), new HashMap<Object, Set<BundleCapability>>());
+        }
+    }
+
+    public void addCapability(BundleCapability cap)
+    {
+        m_capSet.add(cap);
+
+        // Index capability.
+        for (Entry<String, Map<Object, Set<BundleCapability>>> entry : m_indices.entrySet())
+        {
+            Object value = cap.getAttributes().get(entry.getKey());
+            if (value != null)
+            {
+                if (value.getClass().isArray())
+                {
+                    value = convertArrayToList(value);
+                }
+
+                Map<Object, Set<BundleCapability>> index = entry.getValue();
+
+                if (value instanceof Collection)
+                {
+                    Collection c = (Collection) value;
+                    for (Object o : c)
+                    {
+                        indexCapability(index, cap, o);
+                    }
+                }
+                else
+                {
+                    indexCapability(index, cap, value);
+                }
+            }
+        }
+    }
+
+    private void indexCapability(
+        Map<Object, Set<BundleCapability>> index, BundleCapability cap, Object capValue)
+    {
+        Set<BundleCapability> caps = index.get(capValue);
+        if (caps == null)
+        {
+            caps = new HashSet<BundleCapability>();
+            index.put(capValue, caps);
+        }
+        caps.add(cap);
+    }
+
+    public void removeCapability(BundleCapability cap)
+    {
+        if (m_capSet.remove(cap))
+        {
+            for (Entry<String, Map<Object, Set<BundleCapability>>> entry : m_indices.entrySet())
+            {
+                Object value = cap.getAttributes().get(entry.getKey());
+                if (value != null)
+                {
+                    if (value.getClass().isArray())
+                    {
+                        value = convertArrayToList(value);
+                    }
+
+                    Map<Object, Set<BundleCapability>> index = entry.getValue();
+
+                    if (value instanceof Collection)
+                    {
+                        Collection c = (Collection) value;
+                        for (Object o : c)
+                        {
+                            deindexCapability(index, cap, o);
+                        }
+                    }
+                    else
+                    {
+                        deindexCapability(index, cap, value);
+                    }
+                }
+            }
+        }
+    }
+
+    private void deindexCapability(
+        Map<Object, Set<BundleCapability>> index, BundleCapability cap, Object value)
+    {
+        Set<BundleCapability> caps = index.get(value);
+        if (caps != null)
+        {
+            caps.remove(cap);
+            if (caps.isEmpty())
+            {
+                index.remove(value);
+            }
+        }
+    }
+
+    public Set<BundleCapability> match(SimpleFilter sf, boolean obeyMandatory)
+    {
+        Set<BundleCapability> matches = match(m_capSet, sf);
+        return (obeyMandatory)
+            ? matchMandatory(matches, sf)
+            : matches;
+    }
+
+    private Set<BundleCapability> match(Set<BundleCapability> caps, SimpleFilter sf)
+    {
+        Set<BundleCapability> matches = new HashSet<BundleCapability>();
+
+        if (sf.getOperation() == SimpleFilter.MATCH_ALL)
+        {
+            matches.addAll(caps);
+        }
+        else if (sf.getOperation() == SimpleFilter.AND)
+        {
+            // Evaluate each subfilter against the remaining capabilities.
+            // For AND we calculate the intersection of each subfilter.
+            // We can short-circuit the AND operation if there are no
+            // remaining capabilities.
+            List<SimpleFilter> sfs = (List<SimpleFilter>) sf.getValue();
+            for (int i = 0; (caps.size() > 0) && (i < sfs.size()); i++)
+            {
+                matches = match(caps, sfs.get(i));
+                caps = matches;
+            }
+        }
+        else if (sf.getOperation() == SimpleFilter.OR)
+        {
+            // Evaluate each subfilter against the remaining capabilities.
+            // For OR we calculate the union of each subfilter.
+            List<SimpleFilter> sfs = (List<SimpleFilter>) sf.getValue();
+            for (int i = 0; i < sfs.size(); i++)
+            {
+                matches.addAll(match(caps, sfs.get(i)));
+            }
+        }
+        else if (sf.getOperation() == SimpleFilter.NOT)
+        {
+            // Evaluate each subfilter against the remaining capabilities.
+            // For OR we calculate the union of each subfilter.
+            matches.addAll(caps);
+            List<SimpleFilter> sfs = (List<SimpleFilter>) sf.getValue();
+            for (int i = 0; i < sfs.size(); i++)
+            {
+                matches.removeAll(match(caps, sfs.get(i)));
+            }
+        }
+        else
+        {
+            Map<Object, Set<BundleCapability>> index = m_indices.get(sf.getName());
+            if ((sf.getOperation() == SimpleFilter.EQ) && (index != null))
+            {
+                Set<BundleCapability> existingCaps = index.get(sf.getValue());
+                if (existingCaps != null)
+                {
+                    matches.addAll(existingCaps);
+                    matches.retainAll(caps);
+                }
+            }
+            else
+            {
+                for (Iterator<BundleCapability> it = caps.iterator(); it.hasNext(); )
+                {
+                    BundleCapability cap = it.next();
+                    Object lhs = cap.getAttributes().get(sf.getName());
+                    if (lhs != null)
+                    {
+                        if (compare(lhs, sf.getValue(), sf.getOperation()))
+                        {
+                            matches.add(cap);
+                        }
+                    }
+                }
+            }
+        }
+
+        return matches;
+    }
+
+    public static boolean matches(BundleCapability cap, SimpleFilter sf)
+    {
+        return matchesInternal(cap, sf) && matchMandatory(cap, sf);
+    }
+
+    private static boolean matchesInternal(BundleCapability cap, SimpleFilter sf)
+    {
+        boolean matched = true;
+
+        if (sf.getOperation() == SimpleFilter.MATCH_ALL)
+        {
+            matched = true;
+        }
+        else if (sf.getOperation() == SimpleFilter.AND)
+        {
+            // Evaluate each subfilter against the remaining capabilities.
+            // For AND we calculate the intersection of each subfilter.
+            // We can short-circuit the AND operation if there are no
+            // remaining capabilities.
+            List<SimpleFilter> sfs = (List<SimpleFilter>) sf.getValue();
+            for (int i = 0; matched && (i < sfs.size()); i++)
+            {
+                matched = matchesInternal(cap, sfs.get(i));
+            }
+        }
+        else if (sf.getOperation() == SimpleFilter.OR)
+        {
+            // Evaluate each subfilter against the remaining capabilities.
+            // For OR we calculate the union of each subfilter.
+            matched = false;
+            List<SimpleFilter> sfs = (List<SimpleFilter>) sf.getValue();
+            for (int i = 0; !matched && (i < sfs.size()); i++)
+            {
+                matched = matchesInternal(cap, sfs.get(i));
+            }
+        }
+        else if (sf.getOperation() == SimpleFilter.NOT)
+        {
+            // Evaluate each subfilter against the remaining capabilities.
+            // For OR we calculate the union of each subfilter.
+            List<SimpleFilter> sfs = (List<SimpleFilter>) sf.getValue();
+            for (int i = 0; i < sfs.size(); i++)
+            {
+                matched = !(matchesInternal(cap, sfs.get(i)));
+            }
+        }
+        else
+        {
+            matched = false;
+            Object lhs = cap.getAttributes().get(sf.getName());
+            if (lhs != null)
+            {
+                matched = compare(lhs, sf.getValue(), sf.getOperation());
+            }
+        }
+
+        return matched;
+    }
+
+    private static Set<BundleCapability> matchMandatory(
+        Set<BundleCapability> caps, SimpleFilter sf)
+    {
+        for (Iterator<BundleCapability> it = caps.iterator(); it.hasNext(); )
+        {
+            BundleCapability cap = it.next();
+            if (!matchMandatory(cap, sf))
+            {
+                it.remove();
+            }
+        }
+        return caps;
+    }
+
+    private static boolean matchMandatory(BundleCapability cap, SimpleFilter sf)
+    {
+        Map<String, Object> attrs = cap.getAttributes();
+        for (Entry<String, Object> entry : attrs.entrySet())
+        {
+            if (((BundleCapabilityImpl) cap).isAttributeMandatory(entry.getKey())
+                && !matchMandatoryAttrbute(entry.getKey(), sf))
+            {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private static boolean matchMandatoryAttrbute(String attrName, SimpleFilter sf)
+    {
+        if ((sf.getName() != null) && sf.getName().equals(attrName))
+        {
+            return true;
+        }
+        else if (sf.getOperation() == SimpleFilter.AND)
+        {
+            List list = (List) sf.getValue();
+            for (int i = 0; i < list.size(); i++)
+            {
+                SimpleFilter sf2 = (SimpleFilter) list.get(i);
+                if ((sf2.getName() != null)
+                    && sf2.getName().equals(attrName))
+                {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private static final Class<?>[] STRING_CLASS = new Class[] { String.class };
+    private static final String VALUE_OF_METHOD_NAME = "valueOf";
+
+    private static boolean compare(Object lhs, Object rhsUnknown, int op)
+    {
+        if (lhs == null)
+        {
+            return false;
+        }
+
+        // If this is a PRESENT operation, then just return true immediately
+        // since we wouldn't be here if the attribute wasn't present.
+        if (op == SimpleFilter.PRESENT)
+        {
+            return true;
+        }
+
+        // If the type is comparable, then we can just return the
+        // result immediately.
+        if (lhs instanceof Comparable)
+        {
+            // Spec says SUBSTRING is false for all types other than string.
+            if ((op == SimpleFilter.SUBSTRING) && !(lhs instanceof String))
+            {
+                return false;
+            }
+
+            Object rhs;
+            if (op == SimpleFilter.SUBSTRING)
+            {
+                rhs = rhsUnknown;
+            }
+            else
+            {
+                try
+                {
+                    rhs = coerceType(lhs, (String) rhsUnknown);
+                }
+                catch (Exception ex)
+                {
+                    return false;
+                }
+            }
+
+            switch (op)
+            {
+                case SimpleFilter.EQ :
+                    try
+                    {
+                        return (((Comparable) lhs).compareTo(rhs) == 0);
+                    }
+                    catch (Exception ex)
+                    {
+                        return false;
+                    }
+                case SimpleFilter.GTE :
+                    try
+                    {
+                        return (((Comparable) lhs).compareTo(rhs) >= 0);
+                    }
+                    catch (Exception ex)
+                    {
+                        return false;
+                    }
+                case SimpleFilter.LTE :
+                    try
+                    {
+                        return (((Comparable) lhs).compareTo(rhs) <= 0);
+                    }
+                    catch (Exception ex)
+                    {
+                        return false;
+                    }
+                case SimpleFilter.APPROX :
+                    return compareApproximate(((Comparable) lhs), rhs);
+                case SimpleFilter.SUBSTRING :
+                    return SimpleFilter.compareSubstring((List<String>) rhs, (String) lhs);
+                default:
+                    throw new RuntimeException(
+                        "Unknown comparison operator: " + op);
+            }
+        }
+        // Booleans do not implement comparable, so special case them.
+        else if (lhs instanceof Boolean)
+        {
+            Object rhs;
+            try
+            {
+                rhs = coerceType(lhs, (String) rhsUnknown);
+            }
+            catch (Exception ex)
+            {
+                return false;
+            }
+
+            switch (op)
+            {
+                case SimpleFilter.EQ :
+                case SimpleFilter.GTE :
+                case SimpleFilter.LTE :
+                case SimpleFilter.APPROX :
+                    return (lhs.equals(rhs));
+                default:
+                    throw new RuntimeException(
+                        "Unknown comparison operator: " + op);
+            }
+        }
+
+        // If the LHS is not a comparable or boolean, check if it is an
+        // array. If so, convert it to a list so we can treat it as a
+        // collection.
+        if (lhs.getClass().isArray())
+        {
+            lhs = convertArrayToList(lhs);
+        }
+
+        // If LHS is a collection, then call compare() on each element
+        // of the collection until a match is found.
+        if (lhs instanceof Collection)
+        {
+            for (Iterator iter = ((Collection) lhs).iterator(); iter.hasNext(); )
+            {
+                if (compare(iter.next(), rhsUnknown, op))
+                {
+                    return true;
+                }
+            }
+
+            return false;
+        }
+
+        // Spec says SUBSTRING is false for all types other than string.
+        if ((op == SimpleFilter.SUBSTRING) && !(lhs instanceof String))
+        {
+            return false;
+        }
+
+        // Since we cannot identify the LHS type, then we can only perform
+        // equality comparison.
+        try
+        {
+            return lhs.equals(coerceType(lhs, (String) rhsUnknown));
+        }
+        catch (Exception ex)
+        {
+            return false;
+        }
+    }
+
+    private static boolean compareApproximate(Object lhs, Object rhs)
+    {
+        if (rhs instanceof String)
+        {
+            return removeWhitespace((String) lhs)
+                .equalsIgnoreCase(removeWhitespace((String) rhs));
+        }
+        else if (rhs instanceof Character)
+        {
+            return Character.toLowerCase(((Character) lhs))
+                == Character.toLowerCase(((Character) rhs));
+        }
+        return lhs.equals(rhs);
+    }
+
+    private static String removeWhitespace(String s)
+    {
+        StringBuffer sb = new StringBuffer(s.length());
+        for (int i = 0; i < s.length(); i++)
+        {
+            if (!Character.isWhitespace(s.charAt(i)))
+            {
+                sb.append(s.charAt(i));
+            }
+        }
+        return sb.toString();
+    }
+
+    private static Object coerceType(Object lhs, String rhsString) throws Exception
+    {
+        // If the LHS expects a string, then we can just return
+        // the RHS since it is a string.
+        if (lhs.getClass() == rhsString.getClass())
+        {
+            return rhsString;
+        }
+
+        // Try to convert the RHS type to the LHS type by using
+        // the string constructor of the LHS class, if it has one.
+        Object rhs = null;
+        try
+        {
+            // The Character class is a special case, since its constructor
+            // does not take a string, so handle it separately.
+            if (lhs instanceof Character)
+            {
+                rhs = new Character(rhsString.charAt(0));
+            }
+            else
+            {
+                // Spec says we should trim number types.
+                if ((lhs instanceof Number) || (lhs instanceof Boolean))
+                {
+                    rhsString = rhsString.trim();
+                }
+
+                try
+                {
+                    // Try to find a suitable static valueOf method
+                    Method valueOfMethod = m_secureAction.getDeclaredMethod(
+                        lhs.getClass(), VALUE_OF_METHOD_NAME, STRING_CLASS);
+                    if (valueOfMethod.getReturnType().isAssignableFrom(lhs.getClass())
+                        && ((valueOfMethod.getModifiers() & Modifier.STATIC) > 0))
+                    {
+                        m_secureAction.setAccesssible(valueOfMethod);
+                        rhs = valueOfMethod.invoke(null, new Object[] { rhsString });
+                    }
+                }
+                catch (Exception ex)
+                {
+                    // Static valueOf fails, try the next conversion mechanism
+                }
+
+                if (rhs == null)
+                {
+                    Constructor ctor = m_secureAction.getConstructor(lhs.getClass(), STRING_CLASS);
+                    m_secureAction.setAccesssible(ctor);
+                    rhs = ctor.newInstance(new Object[] { rhsString });
+                }
+            }
+        }
+        catch (Exception ex)
+        {
+            throw new Exception(
+                "Could not instantiate class "
+                    + lhs.getClass().getName()
+                    + " from string constructor with argument '"
+                    + rhsString + "' because " + ex);
+        }
+
+        return rhs;
+    }
+
+    /**
+     * This is an ugly utility method to convert an array of primitives
+     * to an array of primitive wrapper objects. This method simplifies
+     * processing LDAP filters since the special case of primitive arrays
+     * can be ignored.
+     * @param array An array of primitive types.
+     * @return An corresponding array using pritive wrapper objects.
+    **/
+    private static List convertArrayToList(Object array)
+    {
+        int len = Array.getLength(array);
+        List list = new ArrayList(len);
+        for (int i = 0; i < len; i++)
+        {
+            list.add(Array.get(array, i));
+        }
+        return list;
+    }
+}
diff --git a/src/main/java/org/apache/felix/framework/capabilityset/SimpleFilter.java b/src/main/java/org/apache/felix/framework/capabilityset/SimpleFilter.java
new file mode 100644
index 0000000..1bd517b
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/capabilityset/SimpleFilter.java
@@ -0,0 +1,650 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.capabilityset;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import org.apache.felix.framework.util.VersionRange;
+
+public class SimpleFilter
+{
+    public static final int MATCH_ALL = 0;
+    public static final int AND = 1;
+    public static final int OR = 2;
+    public static final int NOT = 3;
+    public static final int EQ = 4;
+    public static final int LTE = 5;
+    public static final int GTE = 6;
+    public static final int SUBSTRING = 7;
+    public static final int PRESENT = 8;
+    public static final int APPROX = 9;
+
+    private final String m_name;
+    private final Object m_value;
+    private final int m_op;
+
+    public SimpleFilter(String attr, Object value, int op)
+    {
+        m_name = attr;
+        m_value = value;
+        m_op = op;
+    }
+
+    public String getName()
+    {
+        return m_name;
+    }
+
+    public Object getValue()
+    {
+        return m_value;
+    }
+
+    public int getOperation()
+    {
+        return m_op;
+    }
+
+    public String toString()
+    {
+        String s = null;
+        switch (m_op)
+        {
+            case AND:
+                s = "(&" + toString((List) m_value) + ")";
+                break;
+            case OR:
+                s = "(|" + toString((List) m_value) + ")";
+                break;
+            case NOT:
+                s = "(!" + toString((List) m_value) + ")";
+                break;
+            case EQ:
+                s = "(" + m_name + "=" + toEncodedString(m_value) + ")";
+                break;
+            case LTE:
+                s = "(" + m_name + "<=" + toEncodedString(m_value) + ")";
+                break;
+            case GTE:
+                s = "(" + m_name + ">=" + toEncodedString(m_value) + ")";
+                break;
+            case SUBSTRING:
+                s = "(" + m_name + "=" + unparseSubstring((List<String>) m_value) + ")";
+                break;
+            case PRESENT:
+                s = "(" + m_name + "=*)";
+                break;
+            case APPROX:
+                s = "(" + m_name + "~=" + toEncodedString(m_value) + ")";
+                break;
+            case MATCH_ALL:
+                s = "(*)";
+                break;
+        }
+        return s;
+    }
+
+    private static String toString(List list)
+    {
+        StringBuffer sb = new StringBuffer();
+        for (int i = 0; i < list.size(); i++)
+        {
+            sb.append(list.get(i).toString());
+        }
+        return sb.toString();
+    }
+
+    private static String toDecodedString(String s, int startIdx, int endIdx)
+    {
+        StringBuffer sb = new StringBuffer(endIdx - startIdx);
+        boolean escaped = false;
+        for (int i = 0; i < (endIdx - startIdx); i++)
+        {
+            char c = s.charAt(startIdx + i);
+            if (!escaped && (c == '\\'))
+            {
+                escaped = true;
+            }
+            else
+            {
+                escaped = false;
+                sb.append(c);
+            }
+        }
+
+        return sb.toString();
+    }
+
+    private static String toEncodedString(Object o)
+    {
+        if (o instanceof String)
+        {
+            String s = (String) o;
+            StringBuffer sb = new StringBuffer();
+            for (int i = 0; i < s.length(); i++)
+            {
+                char c = s.charAt(i);
+                if ((c == '\\') || (c == '(') || (c == ')') || (c == '*'))
+                {
+                    sb.append('\\');
+                }
+                sb.append(c);
+            }
+
+            o = sb.toString();
+        }
+
+        return o.toString();
+    }
+
+    public static SimpleFilter parse(String filter)
+    {
+        int idx = skipWhitespace(filter, 0);
+
+        if ((filter == null) || (filter.length() == 0) || (idx >= filter.length()))
+        {
+            throw new IllegalArgumentException("Null or empty filter.");
+        }
+        else if (filter.charAt(idx) != '(')
+        {
+            throw new IllegalArgumentException("Missing opening parenthesis: " + filter);
+        }
+
+        SimpleFilter sf = null;
+        List stack = new ArrayList();
+        boolean isEscaped = false;
+        while (idx < filter.length())
+        {
+            if (sf != null)
+            {
+                throw new IllegalArgumentException(
+                    "Only one top-level operation allowed: " + filter);
+            }
+
+            if (!isEscaped && (filter.charAt(idx) == '('))
+            {
+                // Skip paren and following whitespace.
+                idx = skipWhitespace(filter, idx + 1);
+
+                if (filter.charAt(idx) == '&')
+                {
+                    int peek = skipWhitespace(filter, idx + 1);
+                    if (filter.charAt(peek) == '(')
+                    {
+                        idx = peek - 1;
+                        stack.add(0, new SimpleFilter(null, new ArrayList(), SimpleFilter.AND));
+                    }
+                    else
+                    {
+                        stack.add(0, new Integer(idx));
+                    }
+                }
+                else if (filter.charAt(idx) == '|')
+                {
+                    int peek = skipWhitespace(filter, idx + 1);
+                    if (filter.charAt(peek) == '(')
+                    {
+                        idx = peek - 1;
+                        stack.add(0, new SimpleFilter(null, new ArrayList(), SimpleFilter.OR));
+                    }
+                    else
+                    {
+                        stack.add(0, new Integer(idx));
+                    }
+                }
+                else if (filter.charAt(idx) == '!')
+                {
+                    int peek = skipWhitespace(filter, idx + 1);
+                    if (filter.charAt(peek) == '(')
+                    {
+                        idx = peek - 1;
+                        stack.add(0, new SimpleFilter(null, new ArrayList(), SimpleFilter.NOT));
+                    }
+                    else
+                    {
+                        stack.add(0, new Integer(idx));
+                    }
+                }
+                else
+                {
+                    stack.add(0, new Integer(idx));
+                }
+            }
+            else if (!isEscaped && (filter.charAt(idx) == ')'))
+            {
+                Object top = stack.remove(0);
+                if (top instanceof SimpleFilter)
+                {
+                    if (!stack.isEmpty() && (stack.get(0) instanceof SimpleFilter))
+                    {
+                        ((List) ((SimpleFilter) stack.get(0)).m_value).add(top);
+                    }
+                    else
+                    {
+                        sf = (SimpleFilter) top;
+                    }
+                }
+                else if (!stack.isEmpty() && (stack.get(0) instanceof SimpleFilter))
+                {
+                    ((List) ((SimpleFilter) stack.get(0)).m_value).add(
+                        SimpleFilter.subfilter(filter, ((Integer) top).intValue(), idx));
+                }
+                else
+                {
+                    sf = SimpleFilter.subfilter(filter, ((Integer) top).intValue(), idx);
+                }
+            }
+            else if (!isEscaped && (filter.charAt(idx) == '\\'))
+            {
+                isEscaped = true;
+            }
+            else
+            {
+                isEscaped = false;
+            }
+
+            idx = skipWhitespace(filter, idx + 1);
+        }
+
+        if (sf == null)
+        {
+            throw new IllegalArgumentException("Missing closing parenthesis: " + filter);
+        }
+
+        return sf;
+    }
+
+    private static SimpleFilter subfilter(String filter, int startIdx, int endIdx)
+    {
+        final String opChars = "=<>~";
+
+        // Determine the ending index of the attribute name.
+        int attrEndIdx = startIdx;
+        for (int i = 0; i < (endIdx - startIdx); i++)
+        {
+            char c = filter.charAt(startIdx + i);
+            if (opChars.indexOf(c) >= 0)
+            {
+                break;
+            }
+            else if (!Character.isWhitespace(c))
+            {
+                attrEndIdx = startIdx + i + 1;
+            }
+        }
+        if (attrEndIdx == startIdx)
+        {
+            throw new IllegalArgumentException(
+                "Missing attribute name: " + filter.substring(startIdx, endIdx));
+        }
+        String attr = filter.substring(startIdx, attrEndIdx);
+
+        // Skip the attribute name and any following whitespace.
+        startIdx = skipWhitespace(filter, attrEndIdx);
+
+        // Determine the operator type.
+        int op = -1;
+        switch (filter.charAt(startIdx))
+        {
+            case '=':
+                op = EQ;
+                startIdx++;
+                break;
+            case '<':
+                if (filter.charAt(startIdx + 1) != '=')
+                {
+                    throw new IllegalArgumentException(
+                        "Unknown operator: " + filter.substring(startIdx, endIdx));
+                }
+                op = LTE;
+                startIdx += 2;
+                break;
+            case '>':
+                if (filter.charAt(startIdx + 1) != '=')
+                {
+                    throw new IllegalArgumentException(
+                        "Unknown operator: " + filter.substring(startIdx, endIdx));
+                }
+                op = GTE;
+                startIdx += 2;
+                break;
+            case '~':
+                if (filter.charAt(startIdx + 1) != '=')
+                {
+                    throw new IllegalArgumentException(
+                        "Unknown operator: " + filter.substring(startIdx, endIdx));
+                }
+                op = APPROX;
+                startIdx += 2;
+                break;
+            default:
+                throw new IllegalArgumentException(
+                    "Unknown operator: " + filter.substring(startIdx, endIdx));
+        }
+
+        // Parse value.
+        Object value = toDecodedString(filter, startIdx, endIdx);
+
+        // Check if the equality comparison is actually a substring
+        // or present operation.
+        if (op == EQ)
+        {
+            String valueStr = filter.substring(startIdx, endIdx);
+            List<String> values = parseSubstring(valueStr);
+            if ((values.size() == 2)
+                && (values.get(0).length() == 0)
+                && (values.get(1).length() == 0))
+            {
+                op = PRESENT;
+            }
+            else if (values.size() > 1)
+            {
+                op = SUBSTRING;
+                value = values;
+            }
+        }
+
+        return new SimpleFilter(attr, value, op);
+    }
+
+    public static List<String> parseSubstring(String value)
+    {
+        List<String> pieces = new ArrayList();
+        StringBuffer ss = new StringBuffer();
+        // int kind = SIMPLE; // assume until proven otherwise
+        boolean wasStar = false; // indicates last piece was a star
+        boolean leftstar = false; // track if the initial piece is a star
+        boolean rightstar = false; // track if the final piece is a star
+
+        int idx = 0;
+
+        // We assume (sub)strings can contain leading and trailing blanks
+        boolean escaped = false;
+loop:   for (;;)
+        {
+            if (idx >= value.length())
+            {
+                if (wasStar)
+                {
+                    // insert last piece as "" to handle trailing star
+                    rightstar = true;
+                }
+                else
+                {
+                    pieces.add(ss.toString());
+                    // accumulate the last piece
+                    // note that in the case of
+                    // (cn=); this might be
+                    // the string "" (!=null)
+                }
+                ss.setLength(0);
+                break loop;
+            }
+
+            // Read the next character and account for escapes.
+            char c = value.charAt(idx++);
+            if (!escaped && (c == '*'))
+            {
+                // If we have successive '*' characters, then we can
+                // effectively collapse them by ignoring succeeding ones.
+                if (!wasStar)
+                {
+                    if (ss.length() > 0)
+                    {
+                        pieces.add(ss.toString()); // accumulate the pieces
+                        // between '*' occurrences
+                    }
+                    ss.setLength(0);
+                    // if this is a leading star, then track it
+                    if (pieces.isEmpty())
+                    {
+                        leftstar = true;
+                    }
+                    wasStar = true;
+                }
+            }
+            else if (!escaped && (c == '\\'))
+            {
+                escaped = true;
+            }
+            else
+            {
+                escaped = false;
+                wasStar = false;
+                ss.append(c);
+            }
+        }
+        if (leftstar || rightstar || pieces.size() > 1)
+        {
+            // insert leading and/or trailing "" to anchor ends
+            if (rightstar)
+            {
+                pieces.add("");
+            }
+            if (leftstar)
+            {
+                pieces.add(0, "");
+            }
+        }
+        return pieces;
+    }
+
+    public static String unparseSubstring(List<String> pieces)
+    {
+        StringBuffer sb = new StringBuffer();
+        for (int i = 0; i < pieces.size(); i++)
+        {
+            if (i > 0)
+            {
+                sb.append("*");
+            }
+            sb.append(toEncodedString(pieces.get(i)));
+        }
+        return sb.toString();
+    }
+
+    public static boolean compareSubstring(List<String> pieces, String s)
+    {
+        // Walk the pieces to match the string
+        // There are implicit stars between each piece,
+        // and the first and last pieces might be "" to anchor the match.
+        // assert (pieces.length > 1)
+        // minimal case is <string>*<string>
+
+        boolean result = true;
+        int len = pieces.size();
+
+        // Special case, if there is only one piece, then
+        // we must perform an equality test.
+        if (len == 1)
+        {
+            return s.equals(pieces.get(0));
+        }
+
+        // Otherwise, check whether the pieces match
+        // the specified string.
+
+        int index = 0;
+
+loop:   for (int i = 0; i < len; i++)
+        {
+            String piece = pieces.get(i);
+
+            // If this is the first piece, then make sure the
+            // string starts with it.
+            if (i == 0)
+            {
+                if (!s.startsWith(piece))
+                {
+                    result = false;
+                    break loop;
+                }
+            }
+
+            // If this is the last piece, then make sure the
+            // string ends with it.
+            if (i == (len - 1))
+            {
+                if (s.endsWith(piece) && (s.length() >= (index + piece.length())))
+                {
+                    result = true;
+                }
+                else
+                {
+                    result = false;
+                }
+                break loop;
+            }
+
+            // If this is neither the first or last piece, then
+            // make sure the string contains it.
+            if ((i > 0) && (i < (len - 1)))
+            {
+                index = s.indexOf(piece, index);
+                if (index < 0)
+                {
+                    result = false;
+                    break loop;
+                }
+            }
+
+            // Move string index beyond the matching piece.
+            index += piece.length();
+        }
+
+        return result;
+    }
+
+    private static int skipWhitespace(String s, int startIdx)
+    {
+        int len = s.length();
+        while ((startIdx < len) && Character.isWhitespace(s.charAt(startIdx)))
+        {
+            startIdx++;
+        }
+        return startIdx;
+    }
+
+    /**
+     * Converts a attribute map to a filter. The filter is created by iterating
+     * over the map's entry set. If ordering of attributes is important (e.g.,
+     * for hitting attribute indices), then the map's entry set should iterate
+     * in the desired order. Equality testing is assumed for all attribute types
+     * other than version ranges, which are handled appropriated. If the attribute
+     * map is empty, then a filter that matches anything is returned.
+     * @param attrs Map of attributes to convert to a filter.
+     * @return A filter corresponding to the attributes.
+     */
+    public static SimpleFilter convert(Map<String, Object> attrs)
+    {
+        // Rather than building a filter string to be parsed into a SimpleFilter,
+        // we will just create the parsed SimpleFilter directly.
+
+        List<SimpleFilter> filters = new ArrayList<SimpleFilter>();
+
+        for (Entry<String, Object> entry : attrs.entrySet())
+        {
+            if (entry.getValue() instanceof VersionRange)
+            {
+                VersionRange vr = (VersionRange) entry.getValue();
+                if (vr.isFloorInclusive())
+                {
+                    filters.add(
+                        new SimpleFilter(
+                            entry.getKey(),
+                            vr.getFloor().toString(),
+                            SimpleFilter.GTE));
+                }
+                else
+                {
+                    SimpleFilter not =
+                        new SimpleFilter(null, new ArrayList(), SimpleFilter.NOT);
+                    ((List) not.getValue()).add(
+                        new SimpleFilter(
+                            entry.getKey(),
+                            vr.getFloor().toString(),
+                            SimpleFilter.LTE));
+                    filters.add(not);
+                }
+
+                if (vr.getCeiling() != null)
+                {
+                    if (vr.isCeilingInclusive())
+                    {
+                        filters.add(
+                            new SimpleFilter(
+                                entry.getKey(),
+                                vr.getCeiling().toString(),
+                                SimpleFilter.LTE));
+                    }
+                    else
+                    {
+                        SimpleFilter not =
+                            new SimpleFilter(null, new ArrayList(), SimpleFilter.NOT);
+                        ((List) not.getValue()).add(
+                            new SimpleFilter(
+                                entry.getKey(),
+                                vr.getCeiling().toString(),
+                                SimpleFilter.GTE));
+                        filters.add(not);
+                    }
+                }
+            }
+            else
+            {
+                List<String> values = SimpleFilter.parseSubstring(entry.getValue().toString());
+                if (values.size() > 1)
+                {
+                    filters.add(
+                        new SimpleFilter(
+                            entry.getKey(),
+                            values,
+                            SimpleFilter.SUBSTRING));
+                }
+                else
+                {
+                    filters.add(
+                        new SimpleFilter(
+                            entry.getKey(),
+                            values.get(0),
+                            SimpleFilter.EQ));
+                }
+            }
+        }
+
+        SimpleFilter sf = null;
+
+        if (filters.size() == 1)
+        {
+            sf = filters.get(0);
+        }
+        else if (attrs.size() > 1)
+        {
+            sf = new SimpleFilter(null, filters, SimpleFilter.AND);
+        }
+        else if (filters.isEmpty())
+        {
+            sf = new SimpleFilter(null, null, SimpleFilter.MATCH_ALL);
+        }
+
+        return sf;
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/felix/framework/ext/FelixBundleContext.java b/src/main/java/org/apache/felix/framework/ext/FelixBundleContext.java
new file mode 100644
index 0000000..a3bfa78
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/ext/FelixBundleContext.java
@@ -0,0 +1,30 @@
+/* 
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.ext;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+
+public interface FelixBundleContext extends BundleContext
+{
+    public void addRequirement(String s) throws BundleException;
+    public void removeRequirement() throws BundleException;
+    public void addCapability() throws BundleException;
+    public void removeCapability() throws BundleException;
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/felix/framework/ext/SecurityProvider.java b/src/main/java/org/apache/felix/framework/ext/SecurityProvider.java
new file mode 100644
index 0000000..2ae3c45
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/ext/SecurityProvider.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.ext;
+
+import java.security.Permission;
+import java.security.ProtectionDomain;
+
+import org.osgi.framework.Bundle;
+
+public interface SecurityProvider
+{
+    boolean hasBundlePermission(ProtectionDomain pd, Permission p, boolean direct);
+
+    Object getSignerMatcher(Bundle bundle, int signersType);
+    
+    void checkBundle(Bundle bundle) throws Exception;
+}
diff --git a/src/main/java/org/apache/felix/framework/resolver/CandidateComparator.java b/src/main/java/org/apache/felix/framework/resolver/CandidateComparator.java
new file mode 100644
index 0000000..dccd7ca
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/resolver/CandidateComparator.java
@@ -0,0 +1,101 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.resolver;
+
+import java.util.Comparator;
+import org.apache.felix.framework.wiring.BundleCapabilityImpl;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Version;
+import org.osgi.framework.wiring.BundleCapability;
+import org.osgi.framework.wiring.BundleRevision;
+
+public class CandidateComparator implements Comparator<BundleCapability>
+{
+    public int compare(BundleCapability cap1, BundleCapability cap2)
+    {
+        // First check resolved state, since resolved capabilities have priority
+        // over unresolved ones. Compare in reverse order since we want to sort
+        // in descending order.
+        int c = 0;
+        if ((cap1.getRevision().getWiring() != null)
+            && (cap2.getRevision().getWiring() == null))
+        {
+            c = -1;
+        }
+        else if ((cap1.getRevision().getWiring() == null)
+            && (cap2.getRevision().getWiring() != null))
+        {
+            c = 1;
+        }
+
+        // Compare revision capabilities.
+        if ((c == 0) && cap1.getNamespace().equals(BundleRevision.BUNDLE_NAMESPACE))
+        {
+            c = ((Comparable) cap1.getAttributes().get(BundleRevision.BUNDLE_NAMESPACE))
+                .compareTo(cap2.getAttributes().get(BundleRevision.BUNDLE_NAMESPACE));
+            if (c == 0)
+            {
+                Version v1 = (!cap1.getAttributes().containsKey(Constants.BUNDLE_VERSION_ATTRIBUTE))
+                    ? Version.emptyVersion
+                    : (Version) cap1.getAttributes().get(Constants.BUNDLE_VERSION_ATTRIBUTE);
+                Version v2 = (!cap2.getAttributes().containsKey(Constants.BUNDLE_VERSION_ATTRIBUTE))
+                    ? Version.emptyVersion
+                    : (Version) cap2.getAttributes().get(Constants.BUNDLE_VERSION_ATTRIBUTE);
+                // Compare these in reverse order, since we want
+                // highest version to have priority.
+                c = v2.compareTo(v1);
+            }
+        }
+        // Compare package capabilities.
+        else if ((c == 0) && cap1.getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE))
+        {
+            c = ((Comparable) cap1.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE))
+                .compareTo(cap2.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE));
+            if (c == 0)
+            {
+                Version v1 = (!cap1.getAttributes().containsKey(BundleCapabilityImpl.VERSION_ATTR))
+                    ? Version.emptyVersion
+                    : (Version) cap1.getAttributes().get(BundleCapabilityImpl.VERSION_ATTR);
+                Version v2 = (!cap2.getAttributes().containsKey(BundleCapabilityImpl.VERSION_ATTR))
+                    ? Version.emptyVersion
+                    : (Version) cap2.getAttributes().get(BundleCapabilityImpl.VERSION_ATTR);
+                // Compare these in reverse order, since we want
+                // highest version to have priority.
+                c = v2.compareTo(v1);
+            }
+        }
+
+        // Finally, compare bundle identity.
+        if (c == 0)
+        {
+            if (cap1.getRevision().getBundle().getBundleId() <
+                cap2.getRevision().getBundle().getBundleId())
+            {
+                c = -1;
+            }
+            else if (cap1.getRevision().getBundle().getBundleId() >
+                cap2.getRevision().getBundle().getBundleId())
+            {
+                c = 1;
+            }
+        }
+
+        return c;
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/felix/framework/resolver/Candidates.java b/src/main/java/org/apache/felix/framework/resolver/Candidates.java
new file mode 100644
index 0000000..9f57f95
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/resolver/Candidates.java
@@ -0,0 +1,1046 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.resolver;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.TreeMap;
+
+import org.apache.felix.framework.ResolveContextImpl;
+import org.apache.felix.framework.util.FelixConstants;
+import org.apache.felix.framework.util.Util;
+import org.apache.felix.framework.wiring.BundleCapabilityImpl;
+import org.apache.felix.framework.wiring.BundleRequirementImpl;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Version;
+import org.osgi.framework.wiring.BundleCapability;
+import org.osgi.framework.wiring.BundleRequirement;
+import org.osgi.framework.wiring.BundleRevision;
+import org.osgi.framework.wiring.BundleWire;
+import org.osgi.framework.wiring.BundleWiring;
+
+class Candidates
+{
+    public static final int MANDATORY = 0;
+    public static final int OPTIONAL = 1;
+    public static final int ON_DEMAND = 2;
+
+    private final Set<BundleRevision> m_mandatoryRevisions;
+    // Maps a capability to requirements that match it.
+    private final Map<BundleCapability, Set<BundleRequirement>> m_dependentMap;
+    // Maps a requirement to the capability it matches.
+    private final Map<BundleRequirement, List<BundleCapability>> m_candidateMap;
+    // Maps a bundle revision to its associated wrapped revision; this only happens
+    // when a revision being resolved has fragments to attach to it.
+    private final Map<BundleRevision, WrappedRevision> m_allWrappedHosts;
+    // Map used when populating candidates to hold intermediate and final results.
+    private final Map<BundleRevision, Object> m_populateResultCache;
+
+    // Flag to signal if fragments are present in the candidate map.
+    private boolean m_fragmentsPresent = false;
+
+    /**
+     * Private copy constructor used by the copy() method.
+     * @param dependentMap the capability dependency map.
+     * @param candidateMap the requirement candidate map.
+     * @param hostFragments the fragment map.
+     * @param wrappedHosts the wrapped hosts map.
+    **/
+    private Candidates(
+        Set<BundleRevision> mandatoryRevisions,
+        Map<BundleCapability, Set<BundleRequirement>> dependentMap,
+        Map<BundleRequirement, List<BundleCapability>> candidateMap,
+        Map<BundleRevision, WrappedRevision> wrappedHosts, Map<BundleRevision, Object> populateResultCache,
+        boolean fragmentsPresent)
+    {
+        m_mandatoryRevisions = mandatoryRevisions;
+        m_dependentMap = dependentMap;
+        m_candidateMap = candidateMap;
+        m_allWrappedHosts = wrappedHosts;
+        m_populateResultCache = populateResultCache;
+        m_fragmentsPresent = fragmentsPresent;
+    }
+
+    /**
+     * Constructs an empty Candidates object.
+    **/
+    public Candidates()
+    {
+        m_mandatoryRevisions = new HashSet<BundleRevision>();
+        m_dependentMap = new HashMap<BundleCapability, Set<BundleRequirement>>();
+        m_candidateMap = new HashMap<BundleRequirement, List<BundleCapability>>();
+        m_allWrappedHosts = new HashMap<BundleRevision, WrappedRevision>();
+        m_populateResultCache = new HashMap<BundleRevision, Object>();
+    }
+
+    /**
+     * Populates candidates for the specified revision. How a revision is
+     * resolved depends on its resolution type as follows:
+     * <ul>
+     *   <li><tt>MANDATORY</tt> - must resolve and failure to do so throws
+     *       an exception.</li>
+     *   <li><tt>OPTIONAL</tt> - attempt to resolve, but no exception is thrown
+     *       if the resolve fails.</li>
+     *   <li><tt>ON_DEMAND</tt> - only resolve on demand; this only applies to
+     *       fragments and will only resolve a fragment if its host is already
+     *       selected as a candidate.</li>
+     * </ul>
+     * @param state the resolver state used for populating the candidates.
+     * @param revision the revision whose candidates should be populated.
+     * @param resolution indicates the resolution type.
+     */
+    public final void populate(
+        ResolveContext rc, BundleRevision revision, int resolution)
+    {
+        // Get the current result cache value, to make sure the revision
+        // hasn't already been populated.
+        Object cacheValue = m_populateResultCache.get(revision);
+        // Has been unsuccessfully populated.
+        if (cacheValue instanceof ResolveException)
+        {
+            return;
+        }
+        // Has been successfully populated.
+        else if (cacheValue instanceof Boolean)
+        {
+            return;
+        }
+
+        // We will always attempt to populate fragments, since this is necessary
+        // for ondemand attaching of fragment. However, we'll only attempt to
+        // populate optional non-fragment revisions if they aren't already
+        // resolved.
+        boolean isFragment = Util.isFragment(revision);
+        if (!isFragment && (revision.getWiring() != null))
+        {
+            return;
+        }
+
+        // Always attempt to populate mandatory or optional revisions.
+        // However, for on-demand fragments only populate if their host
+        // is already populated.
+        if ((resolution != ON_DEMAND)
+            || (isFragment && populateFragmentOndemand(rc, revision)))
+        {
+            if (resolution == MANDATORY)
+            {
+                m_mandatoryRevisions.add(revision);
+            }
+            try
+            {
+                // Try to populate candidates for the optional revision.
+                populateRevision(rc, revision);
+            }
+            catch (ResolveException ex)
+            {
+                // Only throw an exception if resolution is mandatory.
+                if (resolution == MANDATORY)
+                {
+                    throw ex;
+                }
+            }
+        }
+    }
+
+    /**
+     * Populates candidates for the specified revision.
+     * @param state the resolver state used for populating the candidates.
+     * @param revision the revision whose candidates should be populated.
+     */
+// TODO: FELIX3 - Modify to not be recursive.
+    private void populateRevision(ResolveContext rc, BundleRevision revision)
+    {
+        // Determine if we've already calculated this revision's candidates.
+        // The result cache will have one of three values:
+        //   1. A resolve exception if we've already attempted to populate the
+        //      revision's candidates but were unsuccessful.
+        //   2. Boolean.TRUE indicating we've already attempted to populate the
+        //      revision's candidates and were successful.
+        //   3. An array containing the cycle count, current map of candidates
+        //      for already processed requirements, and a list of remaining
+        //      requirements whose candidates still need to be calculated.
+        // For case 1, rethrow the exception. For case 2, simply return immediately.
+        // For case 3, this means we have a cycle so we should continue to populate
+        // the candidates where we left off and not record any results globally
+        // until we've popped completely out of the cycle.
+
+        // Keeps track of the number of times we've reentered this method
+        // for the current revision.
+        Integer cycleCount = null;
+
+        // Keeps track of the candidates we've already calculated for the
+        // current revision's requirements.
+        Map<BundleRequirement, List<BundleCapability>> localCandidateMap = null;
+
+        // Keeps track of the current revision's requirements for which we
+        // haven't yet found candidates.
+        List<BundleRequirement> remainingReqs = null;
+
+        // Get the cache value for the current revision.
+        Object cacheValue = m_populateResultCache.get(revision);
+
+        // This is case 1.
+        if (cacheValue instanceof ResolveException)
+        {
+            throw (ResolveException) cacheValue;
+        }
+        // This is case 2.
+        else if (cacheValue instanceof Boolean)
+        {
+            return;
+        }
+        // This is case 3.
+        else if (cacheValue != null)
+        {
+            // Increment and get the cycle count.
+            cycleCount = (Integer)
+                (((Object[]) cacheValue)[0]
+                    = new Integer(((Integer) ((Object[]) cacheValue)[0]).intValue() + 1));
+            // Get the already populated candidates.
+            localCandidateMap = (Map) ((Object[]) cacheValue)[1];
+            // Get the remaining requirements.
+            remainingReqs = (List) ((Object[]) cacheValue)[2];
+        }
+
+        // If there is no cache value for the current revision, then this is
+        // the first time we are attempting to populate its candidates, so
+        // do some one-time checks and initialization.
+        if ((remainingReqs == null) && (localCandidateMap == null))
+        {
+            // Verify that any native libraries match the current platform.
+            ((ResolveContextImpl) rc).checkNativeLibraries(revision);
+
+            // Record cycle count.
+            cycleCount = new Integer(0);
+
+            // Create a local map for populating candidates first, just in case
+            // the revision is not resolvable.
+            localCandidateMap = new HashMap();
+
+            // Create a modifiable list of the revision's requirements.
+            remainingReqs = new ArrayList(revision.getDeclaredRequirements(null));
+
+            // Add these value to the result cache so we know we are
+            // in the middle of populating candidates for the current
+            // revision.
+            m_populateResultCache.put(revision,
+                cacheValue = new Object[] { cycleCount, localCandidateMap, remainingReqs });
+        }
+
+        // If we have requirements remaining, then find candidates for them.
+        while (!remainingReqs.isEmpty())
+        {
+            BundleRequirement req = remainingReqs.remove(0);
+
+            // Ignore non-effective and dynamic requirements.
+            String resolution = req.getDirectives().get(Constants.RESOLUTION_DIRECTIVE);
+            if (!rc.isEffective(req)
+                || ((resolution != null)
+                    && resolution.equals(FelixConstants.RESOLUTION_DYNAMIC)))
+            {
+                continue;
+            }
+
+            // Process the candidates, removing any candidates that
+            // cannot resolve.
+            List<BundleCapability> candidates = rc.findProviders(req, true);
+            ResolveException rethrow = processCandidates(rc, revision, candidates);
+
+            // First, due to cycles, makes sure we haven't already failed in
+            // a deeper recursion.
+            Object result = m_populateResultCache.get(revision);
+            if (result instanceof ResolveException)
+            {
+                throw (ResolveException) result;
+            }
+            // Next, if are no candidates remaining and the requirement is not
+            // not optional, then record and throw a resolve exception.
+            else if (candidates.isEmpty() && !((BundleRequirementImpl) req).isOptional())
+            {
+                String msg = "Unable to resolve " + revision
+                    + ": missing requirement " + req;
+                if (rethrow != null)
+                {
+                    msg = msg + " [caused by: " + rethrow.getMessage() + "]";
+                }
+                rethrow = new ResolveException(msg, revision, req);
+                m_populateResultCache.put(revision, rethrow);
+                throw rethrow;
+            }
+            // Otherwise, if we actually have candidates for the requirement, then
+            // add them to the local candidate map.
+            else if (candidates.size() > 0)
+            {
+                localCandidateMap.put(req, candidates);
+            }
+        }
+
+        // If we are exiting from a cycle then decrement
+        // cycle counter, otherwise record the result.
+        if (cycleCount.intValue() > 0)
+        {
+            ((Object[]) cacheValue)[0] = new Integer(cycleCount.intValue() - 1);
+        }
+        else if (cycleCount.intValue() == 0)
+        {
+            // Record that the revision was successfully populated.
+            m_populateResultCache.put(revision, Boolean.TRUE);
+
+            // Merge local candidate map into global candidate map.
+            if (localCandidateMap.size() > 0)
+            {
+                add(localCandidateMap);
+            }
+        }
+    }
+
+    private boolean populateFragmentOndemand(ResolveContext rc, BundleRevision revision)
+        throws ResolveException
+    {
+        // Create a modifiable list of the revision's requirements.
+        List<BundleRequirement> remainingReqs =
+            new ArrayList(revision.getDeclaredRequirements(null));
+        // Find the host requirement.
+        BundleRequirement hostReq = null;
+        for (Iterator<BundleRequirement> it = remainingReqs.iterator();
+            it.hasNext(); )
+        {
+            BundleRequirement r = it.next();
+            if (r.getNamespace().equals(BundleRevision.HOST_NAMESPACE))
+            {
+                hostReq = r;
+                it.remove();
+                break;
+            }
+        }
+        // Get candidates hosts and keep any that have been populated.
+        List<BundleCapability> hosts = rc.findProviders(hostReq, false);
+        for (Iterator<BundleCapability> it = hosts.iterator(); it.hasNext(); )
+        {
+            BundleCapability host = it.next();
+            if (!isPopulated(host.getRevision()))
+            {
+                it.remove();
+            }
+        }
+        // If there aren't any populated hosts, then we can just
+        // return since this fragment isn't needed.
+        if (hosts.isEmpty())
+        {
+            return false;
+        }
+        // If there are populates host candidates, then finish up
+        // some other checks and prepopulate the result cache with
+        // the work we've done so far.
+        // Verify that any native libraries match the current platform.
+        ((ResolveContextImpl) rc).checkNativeLibraries(revision);
+        // Record cycle count, but start at -1 since it will
+        // be incremented again in populate().
+        Integer cycleCount = new Integer(-1);
+        // Create a local map for populating candidates first, just in case
+        // the revision is not resolvable.
+        Map<BundleRequirement, List<BundleCapability>> localCandidateMap =
+            new HashMap<BundleRequirement, List<BundleCapability>>();
+        // Add the discovered host candidates to the local candidate map.
+        localCandidateMap.put(hostReq, hosts);
+        // Add these value to the result cache so we know we are
+        // in the middle of populating candidates for the current
+        // revision.
+        m_populateResultCache.put(revision,
+            new Object[] { cycleCount, localCandidateMap, remainingReqs });
+        return true;
+    }
+
+    public void populateDynamic(
+        ResolveContext rc, BundleRevision revision,
+        BundleRequirement req, List<BundleCapability> candidates)
+    {
+        // Record the revision associated with the dynamic require
+        // as a mandatory revision.
+        m_mandatoryRevisions.add(revision);
+
+        // Add the dynamic imports candidates.
+        add(req, candidates);
+
+        // Process the candidates, removing any candidates that
+        // cannot resolve.
+        ResolveException rethrow = processCandidates(rc, revision, candidates);
+
+        if (candidates.isEmpty())
+        {
+            if (rethrow == null)
+            {
+                rethrow = new ResolveException("Dynamic import failed.", revision, req);
+            }
+            throw rethrow;
+        }
+
+        m_populateResultCache.put(revision, Boolean.TRUE);
+    }
+
+    /**
+     * This method performs common processing on the given set of candidates.
+     * Specifically, it removes any candidates which cannot resolve and it
+     * synthesizes candidates for any candidates coming from any attached
+     * fragments, since fragment capabilities only appear once, but technically
+     * each host represents a unique capability.
+     * @param state the resolver state.
+     * @param revision the revision being resolved.
+     * @param candidates the candidates to process.
+     * @return a resolve exception to be re-thrown, if any, or null.
+     */
+    private ResolveException processCandidates(
+        ResolveContext rc,
+        BundleRevision revision,
+        List<BundleCapability> candidates)
+    {
+        // Get satisfying candidates and populate their candidates if necessary.
+        ResolveException rethrow = null;
+        Set<BundleCapability> fragmentCands = null;
+        for (Iterator<BundleCapability> itCandCap = candidates.iterator();
+            itCandCap.hasNext(); )
+        {
+            BundleCapability candCap = itCandCap.next();
+
+            boolean isFragment = Util.isFragment(candCap.getRevision());
+
+            // If the capability is from a fragment, then record it
+            // because we have to insert associated host capabilities
+            // if the fragment is already attached to any hosts.
+            if (isFragment)
+            {
+                if (fragmentCands == null)
+                {
+                    fragmentCands = new HashSet<BundleCapability>();
+                }
+                fragmentCands.add(candCap);
+            }
+
+            // If the candidate revision is a fragment, then always attempt
+            // to populate candidates for its dependency, since it must be
+            // attached to a host to be used. Otherwise, if the candidate
+            // revision is not already resolved and is not the current version
+            // we are trying to populate, then populate the candidates for
+            // its dependencies as well.
+            // NOTE: Technically, we don't have to check to see if the
+            // candidate revision is equal to the current revision, but this
+            // saves us from recursing and also simplifies exceptions messages
+            // since we effectively chain exception messages for each level
+            // of recursion; thus, any avoided recursion results in fewer
+            // exceptions to chain when an error does occur.
+            if ((isFragment || (candCap.getRevision().getWiring() == null))
+                && !candCap.getRevision().equals(revision))
+            {
+                try
+                {
+                    populateRevision(rc, candCap.getRevision());
+                }
+                catch (ResolveException ex)
+                {
+                    if (rethrow == null)
+                    {
+                        rethrow = ex;
+                    }
+                    // Remove the candidate since we weren't able to
+                    // populate its candidates.
+                    itCandCap.remove();
+                }
+            }
+        }
+
+        // If any of the candidates for the requirement were from a fragment,
+        // then also insert synthesized hosted capabilities for any other host
+        // to which the fragment is attached since they are all effectively
+        // unique capabilities.
+        if (fragmentCands != null)
+        {
+            for (BundleCapability fragCand : fragmentCands)
+            {
+                // Only necessary for resolved fragments.
+                BundleWiring wiring = fragCand.getRevision().getWiring();
+                if (wiring != null)
+                {
+                    // Fragments only have host wire, so each wire represents
+                    // an attached host.
+                    for (BundleWire wire : wiring.getRequiredWires(null))
+                    {
+                        // If the capability is a package, then make sure the
+                        // host actually provides it in its resolved capabilities,
+                        // since it may be a substitutable export.
+                        if (!fragCand.getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE)
+                            || wire.getProviderWiring().getCapabilities(null).contains(fragCand))
+                        {
+                            // Note that we can just add this as a candidate
+                            // directly, since we know it is already resolved.
+                            // NOTE: We are synthesizing a hosted capability here,
+                            // but we are not using a ShadowList like we do when
+                            // we synthesizing capabilities for unresolved hosts.
+                            // It is not necessary to use the ShadowList here since
+                            // the host is resolved, because in that case we can
+                            // calculate the proper package space by traversing
+                            // the wiring. In the unresolved case, this isn't possible
+                            // so we need to use the ShadowList so we can keep
+                            // a reference to a synthesized resource with attached
+                            // fragments so we can correctly calculate its package
+                            // space.
+                            rc.insertHostedCapability(candidates,
+                                new WrappedCapability(
+                                    wire.getCapability().getRevision(),
+                                    (BundleCapabilityImpl) fragCand));
+                        }
+                    }
+                }
+            }
+        }
+
+        return rethrow;
+    }
+
+    public boolean isPopulated(BundleRevision revision)
+    {
+        Object value = m_populateResultCache.get(revision);
+        return ((value != null) && (value instanceof Boolean));
+    }
+
+    public ResolveException getResolveException(BundleRevision revision)
+    {
+        Object value = m_populateResultCache.get(revision);
+        return ((value != null) && (value instanceof ResolveException))
+            ? (ResolveException) value : null;
+    }
+
+    /**
+     * Adds a requirement and its matching candidates to the internal data
+     * structure. This method assumes it owns the data being passed in and
+     * does not make a copy. It takes the data and processes, such as calculating
+     * which requirements depend on which capabilities and recording any fragments
+     * it finds for future merging.
+     * @param req the requirement to add.
+     * @param candidates the candidates matching the requirement.
+    **/
+    private void add(BundleRequirement req, List<BundleCapability> candidates)
+    {
+        if (req.getNamespace().equals(BundleRevision.HOST_NAMESPACE))
+        {
+            m_fragmentsPresent = true;
+        }
+
+        // Record the candidates.
+        m_candidateMap.put(req, candidates);
+    }
+
+    /**
+     * Adds requirements and candidates in bulk. The outer map is not retained
+     * by this method, but the inner data structures are, so they should not
+     * be further modified by the caller.
+     * @param candidates the bulk requirements and candidates to add.
+    **/
+    private void add(Map<BundleRequirement, List<BundleCapability>> candidates)
+    {
+        for (Entry<BundleRequirement, List<BundleCapability>> entry : candidates.entrySet())
+        {
+            add(entry.getKey(), entry.getValue());
+        }
+    }
+
+    /**
+     * Returns the wrapped module associated with the given module. If the module
+     * was not wrapped, then the module itself is returned. This is really only
+     * needed to determine if the root modules of the resolve have been wrapped.
+     * @param m the module whose wrapper is desired.
+     * @return the wrapper module or the module itself if it was not wrapped.
+    **/
+    public BundleRevision getWrappedHost(BundleRevision m)
+    {
+        BundleRevision wrapped = m_allWrappedHosts.get(m);
+        return (wrapped == null) ? m : wrapped;
+    }
+
+    /**
+     * Gets the candidates associated with a given requirement.
+     * @param req the requirement whose candidates are desired.
+     * @return the matching candidates or null.
+    **/
+    public List<BundleCapability> getCandidates(BundleRequirement req)
+    {
+        return m_candidateMap.get(req);
+    }
+
+    /**
+     * Merges fragments into their hosts. It does this by wrapping all host
+     * modules and attaching their selected fragments, removing all unselected
+     * fragment modules, and replacing all occurrences of the original fragments
+     * in the internal data structures with the wrapped host modules instead.
+     * Thus, fragment capabilities and requirements are merged into the appropriate
+     * host and the candidates for the fragment now become candidates for the host.
+     * Likewise, any module depending on a fragment now depend on the host. Note
+     * that this process is sort of like multiplication, since one fragment that
+     * can attach to two hosts effectively gets multiplied across the two hosts.
+     * So, any modules being satisfied by the fragment will end up having the
+     * two hosts as potential candidates, rather than the single fragment.
+     * @param existingSingletons existing resolved singletons.
+     * @throws ResolveException if the removal of any unselected fragments result
+     *         in the root module being unable to resolve.
+    **/
+    public void prepare(ResolveContext rc)
+    {
+        // Maps a host capability to a map containing its potential fragments;
+        // the fragment map maps a fragment symbolic name to a map that maps
+        // a version to a list of fragments requirements matching that symbolic
+        // name and version.
+        Map<BundleCapability, Map<String, Map<Version, List<BundleRequirement>>>>
+            hostFragments = Collections.EMPTY_MAP;
+        if (m_fragmentsPresent)
+        {
+            hostFragments = populateDependents();
+        }
+
+        // This method performs the following steps:
+        // 1. Select the fragments to attach to a given host.
+        // 2. Wrap hosts and attach fragments.
+        // 3. Remove any unselected fragments. This is necessary because
+        //    other revisions may depend on the capabilities of unselected
+        //    fragments, so we need to remove the unselected fragments and
+        //    any revisions that depends on them, which could ultimately cause
+        //    the entire resolve to fail.
+        // 4. Replace all fragments with any host it was merged into
+        //    (effectively multiplying it).
+        //    * This includes setting candidates for attached fragment
+        //      requirements as well as replacing fragment capabilities
+        //      with host's attached fragment capabilities.
+
+        // Steps 1 and 2
+        List<WrappedRevision> hostRevisions = new ArrayList<WrappedRevision>();
+        List<BundleRevision> unselectedFragments = new ArrayList<BundleRevision>();
+        for (Entry<BundleCapability, Map<String, Map<Version, List<BundleRequirement>>>>
+            hostEntry : hostFragments.entrySet())
+        {
+            // Step 1
+            BundleCapability hostCap = hostEntry.getKey();
+            Map<String, Map<Version, List<BundleRequirement>>> fragments
+                = hostEntry.getValue();
+            List<BundleRevision> selectedFragments = new ArrayList<BundleRevision>();
+            for (Entry<String, Map<Version, List<BundleRequirement>>> fragEntry
+                : fragments.entrySet())
+            {
+                boolean isFirst = true;
+                for (Entry<Version, List<BundleRequirement>> versionEntry
+                    : fragEntry.getValue().entrySet())
+                {
+                    for (BundleRequirement hostReq : versionEntry.getValue())
+                    {
+                        // Selecting the first fragment in each entry, which
+                        // is equivalent to selecting the highest version of
+                        // each fragment with a given symbolic name.
+                        if (isFirst)
+                        {
+                            selectedFragments.add(hostReq.getRevision());
+                            isFirst = false;
+                        }
+                        // For any fragment that wasn't selected, remove the
+                        // current host as a potential host for it and remove it
+                        // as a dependent on the host. If there are no more
+                        // potential hosts for the fragment, then mark it as
+                        // unselected for later removal.
+                        else
+                        {
+                            m_dependentMap.get(hostCap).remove(hostReq);
+                            List<BundleCapability> hosts = m_candidateMap.get(hostReq);
+                            hosts.remove(hostCap);
+                            if (hosts.isEmpty())
+                            {
+                                unselectedFragments.add(hostReq.getRevision());
+                            }
+                        }
+                    }
+                }
+            }
+
+            // Step 2
+            WrappedRevision wrappedHost =
+                new WrappedRevision(hostCap.getRevision(), selectedFragments);
+            hostRevisions.add(wrappedHost);
+            m_allWrappedHosts.put(hostCap.getRevision(), wrappedHost);
+        }
+
+        // Step 3
+        for (BundleRevision br : unselectedFragments)
+        {
+            removeRevision(br,
+                new ResolveException(
+                    "Fragment was not selected for attachment.", br, null));
+        }
+
+        // Step 4
+        for (WrappedRevision hostRevision : hostRevisions)
+        {
+            // Replaces capabilities from fragments with the capabilities
+            // from the merged host.
+            for (BundleCapability c : hostRevision.getDeclaredCapabilities(null))
+            {
+                // Don't replace the host capability, since the fragment will
+                // really be attached to the original host, not the wrapper.
+                if (!c.getNamespace().equals(BundleRevision.HOST_NAMESPACE))
+                {
+                    BundleCapability origCap = ((HostedCapability) c).getDeclaredCapability();
+                    // Note that you might think we could remove the original cap
+                    // from the dependent map, but you can't since it may come from
+                    // a fragment that is attached to multiple hosts, so each host
+                    // will need to make their own copy.
+                    Set<BundleRequirement> dependents = m_dependentMap.get(origCap);
+                    if (dependents != null)
+                    {
+                        dependents = new HashSet<BundleRequirement>(dependents);
+                        m_dependentMap.put(c, dependents);
+                        for (BundleRequirement r : dependents)
+                        {
+                            // We have synthesized hosted capabilities for all
+                            // fragments that have been attached to hosts by
+                            // wrapping the host bundle and their attached
+                            // fragments. We need to use the ResolveContext to
+                            // determine the proper priority order for hosted
+                            // capabilities since the order may depend on the
+                            // declaring host/fragment combination. However,
+                            // internally we completely wrap the host revision
+                            // and make all capabilities/requirements point back
+                            // to the wrapped host not the declaring host. The
+                            // ResolveContext expects HostedCapabilities to point
+                            // to the declaring revision, so we need two separate
+                            // candidate lists: one for the ResolveContext with
+                            // HostedCapabilities pointing back to the declaring
+                            // host and one for the resolver with HostedCapabilities
+                            // pointing back to the wrapped host. We ask the
+                            // ResolveContext to insert its appropriate HostedCapability
+                            // into its list, then we mirror the insert into a
+                            // shadow list with the resolver's HostedCapability.
+                            // We only need to ask the ResolveContext to find
+                            // the insert position for fragment caps since these
+                            // were synthesized and we don't know their priority.
+                            // However, in the resolver's candidate list we need
+                            // to replace all caps with the wrapped caps, no
+                            // matter if they come from the host or fragment,
+                            // since we are completing replacing the declaring
+                            // host and fragments with the wrapped host.
+                            List<BundleCapability> cands = m_candidateMap.get(r);
+                            if (!(cands instanceof ShadowList))
+                            {
+                                ShadowList<BundleCapability> shadow =
+                                    new ShadowList<BundleCapability>(cands);
+                                m_candidateMap.put(r, shadow);
+                                cands = shadow;
+                            }
+
+                            // If the original capability is from a fragment, then
+                            // ask the ResolveContext to insert it and update the
+                            // shadow copy of the list accordingly.
+                            if (!origCap.getRevision().equals(hostRevision.getHost()))
+                            {
+                                List<BundleCapability> original = ((ShadowList) cands).getOriginal();
+                                int removeIdx = original.indexOf(origCap);
+                                original.remove(removeIdx);
+                                int insertIdx = rc.insertHostedCapability(
+                                    original,
+                                    new SimpleHostedCapability(hostRevision.getHost(), origCap));
+                                cands.remove(removeIdx);
+                                cands.add(insertIdx, c);
+                            }
+                            // If the original capability is from the host, then
+                            // we just need to replace it in the shadow list.
+                            else
+                            {
+                                int idx = cands.indexOf(origCap);
+                                cands.set(idx, c);
+                            }
+                        }
+                    }
+                }
+            }
+
+            // Copy candidates for fragment requirements to the host.
+            for (BundleRequirement r : hostRevision.getDeclaredRequirements(null))
+            {
+                BundleRequirement origReq =
+                    ((WrappedRequirement) r).getOriginalRequirement();
+                List<BundleCapability> cands = m_candidateMap.get(origReq);
+                if (cands != null)
+                {
+                    m_candidateMap.put(r, new ArrayList<BundleCapability>(cands));
+                    for (BundleCapability cand : cands)
+                    {
+                        Set<BundleRequirement> dependents = m_dependentMap.get(cand);
+                        dependents.remove(origReq);
+                        dependents.add(r);
+                    }
+                }
+            }
+        }
+
+        // Lastly, verify that all mandatory revisions are still
+        // populated, since some might have become unresolved after
+        // selecting fragments/singletons.
+        for (BundleRevision br : m_mandatoryRevisions)
+        {
+            if (!isPopulated(br))
+            {
+                throw getResolveException(br);
+            }
+        }
+    }
+
+    // Maps a host capability to a map containing its potential fragments;
+    // the fragment map maps a fragment symbolic name to a map that maps
+    // a version to a list of fragments requirements matching that symbolic
+    // name and version.
+    private Map<BundleCapability,
+        Map<String, Map<Version, List<BundleRequirement>>>> populateDependents()
+    {
+        Map<BundleCapability, Map<String, Map<Version, List<BundleRequirement>>>>
+            hostFragments = new HashMap<BundleCapability,
+                Map<String, Map<Version, List<BundleRequirement>>>>();
+        for (Entry<BundleRequirement, List<BundleCapability>> entry : m_candidateMap.entrySet())
+        {
+            BundleRequirement req = entry.getKey();
+            List<BundleCapability> caps = entry.getValue();
+            for (BundleCapability cap : caps)
+            {
+                // Record the requirement as dependent on the capability.
+                Set<BundleRequirement> dependents = m_dependentMap.get(cap);
+                if (dependents == null)
+                {
+                    dependents = new HashSet<BundleRequirement>();
+                    m_dependentMap.put(cap, dependents);
+                }
+                dependents.add(req);
+
+                // Keep track of hosts and associated fragments.
+                if (req.getNamespace().equals(BundleRevision.HOST_NAMESPACE))
+                {
+                    Map<String, Map<Version, List<BundleRequirement>>>
+                        fragments = hostFragments.get(cap);
+                    if (fragments == null)
+                    {
+                        fragments = new HashMap<String, Map<Version, List<BundleRequirement>>>();
+                        hostFragments.put(cap, fragments);
+                    }
+                    Map<Version, List<BundleRequirement>> fragmentVersions =
+                        fragments.get(req.getRevision().getSymbolicName());
+                    if (fragmentVersions == null)
+                    {
+                        fragmentVersions =
+                            new TreeMap<Version, List<BundleRequirement>>(Collections.reverseOrder());
+                        fragments.put(req.getRevision().getSymbolicName(), fragmentVersions);
+                    }
+                    List<BundleRequirement> actual = fragmentVersions.get(req.getRevision().getVersion());
+                    if (actual == null)
+                    {
+                        actual = new ArrayList<BundleRequirement>();
+                        fragmentVersions.put(req.getRevision().getVersion(), actual);
+                    }
+                    actual.add(req);
+                }
+            }
+        }
+
+        return hostFragments;
+    }
+
+    /**
+     * Removes a module from the internal data structures if it wasn't selected
+     * as a fragment or a singleton. This process may cause other modules to
+     * become unresolved if they depended on the module's capabilities and there
+     * is no other candidate.
+     * @param revision the module to remove.
+     * @throws ResolveException if removing the module caused the resolve to fail.
+    **/
+    private void removeRevision(BundleRevision revision, ResolveException ex)
+    {
+        // Add removal reason to result cache.
+        m_populateResultCache.put(revision, ex);
+        // Remove from dependents.
+        Set<BundleRevision> unresolvedRevisions = new HashSet<BundleRevision>();
+        remove(revision, unresolvedRevisions);
+        // Remove dependents that failed as a result of removing revision.
+        while (!unresolvedRevisions.isEmpty())
+        {
+            Iterator<BundleRevision> it = unresolvedRevisions.iterator();
+            revision = it.next();
+            it.remove();
+            remove(revision, unresolvedRevisions);
+        }
+    }
+
+    /**
+     * Removes the specified module from the internal data structures, which
+     * involves removing its requirements and its capabilities. This may cause
+     * other modules to become unresolved as a result.
+     * @param br the module to remove.
+     * @param unresolvedRevisions a list to containing any additional modules that
+     *        that became unresolved as a result of removing this module and will
+     *        also need to be removed.
+     * @throws ResolveException if removing the module caused the resolve to fail.
+    **/
+    private void remove(BundleRevision br, Set<BundleRevision> unresolvedRevisions)
+        throws ResolveException
+    {
+        for (BundleRequirement r : br.getDeclaredRequirements(null))
+        {
+            remove(r);
+        }
+
+        for (BundleCapability c : br.getDeclaredCapabilities(null))
+        {
+            remove(c, unresolvedRevisions);
+        }
+    }
+
+    /**
+     * Removes a requirement from the internal data structures.
+     * @param req the requirement to remove.
+    **/
+    private void remove(BundleRequirement req)
+    {
+        boolean isFragment = req.getNamespace().equals(BundleRevision.HOST_NAMESPACE);
+
+        List<BundleCapability> candidates = m_candidateMap.remove(req);
+        if (candidates != null)
+        {
+            for (BundleCapability cap : candidates)
+            {
+                Set<BundleRequirement> dependents = m_dependentMap.get(cap);
+                if (dependents != null)
+                {
+                    dependents.remove(req);
+                }
+            }
+        }
+    }
+
+    /**
+     * Removes a capability from the internal data structures. This may cause
+     * other modules to become unresolved as a result.
+     * @param c the capability to remove.
+     * @param unresolvedRevisions a list to containing any additional modules that
+     *        that became unresolved as a result of removing this module and will
+     *        also need to be removed.
+     * @throws ResolveException if removing the module caused the resolve to fail.
+    **/
+    private void remove(BundleCapability c, Set<BundleRevision> unresolvedRevisions)
+        throws ResolveException
+    {
+        Set<BundleRequirement> dependents = m_dependentMap.remove(c);
+        if (dependents != null)
+        {
+            for (BundleRequirement r : dependents)
+            {
+                List<BundleCapability> candidates = m_candidateMap.get(r);
+                candidates.remove(c);
+                if (candidates.isEmpty())
+                {
+                    m_candidateMap.remove(r);
+                    if (!((BundleRequirementImpl) r).isOptional())
+                    {
+                        String msg = "Unable to resolve " + r.getRevision()
+                            + ": missing requirement " + r;
+                        m_populateResultCache.put(
+                            r.getRevision(), new ResolveException(msg, r.getRevision(), r));
+                        unresolvedRevisions.add(r.getRevision());
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Creates a copy of the Candidates object. This is used for creating
+     * permutations when package space conflicts are discovered.
+     * @return copy of this Candidates object.
+    **/
+    public Candidates copy()
+    {
+        Map<BundleCapability, Set<BundleRequirement>> dependentMap =
+            new HashMap<BundleCapability, Set<BundleRequirement>>();
+        for (Entry<BundleCapability, Set<BundleRequirement>> entry : m_dependentMap.entrySet())
+        {
+            Set<BundleRequirement> dependents = new HashSet<BundleRequirement>(entry.getValue());
+            dependentMap.put(entry.getKey(), dependents);
+        }
+
+        Map<BundleRequirement, List<BundleCapability>> candidateMap =
+            new HashMap<BundleRequirement, List<BundleCapability>>();
+        for (Entry<BundleRequirement, List<BundleCapability>> entry
+            : m_candidateMap.entrySet())
+        {
+            List<BundleCapability> candidates =
+                new ArrayList<BundleCapability>(entry.getValue());
+            candidateMap.put(entry.getKey(), candidates);
+        }
+
+        return new Candidates(
+            m_mandatoryRevisions, dependentMap, candidateMap,
+            m_allWrappedHosts, m_populateResultCache, m_fragmentsPresent);
+    }
+
+    public void dump()
+    {
+        // Create set of all revisions from requirements.
+        Set<BundleRevision> revisions = new HashSet<BundleRevision>();
+        for (Entry<BundleRequirement, List<BundleCapability>> entry
+            : m_candidateMap.entrySet())
+        {
+            revisions.add(entry.getKey().getRevision());
+        }
+        // Now dump the revisions.
+        System.out.println("=== BEGIN CANDIDATE MAP ===");
+        for (BundleRevision br : revisions)
+        {
+            System.out.println("  " + br
+                 + " (" + ((br.getWiring() != null) ? "RESOLVED)" : "UNRESOLVED)"));
+            List<BundleRequirement> reqs = (br.getWiring() != null)
+                ? br.getWiring().getRequirements(null)
+                : br.getDeclaredRequirements(null);
+            for (BundleRequirement req : reqs)
+            {
+                List<BundleCapability> candidates = m_candidateMap.get(req);
+                if ((candidates != null) && (candidates.size() > 0))
+                {
+                    System.out.println("    " + req + ": " + candidates);
+                }
+            }
+            reqs = (br.getWiring() != null)
+                ? Util.getDynamicRequirements(br.getWiring().getRequirements(null))
+                : Util.getDynamicRequirements(br.getDeclaredRequirements(null));
+            for (BundleRequirement req : reqs)
+            {
+                List<BundleCapability> candidates = m_candidateMap.get(req);
+                if ((candidates != null) && (candidates.size() > 0))
+                {
+                    System.out.println("    " + req + ": " + candidates);
+                }
+            }
+        }
+        System.out.println("=== END CANDIDATE MAP ===");
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/felix/framework/resolver/HostedCapability.java b/src/main/java/org/apache/felix/framework/resolver/HostedCapability.java
new file mode 100644
index 0000000..fc45301
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/resolver/HostedCapability.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) OSGi Alliance (2012). All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.framework.resolver;
+
+import org.osgi.framework.wiring.BundleCapability;
+import org.osgi.framework.wiring.BundleRevision;
+
+public interface HostedCapability extends BundleCapability {
+
+	BundleRevision getRevision();
+
+	BundleCapability getDeclaredCapability();
+}
diff --git a/src/main/java/org/apache/felix/framework/resolver/ResolveContext.java b/src/main/java/org/apache/felix/framework/resolver/ResolveContext.java
new file mode 100644
index 0000000..2af0696
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/resolver/ResolveContext.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) OSGi Alliance (2011, 2012). All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.framework.resolver;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import org.osgi.framework.wiring.BundleCapability;
+import org.osgi.framework.wiring.BundleRequirement;
+import org.osgi.framework.wiring.BundleRevision;
+import org.osgi.framework.wiring.BundleWiring;
+
+public abstract class ResolveContext
+{
+    public Collection<BundleRevision> getMandatoryRevisions()
+    {
+        return emptyCollection();
+    }
+
+    public Collection<BundleRevision> getOptionalRevisions()
+    {
+        return emptyCollection();
+    }
+
+    private static <T> Collection<T> emptyCollection()
+    {
+        return Collections.EMPTY_LIST;
+    }
+
+    public abstract List<BundleCapability> findProviders(BundleRequirement br, boolean obeyMandatory);
+
+    public abstract int insertHostedCapability(List<BundleCapability> caps, HostedCapability hc);
+
+    public abstract boolean isEffective(BundleRequirement br);
+
+    public abstract Map<BundleRevision, BundleWiring> getWirings();
+}
diff --git a/src/main/java/org/apache/felix/framework/resolver/ResolveException.java b/src/main/java/org/apache/felix/framework/resolver/ResolveException.java
new file mode 100644
index 0000000..360ef3d
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/resolver/ResolveException.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.resolver;
+
+import org.osgi.framework.wiring.BundleRequirement;
+import org.osgi.framework.wiring.BundleRevision;
+
+public class ResolveException extends RuntimeException
+{
+    private final BundleRevision m_revision;
+    private final BundleRequirement m_req;
+
+    /**
+     * Constructs an instance of <code>ResolveException</code> with the specified detail message.
+     * @param msg the detail message.
+     */
+    public ResolveException(String msg, BundleRevision revision, BundleRequirement req)
+    {
+        super(msg);
+        m_revision = revision;
+        m_req = req;
+    }
+
+    public BundleRevision getRevision()
+    {
+        return m_revision;
+    }
+
+    public BundleRequirement getRequirement()
+    {
+        return m_req;
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/felix/framework/resolver/Resolver.java b/src/main/java/org/apache/felix/framework/resolver/Resolver.java
new file mode 100644
index 0000000..107e4a7
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/resolver/Resolver.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.resolver;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.SortedSet;
+import org.osgi.framework.wiring.BundleCapability;
+import org.osgi.framework.wiring.BundleRequirement;
+import org.osgi.framework.wiring.BundleRevision;
+
+public interface Resolver
+{
+    Map<BundleRevision, List<ResolverWire>> resolve(ResolveContext rc);
+    Map<BundleRevision, List<ResolverWire>> resolve(
+        ResolveContext rc, BundleRevision revision, String pkgName);
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/felix/framework/resolver/ResolverImpl.java b/src/main/java/org/apache/felix/framework/resolver/ResolverImpl.java
new file mode 100644
index 0000000..2512144
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/resolver/ResolverImpl.java
@@ -0,0 +1,1811 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.resolver;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.SortedSet;
+import org.apache.felix.framework.BundleWiringImpl;
+import org.apache.felix.framework.Logger;
+import org.apache.felix.framework.ResolveContextImpl;
+import org.apache.felix.framework.capabilityset.CapabilitySet;
+import org.apache.felix.framework.util.FelixConstants;
+import org.apache.felix.framework.util.Util;
+import org.apache.felix.framework.wiring.BundleCapabilityImpl;
+import org.apache.felix.framework.wiring.BundleRequirementImpl;
+import org.osgi.framework.Constants;
+import org.osgi.framework.wiring.BundleCapability;
+import org.osgi.framework.wiring.BundleRequirement;
+import org.osgi.framework.wiring.BundleRevision;
+import org.osgi.framework.wiring.BundleWire;
+
+public class ResolverImpl implements Resolver
+{
+    private final Logger m_logger;
+
+    // Holds candidate permutations based on permutating "uses" chains.
+    // These permutations are given higher priority.
+    private final List<Candidates> m_usesPermutations = new ArrayList<Candidates>();
+    // Holds candidate permutations based on permutating requirement candidates.
+    // These permutations represent backtracking on previous decisions.
+    private final List<Candidates> m_importPermutations = new ArrayList<Candidates>();
+
+    public ResolverImpl(Logger logger)
+    {
+        m_logger = logger;
+    }
+
+    public Map<BundleRevision, List<ResolverWire>> resolve(ResolveContext rc)
+    {
+        Map<BundleRevision, List<ResolverWire>> wireMap =
+            new HashMap<BundleRevision, List<ResolverWire>>();
+        Map<BundleRevision, Packages> revisionPkgMap =
+            new HashMap<BundleRevision, Packages>();
+
+        Collection<BundleRevision> mandatoryRevisions = rc.getMandatoryRevisions();
+        Collection<BundleRevision> optionalRevisions = rc.getOptionalRevisions();
+        Collection<BundleRevision> ondemandFragments = (rc instanceof ResolveContextImpl)
+            ? ((ResolveContextImpl) rc).getOndemandRevisions() : Collections.EMPTY_LIST;
+        boolean retry;
+        do
+        {
+            retry = false;
+
+            try
+            {
+                // Create object to hold all candidates.
+                Candidates allCandidates = new Candidates();
+
+                // Populate mandatory revisions; since these are mandatory
+                // revisions, failure throws a resolve exception.
+                for (Iterator<BundleRevision> it = mandatoryRevisions.iterator();
+                    it.hasNext(); )
+                {
+                    BundleRevision br = it.next();
+                    if (Util.isFragment(br) || (br.getWiring() == null))
+                    {
+                        allCandidates.populate(rc, br, Candidates.MANDATORY);
+                    }
+                    else
+                    {
+                        it.remove();
+                    }
+                }
+
+                // Populate optional revisions; since these are optional
+                // revisions, failure does not throw a resolve exception.
+                for (BundleRevision br : optionalRevisions)
+                {
+                    boolean isFragment = Util.isFragment(br);
+                    if (isFragment || (br.getWiring() == null))
+                    {
+                        allCandidates.populate(rc, br, Candidates.OPTIONAL);
+                    }
+                }
+
+                // Populate ondemand fragments; since these are optional
+                // revisions, failure does not throw a resolve exception.
+                for (BundleRevision br : ondemandFragments)
+                {
+                    boolean isFragment = Util.isFragment(br);
+                    if (isFragment)
+                    {
+                        allCandidates.populate(rc, br, Candidates.ON_DEMAND);
+                    }
+                }
+
+                // Merge any fragments into hosts.
+                allCandidates.prepare(rc);
+
+                // Create a combined list of populated revisions; for
+                // optional revisions. We do not need to consider ondemand
+                // fragments, since they will only be pulled in if their
+                // host is already present.
+                Set<BundleRevision> allRevisions =
+                    new HashSet<BundleRevision>(mandatoryRevisions);
+                for (BundleRevision br : optionalRevisions)
+                {
+                    if (allCandidates.isPopulated(br))
+                    {
+                        allRevisions.add(br);
+                    }
+                }
+
+                // Record the initial candidate permutation.
+                m_usesPermutations.add(allCandidates);
+
+                ResolveException rethrow = null;
+
+                // If a populated revision is a fragment, then its host
+                // must ultimately be verified, so store its host requirement
+                // to use for package space calculation.
+                Map<BundleRevision, List<BundleRequirement>> hostReqs =
+                    new HashMap<BundleRevision, List<BundleRequirement>>();
+                for (BundleRevision br : allRevisions)
+                {
+                    if (Util.isFragment(br))
+                    {
+                        hostReqs.put(
+                            br,
+                            br.getDeclaredRequirements(BundleRevision.HOST_NAMESPACE));
+                    }
+                }
+
+                do
+                {
+                    rethrow = null;
+
+                    revisionPkgMap.clear();
+                    m_packageSourcesCache.clear();
+
+                    allCandidates = (m_usesPermutations.size() > 0)
+                        ? m_usesPermutations.remove(0)
+                        : m_importPermutations.remove(0);
+//allCandidates.dump();
+
+                    for (BundleRevision br : allRevisions)
+                    {
+                        BundleRevision target = br;
+
+                        // If we are resolving a fragment, then get its
+                        // host candidate and verify it instead.
+                        List<BundleRequirement> hostReq = hostReqs.get(br);
+                        if (hostReq != null)
+                        {
+                            target = allCandidates.getCandidates(hostReq.get(0))
+                                .iterator().next().getRevision();
+                        }
+
+                        calculatePackageSpaces(
+                            allCandidates.getWrappedHost(target), allCandidates, revisionPkgMap,
+                            new HashMap(), new HashSet());
+//System.out.println("+++ PACKAGE SPACES START +++");
+//dumpRevisionPkgMap(revisionPkgMap);
+//System.out.println("+++ PACKAGE SPACES END +++");
+
+                        try
+                        {
+                            checkPackageSpaceConsistency(
+                                rc, false, allCandidates.getWrappedHost(target),
+                                allCandidates, revisionPkgMap, new HashMap());
+                        }
+                        catch (ResolveException ex)
+                        {
+                            rethrow = ex;
+                        }
+                    }
+                }
+                while ((rethrow != null)
+                    && ((m_usesPermutations.size() > 0) || (m_importPermutations.size() > 0)));
+
+                // If there is a resolve exception, then determine if an
+                // optionally resolved revision is to blame (typically a fragment).
+                // If so, then remove the optionally resolved resolved and try
+                // again; otherwise, rethrow the resolve exception.
+                if (rethrow != null)
+                {
+                    BundleRevision faultyRevision =
+                        getDeclaringBundleRevision(rethrow.getRevision());
+                    if (rethrow.getRequirement() instanceof WrappedRequirement)
+                    {
+                        faultyRevision =
+                            ((WrappedRequirement) rethrow.getRequirement())
+                                .getOriginalRequirement().getRevision();
+                    }
+                    if (optionalRevisions.remove(faultyRevision))
+                    {
+                        retry = true;
+                    }
+                    else if (ondemandFragments.remove(faultyRevision))
+                    {
+                        retry = true;
+                    }
+                    else
+                    {
+                        throw rethrow;
+                    }
+                }
+                // If there is no exception to rethrow, then this was a clean
+                // resolve, so populate the wire map.
+                else
+                {
+                    for (BundleRevision br : allRevisions)
+                    {
+                        BundleRevision target = br;
+
+                        // If we are resolving a fragment, then we
+                        // actually want to populate its host's wires.
+                        List<BundleRequirement> hostReq = hostReqs.get(br);
+                        if (hostReq != null)
+                        {
+                            target = allCandidates.getCandidates(hostReq.get(0))
+                                .iterator().next().getRevision();
+                        }
+
+                        if (allCandidates.isPopulated(target))
+                        {
+                            wireMap =
+                                populateWireMap(
+                                    allCandidates.getWrappedHost(target),
+                                    revisionPkgMap, wireMap, allCandidates);
+                        }
+                    }
+                }
+            }
+            finally
+            {
+                // Always clear the state.
+                m_usesPermutations.clear();
+                m_importPermutations.clear();
+            }
+        }
+        while (retry);
+
+        return wireMap;
+    }
+
+    public Map<BundleRevision, List<ResolverWire>> resolve(
+        ResolveContext rc, BundleRevision revision, String pkgName)
+    {
+        // We can only create a dynamic import if the following
+        // conditions are met:
+        // 1. The specified revision is resolved.
+        // 2. The package in question is not already imported.
+        // 3. The package in question is not accessible via require-bundle.
+        // 4. The package in question is not exported by the revision.
+        // 5. The package in question matches a dynamic import of the revision.
+        // The following call checks all of these conditions and returns
+        // the associated dynamic import and matching capabilities.
+        Candidates allCandidates =
+            getDynamicImportCandidates(rc, revision, pkgName);
+        if (allCandidates != null)
+        {
+            Collection<BundleRevision> ondemandFragments = (rc instanceof ResolveContextImpl)
+                ? ((ResolveContextImpl) rc).getOndemandRevisions() : Collections.EMPTY_LIST;
+
+            Map<BundleRevision, List<ResolverWire>> wireMap =
+                new HashMap<BundleRevision, List<ResolverWire>>();
+            Map<BundleRevision, Packages> revisionPkgMap =
+                new HashMap<BundleRevision, Packages>();
+
+            boolean retry;
+            do
+            {
+                retry = false;
+
+                try
+                {
+                    // Try to populate optional fragments.
+                    for (BundleRevision br : ondemandFragments)
+                    {
+                        if (Util.isFragment(br))
+                        {
+                            allCandidates.populate(rc, br, Candidates.ON_DEMAND);
+                        }
+                    }
+
+                    // Merge any fragments into hosts.
+                    allCandidates.prepare(rc);
+
+                    // Record the initial candidate permutation.
+                    m_usesPermutations.add(allCandidates);
+
+                    ResolveException rethrow = null;
+
+                    do
+                    {
+                        rethrow = null;
+
+                        revisionPkgMap.clear();
+                        m_packageSourcesCache.clear();
+
+                        allCandidates = (m_usesPermutations.size() > 0)
+                            ? m_usesPermutations.remove(0)
+                            : m_importPermutations.remove(0);
+//allCandidates.dump();
+
+                        // For a dynamic import, the instigating revision
+                        // will never be a fragment since fragments never
+                        // execute code, so we don't need to check for
+                        // this case like we do for a normal resolve.
+
+                        calculatePackageSpaces(
+                            allCandidates.getWrappedHost(revision), allCandidates, revisionPkgMap,
+                            new HashMap(), new HashSet());
+//System.out.println("+++ PACKAGE SPACES START +++");
+//dumpRevisionPkgMap(revisionPkgMap);
+//System.out.println("+++ PACKAGE SPACES END +++");
+
+                        try
+                        {
+                            checkPackageSpaceConsistency(
+                                rc, false, allCandidates.getWrappedHost(revision),
+                                allCandidates, revisionPkgMap, new HashMap());
+                        }
+                        catch (ResolveException ex)
+                        {
+                            rethrow = ex;
+                        }
+                    }
+                    while ((rethrow != null)
+                        && ((m_usesPermutations.size() > 0) || (m_importPermutations.size() > 0)));
+
+                    // If there is a resolve exception, then determine if an
+                    // optionally resolved revision is to blame (typically a fragment).
+                    // If so, then remove the optionally resolved revision and try
+                    // again; otherwise, rethrow the resolve exception.
+                    if (rethrow != null)
+                    {
+                        BundleRevision faultyRevision =
+                            getDeclaringBundleRevision(rethrow.getRevision());
+                        if (rethrow.getRequirement() instanceof WrappedRequirement)
+                        {
+                            faultyRevision =
+                                ((WrappedRequirement) rethrow.getRequirement())
+                                    .getOriginalRequirement().getRevision();
+                        }
+                        if (ondemandFragments.remove(faultyRevision))
+                        {
+                            retry = true;
+                        }
+                        else
+                        {
+                            throw rethrow;
+                        }
+                    }
+                    // If there is no exception to rethrow, then this was a clean
+                    // resolve, so populate the wire map.
+                    else
+                    {
+                        wireMap = populateDynamicWireMap(
+                            revision, pkgName, revisionPkgMap, wireMap, allCandidates);
+                        return wireMap;
+                    }
+                }
+                finally
+                {
+                    // Always clear the state.
+                    m_usesPermutations.clear();
+                    m_importPermutations.clear();
+                }
+            }
+            while (retry);
+        }
+
+        return null;
+    }
+
+    private static Candidates getDynamicImportCandidates(
+        ResolveContext rc, BundleRevision revision, String pkgName)
+    {
+        // Unresolved revisions cannot dynamically import, nor can the default
+        // package be dynamically imported.
+        if ((revision.getWiring() == null) || pkgName.length() == 0)
+        {
+            return null;
+        }
+
+        // If the revision doesn't have dynamic imports, then just return
+        // immediately.
+        List<BundleRequirement> dynamics =
+            Util.getDynamicRequirements(revision.getWiring().getRequirements(null));
+        if ((dynamics == null) || dynamics.isEmpty())
+        {
+            return null;
+        }
+
+        // If the revision exports this package, then we cannot
+        // attempt to dynamically import it.
+        for (BundleCapability cap : revision.getWiring().getCapabilities(null))
+        {
+            if (cap.getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE)
+                && cap.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE).equals(pkgName))
+            {
+                return null;
+            }
+        }
+
+        // If this revision already imports or requires this package, then
+        // we cannot dynamically import it.
+        if (((BundleWiringImpl) revision.getWiring()).hasPackageSource(pkgName))
+        {
+            return null;
+        }
+
+        // Determine if any providers of the package exist.
+        Map<String, Object> attrs = Collections.singletonMap(
+            BundleRevision.PACKAGE_NAMESPACE, (Object) pkgName);
+        BundleRequirementImpl req = new BundleRequirementImpl(
+            revision,
+            BundleRevision.PACKAGE_NAMESPACE,
+            Collections.EMPTY_MAP,
+            attrs);
+        List<BundleCapability> candidates = rc.findProviders(req, false);
+
+        // Try to find a dynamic requirement that matches the capabilities.
+        BundleRequirementImpl dynReq = null;
+        for (int dynIdx = 0;
+            (candidates.size() > 0) && (dynReq == null) && (dynIdx < dynamics.size());
+            dynIdx++)
+        {
+            for (Iterator<BundleCapability> itCand = candidates.iterator();
+                (dynReq == null) && itCand.hasNext(); )
+            {
+                BundleCapability cap = itCand.next();
+                if (CapabilitySet.matches(
+                    (BundleCapabilityImpl) cap,
+                    ((BundleRequirementImpl) dynamics.get(dynIdx)).getFilter()))
+                {
+                    dynReq = (BundleRequirementImpl) dynamics.get(dynIdx);
+                }
+            }
+        }
+
+        // If we found a matching dynamic requirement, then filter out
+        // any candidates that do not match it.
+        if (dynReq != null)
+        {
+            for (Iterator<BundleCapability> itCand = candidates.iterator();
+                itCand.hasNext(); )
+            {
+                BundleCapability cap = itCand.next();
+                if (!CapabilitySet.matches(
+                    (BundleCapabilityImpl) cap, dynReq.getFilter()))
+                {
+                    itCand.remove();
+                }
+            }
+        }
+        else
+        {
+            candidates.clear();
+        }
+
+        Candidates allCandidates = null;
+
+        if (candidates.size() > 0)
+        {
+            allCandidates = new Candidates();
+            allCandidates.populateDynamic(rc, revision, dynReq, candidates);
+        }
+
+        return allCandidates;
+    }
+
+    private void calculatePackageSpaces(
+        BundleRevision revision,
+        Candidates allCandidates,
+        Map<BundleRevision, Packages> revisionPkgMap,
+        Map<BundleCapability, List<BundleRevision>> usesCycleMap,
+        Set<BundleRevision> cycle)
+    {
+        if (cycle.contains(revision))
+        {
+            return;
+        }
+        cycle.add(revision);
+
+        // Make sure package space hasn't already been calculated.
+        Packages revisionPkgs = revisionPkgMap.get(revision);
+        if (revisionPkgs != null)
+        {
+            if (revisionPkgs.m_isCalculated)
+            {
+                return;
+            }
+            else
+            {
+                revisionPkgs.m_isCalculated = true;
+            }
+        }
+
+        // Create parallel arrays for requirement and proposed candidate
+        // capability or actual capability if revision is resolved or not.
+        List<BundleRequirement> reqs = new ArrayList();
+        List<BundleCapability> caps = new ArrayList();
+        boolean isDynamicImporting = false;
+        if (revision.getWiring() != null)
+        {
+            // Use wires to get actual requirements and satisfying capabilities.
+            for (BundleWire wire : revision.getWiring().getRequiredWires(null))
+            {
+                // Wrap the requirement as a hosted requirement if it comes
+                // from a fragment, since we will need to know the host. We
+                // also need to wrap if the requirement is a dynamic import,
+                // since that requirement will be shared with any other
+                // matching dynamic imports.
+                BundleRequirement r = wire.getRequirement();
+                if (!r.getRevision().equals(wire.getRequirerWiring().getRevision())
+                    || ((r.getDirectives().get(Constants.RESOLUTION_DIRECTIVE) != null)
+                        && r.getDirectives().get(Constants.RESOLUTION_DIRECTIVE).equals("dynamic")))
+                {
+                    r = new WrappedRequirement(
+                        wire.getRequirerWiring().getRevision(),
+                        (BundleRequirementImpl) r);
+                }
+                // Wrap the capability as a hosted capability if it comes
+                // from a fragment, since we will need to know the host.
+                BundleCapability c = wire.getCapability();
+                if (!c.getRevision().equals(wire.getProviderWiring().getRevision()))
+                {
+                    c = new WrappedCapability(
+                        wire.getProviderWiring().getRevision(),
+                        (BundleCapabilityImpl) c);
+                }
+                reqs.add(r);
+                caps.add(c);
+            }
+
+            // Since the revision is resolved, it could be dynamically importing,
+            // so check to see if there are candidates for any of its dynamic
+            // imports.
+            for (BundleRequirement req
+                : Util.getDynamicRequirements(revision.getWiring().getRequirements(null)))
+            {
+                // Get the candidates for the current requirement.
+                List<BundleCapability> candCaps = allCandidates.getCandidates(req);
+                // Optional requirements may not have any candidates.
+                if (candCaps == null)
+                {
+                    continue;
+                }
+
+                // Grab first (i.e., highest priority) candidate.
+                BundleCapability cap = candCaps.get(0);
+                reqs.add(req);
+                caps.add(cap);
+                isDynamicImporting = true;
+                // Can only dynamically import one at a time, so break
+                // out of the loop after the first.
+                break;
+            }
+        }
+        else
+        {
+            for (BundleRequirement req : revision.getDeclaredRequirements(null))
+            {
+                String resolution = req.getDirectives().get(Constants.RESOLUTION_DIRECTIVE);
+                if ((resolution == null)
+                    || !resolution.equals(FelixConstants.RESOLUTION_DYNAMIC))
+                {
+                    // Get the candidates for the current requirement.
+                    List<BundleCapability> candCaps = allCandidates.getCandidates(req);
+                    // Optional requirements may not have any candidates.
+                    if (candCaps == null)
+                    {
+                        continue;
+                    }
+
+                    // Grab first (i.e., highest priority) candidate.
+                    BundleCapability cap = candCaps.get(0);
+                    reqs.add(req);
+                    caps.add(cap);
+                }
+            }
+        }
+
+        // First, add all exported packages to the target revision's package space.
+        calculateExportedPackages(revision, allCandidates, revisionPkgMap);
+        revisionPkgs = revisionPkgMap.get(revision);
+
+        // Second, add all imported packages to the target revision's package space.
+        for (int i = 0; i < reqs.size(); i++)
+        {
+            BundleRequirement req = reqs.get(i);
+            BundleCapability cap = caps.get(i);
+            calculateExportedPackages(cap.getRevision(), allCandidates, revisionPkgMap);
+            mergeCandidatePackages(
+                revision, req, cap, revisionPkgMap, allCandidates,
+                new HashMap<BundleRevision, List<BundleCapability>>());
+        }
+
+        // Third, have all candidates to calculate their package spaces.
+        for (int i = 0; i < caps.size(); i++)
+        {
+            calculatePackageSpaces(
+                caps.get(i).getRevision(), allCandidates, revisionPkgMap,
+                usesCycleMap, cycle);
+        }
+
+        // Fourth, if the target revision is unresolved or is dynamically importing,
+        // then add all the uses constraints implied by its imported and required
+        // packages to its package space.
+        // NOTE: We do not need to do this for resolved revisions because their
+        // package space is consistent by definition and these uses constraints
+        // are only needed to verify the consistency of a resolving revision. The
+        // only exception is if a resolved revision is dynamically importing, then
+        // we need to calculate its uses constraints again to make sure the new
+        // import is consistent with the existing package space.
+        if ((revision.getWiring() == null) || isDynamicImporting)
+        {
+            // Merge uses constraints from required capabilities.
+            for (int i = 0; i < reqs.size(); i++)
+            {
+                BundleRequirement req = reqs.get(i);
+                BundleCapability cap = caps.get(i);
+                // Ignore bundle/package requirements, since they are
+                // considered below.
+                if (!req.getNamespace().equals(BundleRevision.BUNDLE_NAMESPACE)
+                    && !req.getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE))
+                {
+                    List<BundleRequirement> blameReqs = new ArrayList();
+                    blameReqs.add(req);
+
+                    mergeUses(
+                        revision,
+                        revisionPkgs,
+                        cap,
+                        blameReqs,
+                        revisionPkgMap,
+                        allCandidates,
+                        usesCycleMap);
+                }
+            }
+            // Merge uses constraints from imported packages.
+            for (Entry<String, List<Blame>> entry : revisionPkgs.m_importedPkgs.entrySet())
+            {
+                for (Blame blame : entry.getValue())
+                {
+                    // Ignore revisions that import from themselves.
+                    if (!blame.m_cap.getRevision().equals(revision))
+                    {
+                        List<BundleRequirement> blameReqs = new ArrayList();
+                        blameReqs.add(blame.m_reqs.get(0));
+
+                        mergeUses(
+                            revision,
+                            revisionPkgs,
+                            blame.m_cap,
+                            blameReqs,
+                            revisionPkgMap,
+                            allCandidates,
+                            usesCycleMap);
+                    }
+                }
+            }
+            // Merge uses constraints from required bundles.
+            for (Entry<String, List<Blame>> entry : revisionPkgs.m_requiredPkgs.entrySet())
+            {
+                for (Blame blame : entry.getValue())
+                {
+                    List<BundleRequirement> blameReqs = new ArrayList();
+                    blameReqs.add(blame.m_reqs.get(0));
+
+                    mergeUses(
+                        revision,
+                        revisionPkgs,
+                        blame.m_cap,
+                        blameReqs,
+                        revisionPkgMap,
+                        allCandidates,
+                        usesCycleMap);
+                }
+            }
+        }
+    }
+
+    private void mergeCandidatePackages(
+        BundleRevision current, BundleRequirement currentReq, BundleCapability candCap,
+        Map<BundleRevision, Packages> revisionPkgMap,
+        Candidates allCandidates, Map<BundleRevision, List<BundleCapability>> cycles)
+    {
+        List<BundleCapability> cycleCaps = cycles.get(current);
+        if (cycleCaps == null)
+        {
+            cycleCaps = new ArrayList<BundleCapability>();
+            cycles.put(current, cycleCaps);
+        }
+        if (cycleCaps.contains(candCap))
+        {
+            return;
+        }
+        cycleCaps.add(candCap);
+
+        if (candCap.getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE))
+        {
+            mergeCandidatePackage(
+                current, false, currentReq, candCap, revisionPkgMap);
+        }
+        else if (candCap.getNamespace().equals(BundleRevision.BUNDLE_NAMESPACE))
+        {
+// TODO: FELIX3 - THIS NEXT LINE IS A HACK. IMPROVE HOW/WHEN WE CALCULATE EXPORTS.
+            calculateExportedPackages(
+                candCap.getRevision(), allCandidates, revisionPkgMap);
+
+            // Get the candidate's package space to determine which packages
+            // will be visible to the current revision.
+            Packages candPkgs = revisionPkgMap.get(candCap.getRevision());
+
+            // We have to merge all exported packages from the candidate,
+            // since the current revision requires it.
+            for (Entry<String, Blame> entry : candPkgs.m_exportedPkgs.entrySet())
+            {
+                mergeCandidatePackage(
+                    current,
+                    true,
+                    currentReq,
+                    entry.getValue().m_cap,
+                    revisionPkgMap);
+            }
+
+            // If the candidate requires any other bundles with reexport visibility,
+            // then we also need to merge their packages too.
+            if (candCap.getRevision().getWiring() != null)
+            {
+                for (BundleWire bw
+                    : candCap.getRevision().getWiring().getRequiredWires(null))
+                {
+                    if (bw.getRequirement().getNamespace()
+                        .equals(BundleRevision.BUNDLE_NAMESPACE))
+                    {
+                        String value = bw.getRequirement()
+                            .getDirectives().get(Constants.VISIBILITY_DIRECTIVE);
+                        if ((value != null)
+                            && value.equals(Constants.VISIBILITY_REEXPORT))
+                        {
+                            mergeCandidatePackages(
+                                current,
+                                currentReq,
+                                bw.getCapability(),
+                                revisionPkgMap,
+                                allCandidates,
+                                cycles);
+                        }
+                    }
+                }
+            }
+            else
+            {
+                for (BundleRequirement req
+                    : candCap.getRevision().getDeclaredRequirements(null))
+                {
+                    if (req.getNamespace().equals(BundleRevision.BUNDLE_NAMESPACE))
+                    {
+                        String value =
+                            req.getDirectives().get(Constants.VISIBILITY_DIRECTIVE);
+                        if ((value != null)
+                            && value.equals(Constants.VISIBILITY_REEXPORT)
+                            && (allCandidates.getCandidates(req) != null))
+                        {
+                            mergeCandidatePackages(
+                                current,
+                                currentReq,
+                                allCandidates.getCandidates(req).iterator().next(),
+                                revisionPkgMap,
+                                allCandidates,
+                                cycles);
+                        }
+                    }
+                }
+            }
+        }
+
+        cycles.remove(current);
+    }
+
+    private void mergeCandidatePackage(
+        BundleRevision current, boolean requires,
+        BundleRequirement currentReq, BundleCapability candCap,
+        Map<BundleRevision, Packages> revisionPkgMap)
+    {
+        if (candCap.getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE))
+        {
+            // Merge the candidate capability into the revision's package space
+            // for imported or required packages, appropriately.
+
+            String pkgName = (String)
+                candCap.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE);
+
+            List blameReqs = new ArrayList();
+            blameReqs.add(currentReq);
+
+            Packages currentPkgs = revisionPkgMap.get(current);
+
+            Map<String, List<Blame>> packages = (requires)
+                ? currentPkgs.m_requiredPkgs
+                : currentPkgs.m_importedPkgs;
+            List<Blame> blames = packages.get(pkgName);
+            if (blames == null)
+            {
+                blames = new ArrayList<Blame>();
+                packages.put(pkgName, blames);
+            }
+            blames.add(new Blame(candCap, blameReqs));
+
+//dumpRevisionPkgs(current, currentPkgs);
+        }
+    }
+
+    private void mergeUses(
+        BundleRevision current, Packages currentPkgs,
+        BundleCapability mergeCap, List<BundleRequirement> blameReqs,
+        Map<BundleRevision, Packages> revisionPkgMap,
+        Candidates allCandidates,
+        Map<BundleCapability, List<BundleRevision>> cycleMap)
+    {
+        // If there are no uses, then just return.
+        // If the candidate revision is the same as the current revision,
+        // then we don't need to verify and merge the uses constraints
+        // since this will happen as we build up the package space.
+        if (current.equals(mergeCap.getRevision()))
+        {
+            return;
+        }
+
+        // Check for cycles.
+        List<BundleRevision> list = cycleMap.get(mergeCap);
+        if ((list != null) && list.contains(current))
+        {
+            return;
+        }
+        list = (list == null) ? new ArrayList<BundleRevision>() : list;
+        list.add(current);
+        cycleMap.put(mergeCap, list);
+
+        for (BundleCapability candSourceCap : getPackageSources(mergeCap, revisionPkgMap))
+        {
+            for (String usedPkgName : ((BundleCapabilityImpl) candSourceCap).getUses())
+            {
+                Packages candSourcePkgs = revisionPkgMap.get(candSourceCap.getRevision());
+                List<Blame> candSourceBlames = null;
+                // Check to see if the used package is exported.
+                Blame candExportedBlame = candSourcePkgs.m_exportedPkgs.get(usedPkgName);
+                if (candExportedBlame != null)
+                {
+                    candSourceBlames = new ArrayList(1);
+                    candSourceBlames.add(candExportedBlame);
+                }
+                else
+                {
+                    // If the used package is not exported, check to see if it
+                    // is required.
+                    candSourceBlames = candSourcePkgs.m_requiredPkgs.get(usedPkgName);
+                    // Lastly, if the used package is not required, check to see if it
+                    // is imported.
+                    candSourceBlames = (candSourceBlames != null)
+                        ? candSourceBlames : candSourcePkgs.m_importedPkgs.get(usedPkgName);
+                }
+
+                // If the used package cannot be found, then just ignore it
+                // since it has no impact.
+                if (candSourceBlames == null)
+                {
+                    continue;
+                }
+
+                List<Blame> usedCaps = currentPkgs.m_usedPkgs.get(usedPkgName);
+                if (usedCaps == null)
+                {
+                    usedCaps = new ArrayList<Blame>();
+                    currentPkgs.m_usedPkgs.put(usedPkgName, usedCaps);
+                }
+                for (Blame blame : candSourceBlames)
+                {
+                    if (blame.m_reqs != null)
+                    {
+                        List<BundleRequirement> blameReqs2 = new ArrayList(blameReqs);
+                        blameReqs2.add(blame.m_reqs.get(blame.m_reqs.size() - 1));
+                        usedCaps.add(new Blame(blame.m_cap, blameReqs2));
+                        mergeUses(current, currentPkgs, blame.m_cap, blameReqs2,
+                            revisionPkgMap, allCandidates, cycleMap);
+                    }
+                    else
+                    {
+                        usedCaps.add(new Blame(blame.m_cap, blameReqs));
+                        mergeUses(current, currentPkgs, blame.m_cap, blameReqs,
+                            revisionPkgMap, allCandidates, cycleMap);
+                    }
+                }
+            }
+        }
+    }
+
+    private void checkPackageSpaceConsistency(
+        ResolveContext rc,
+        boolean isDynamicImporting,
+        BundleRevision revision,
+        Candidates allCandidates,
+        Map<BundleRevision, Packages> revisionPkgMap,
+        Map<BundleRevision, Object> resultCache)
+    {
+        if ((revision.getWiring() != null) && !isDynamicImporting)
+        {
+            return;
+        }
+        else if(resultCache.containsKey(revision))
+        {
+            return;
+        }
+
+        Packages pkgs = revisionPkgMap.get(revision);
+
+        ResolveException rethrow = null;
+        Candidates permutation = null;
+        Set<BundleRequirement> mutated = null;
+
+        // Check for conflicting imports from fragments.
+        for (Entry<String, List<Blame>> entry : pkgs.m_importedPkgs.entrySet())
+        {
+            if (entry.getValue().size() > 1)
+            {
+                Blame sourceBlame = null;
+                for (Blame blame : entry.getValue())
+                {
+                    if (sourceBlame == null)
+                    {
+                        sourceBlame = blame;
+                    }
+                    else if (!sourceBlame.m_cap.getRevision().equals(blame.m_cap.getRevision()))
+                    {
+                        // Try to permutate the conflicting requirement.
+                        permutate(allCandidates, blame.m_reqs.get(0), m_importPermutations);
+                        // Try to permutate the source requirement.
+                        permutate(allCandidates, sourceBlame.m_reqs.get(0), m_importPermutations);
+                        // Report conflict.
+                        ResolveException ex = new ResolveException(
+                            "Uses constraint violation. Unable to resolve bundle revision "
+                            + revision.getSymbolicName()
+                            + " [" + revision
+                            + "] because it is exposed to package '"
+                            + entry.getKey()
+                            + "' from bundle revisions "
+                            + sourceBlame.m_cap.getRevision().getSymbolicName()
+                            + " [" + sourceBlame.m_cap.getRevision()
+                            + "] and "
+                            + blame.m_cap.getRevision().getSymbolicName()
+                            + " [" + blame.m_cap.getRevision()
+                            + "] via two dependency chains.\n\nChain 1:\n"
+                            + toStringBlame(rc, allCandidates, sourceBlame)
+                            + "\n\nChain 2:\n"
+                            + toStringBlame(rc, allCandidates, blame),
+                            revision,
+                            blame.m_reqs.get(0));
+                        m_logger.log(
+                            Logger.LOG_DEBUG,
+                            "Candidate permutation failed due to a conflict with a "
+                            + "fragment import; will try another if possible.",
+                            ex);
+                        throw ex;
+                    }
+                }
+            }
+        }
+
+        // Check if there are any uses conflicts with exported packages.
+        for (Entry<String, Blame> entry : pkgs.m_exportedPkgs.entrySet())
+        {
+            String pkgName = entry.getKey();
+            Blame exportBlame = entry.getValue();
+            if (!pkgs.m_usedPkgs.containsKey(pkgName))
+            {
+                continue;
+            }
+            for (Blame usedBlame : pkgs.m_usedPkgs.get(pkgName))
+            {
+                if (!isCompatible(exportBlame.m_cap, usedBlame.m_cap, revisionPkgMap))
+                {
+                    // Create a candidate permutation that eliminates all candidates
+                    // that conflict with existing selected candidates.
+                    permutation = (permutation != null)
+                        ? permutation
+                        : allCandidates.copy();
+                    rethrow = (rethrow != null)
+                        ? rethrow
+                        : new ResolveException(
+                            "Uses constraint violation. Unable to resolve bundle revision "
+                            + revision.getSymbolicName()
+                            + " [" + revision
+                            + "] because it exports package '"
+                            + pkgName
+                            + "' and is also exposed to it from bundle revision "
+                            + usedBlame.m_cap.getRevision().getSymbolicName()
+                            + " [" + usedBlame.m_cap.getRevision()
+                            + "] via the following dependency chain:\n\n"
+                            + toStringBlame(rc, allCandidates, usedBlame),
+                            null,
+                            null);
+
+                    mutated = (mutated != null)
+                        ? mutated
+                        : new HashSet<BundleRequirement>();
+
+                    for (int reqIdx = usedBlame.m_reqs.size() - 1; reqIdx >= 0; reqIdx--)
+                    {
+                        BundleRequirement req = usedBlame.m_reqs.get(reqIdx);
+
+                        // If we've already permutated this requirement in another
+                        // uses constraint, don't permutate it again just continue
+                        // with the next uses constraint.
+                        if (mutated.contains(req))
+                        {
+                            break;
+                        }
+
+                        // See if we can permutate the candidates for blamed
+                        // requirement; there may be no candidates if the revision
+                        // associated with the requirement is already resolved.
+                        List<BundleCapability> candidates = permutation.getCandidates(req);
+                        if ((candidates != null) && (candidates.size() > 1))
+                        {
+                            mutated.add(req);
+                            // Remove the conflicting candidate.
+                            candidates.remove(0);
+                            // Continue with the next uses constraint.
+                            break;
+                        }
+                    }
+                }
+            }
+
+            if (rethrow != null)
+            {
+                if (mutated.size() > 0)
+                {
+                    m_usesPermutations.add(permutation);
+                }
+                m_logger.log(
+                    Logger.LOG_DEBUG,
+                    "Candidate permutation failed due to a conflict between "
+                    + "an export and import; will try another if possible.",
+                    rethrow);
+                throw rethrow;
+            }
+        }
+
+        // Check if there are any uses conflicts with imported packages.
+        for (Entry<String, List<Blame>> entry : pkgs.m_importedPkgs.entrySet())
+        {
+            for (Blame importBlame : entry.getValue())
+            {
+                String pkgName = entry.getKey();
+                if (!pkgs.m_usedPkgs.containsKey(pkgName))
+                {
+                    continue;
+                }
+                for (Blame usedBlame : pkgs.m_usedPkgs.get(pkgName))
+                {
+                    if (!isCompatible(importBlame.m_cap, usedBlame.m_cap, revisionPkgMap))
+                    {
+                        // Create a candidate permutation that eliminates any candidates
+                        // that conflict with existing selected candidates.
+                        permutation = (permutation != null)
+                            ? permutation
+                            : allCandidates.copy();
+                        rethrow = (rethrow != null)
+                            ? rethrow
+                            : new ResolveException(
+                                "Uses constraint violation. Unable to resolve bundle revision "
+                                + revision.getSymbolicName()
+                                + " [" + revision
+                                + "] because it is exposed to package '"
+                                + pkgName
+                                + "' from bundle revisions "
+                                + importBlame.m_cap.getRevision().getSymbolicName()
+                                + " [" + importBlame.m_cap.getRevision()
+                                + "] and "
+                                + usedBlame.m_cap.getRevision().getSymbolicName()
+                                + " [" + usedBlame.m_cap.getRevision()
+                                + "] via two dependency chains.\n\nChain 1:\n"
+                                + toStringBlame(rc, allCandidates, importBlame)
+                                + "\n\nChain 2:\n"
+                                + toStringBlame(rc, allCandidates, usedBlame),
+                                null,
+                                null);
+
+                        mutated = (mutated != null)
+                            ? mutated
+                            : new HashSet();
+
+                        for (int reqIdx = usedBlame.m_reqs.size() - 1; reqIdx >= 0; reqIdx--)
+                        {
+                            BundleRequirement req = usedBlame.m_reqs.get(reqIdx);
+
+                            // If we've already permutated this requirement in another
+                            // uses constraint, don't permutate it again just continue
+                            // with the next uses constraint.
+                            if (mutated.contains(req))
+                            {
+                                break;
+                            }
+
+                            // See if we can permutate the candidates for blamed
+                            // requirement; there may be no candidates if the revision
+                            // associated with the requirement is already resolved.
+                            List<BundleCapability> candidates = permutation.getCandidates(req);
+                            if ((candidates != null) && (candidates.size() > 1))
+                            {
+                                mutated.add(req);
+                                // Remove the conflicting candidate.
+                                candidates.remove(0);
+                                // Continue with the next uses constraint.
+                                break;
+                            }
+                        }
+                    }
+                }
+
+                // If there was a uses conflict, then we should add a uses
+                // permutation if we were able to permutate any candidates.
+                // Additionally, we should try to push an import permutation
+                // for the original import to force a backtracking on the
+                // original candidate decision if no viable candidate is found
+                // for the conflicting uses constraint.
+                if (rethrow != null)
+                {
+                    // Add uses permutation if we mutated any candidates.
+                    if (mutated.size() > 0)
+                    {
+                        m_usesPermutations.add(permutation);
+                    }
+
+                    // Try to permutate the candidate for the original
+                    // import requirement; only permutate it if we haven't
+                    // done so already.
+                    BundleRequirement req = importBlame.m_reqs.get(0);
+                    if (!mutated.contains(req))
+                    {
+                        // Since there may be lots of uses constraint violations
+                        // with existing import decisions, we may end up trying
+                        // to permutate the same import a lot of times, so we should
+                        // try to check if that the case and only permutate it once.
+                        permutateIfNeeded(allCandidates, req, m_importPermutations);
+                    }
+
+                    m_logger.log(
+                        Logger.LOG_DEBUG,
+                        "Candidate permutation failed due to a conflict between "
+                        + "imports; will try another if possible.",
+                        rethrow);
+                    throw rethrow;
+                }
+            }
+        }
+
+        resultCache.put(revision, Boolean.TRUE);
+
+        // Now check the consistency of all revisions on which the
+        // current revision depends. Keep track of the current number
+        // of permutations so we know if the lower level check was
+        // able to create a permutation or not in the case of failure.
+        int permCount = m_usesPermutations.size() + m_importPermutations.size();
+        for (Entry<String, List<Blame>> entry : pkgs.m_importedPkgs.entrySet())
+        {
+            for (Blame importBlame : entry.getValue())
+            {
+                if (!revision.equals(importBlame.m_cap.getRevision()))
+                {
+                    try
+                    {
+                        checkPackageSpaceConsistency(
+                            rc, false, importBlame.m_cap.getRevision(),
+                            allCandidates, revisionPkgMap, resultCache);
+                    }
+                    catch (ResolveException ex)
+                    {
+                        // If the lower level check didn't create any permutations,
+                        // then we should create an import permutation for the
+                        // requirement with the dependency on the failing revision
+                        // to backtrack on our current candidate selection.
+                        if (permCount == (m_usesPermutations.size() + m_importPermutations.size()))
+                        {
+                            BundleRequirement req = importBlame.m_reqs.get(0);
+                            permutate(allCandidates, req, m_importPermutations);
+                        }
+                        throw ex;
+                    }
+                }
+            }
+        }
+    }
+
+    private static void permutate(
+        Candidates allCandidates, BundleRequirement req, List<Candidates> permutations)
+    {
+        List<BundleCapability> candidates = allCandidates.getCandidates(req);
+        if (candidates.size() > 1)
+        {
+            Candidates perm = allCandidates.copy();
+            candidates = perm.getCandidates(req);
+            candidates.remove(0);
+            permutations.add(perm);
+        }
+    }
+
+    private static void permutateIfNeeded(
+        Candidates allCandidates, BundleRequirement req, List<Candidates> permutations)
+    {
+        List<BundleCapability> candidates = allCandidates.getCandidates(req);
+        if (candidates.size() > 1)
+        {
+            // Check existing permutations to make sure we haven't
+            // already permutated this requirement. This check for
+            // duplicate permutations is simplistic. It assumes if
+            // there is any permutation that contains a different
+            // initial candidate for the requirement in question,
+            // then it has already been permutated.
+            boolean permutated = false;
+            for (Candidates existingPerm : permutations)
+            {
+                List<BundleCapability> existingPermCands = existingPerm.getCandidates(req);
+                if (!existingPermCands.get(0).equals(candidates.get(0)))
+                {
+                    permutated = true;
+                }
+            }
+            // If we haven't already permutated the existing
+            // import, do so now.
+            if (!permutated)
+            {
+                permutate(allCandidates, req, permutations);
+            }
+        }
+    }
+
+    private static void calculateExportedPackages(
+        BundleRevision revision,
+        Candidates allCandidates,
+        Map<BundleRevision, Packages> revisionPkgMap)
+    {
+        Packages packages = revisionPkgMap.get(revision);
+        if (packages != null)
+        {
+            return;
+        }
+        packages = new Packages(revision);
+
+        // Get all exported packages.
+        List<BundleCapability> caps = (revision.getWiring() != null)
+            ? revision.getWiring().getCapabilities(null)
+            : revision.getDeclaredCapabilities(null);
+        Map<String, BundleCapability> exports =
+            new HashMap<String, BundleCapability>(caps.size());
+        for (BundleCapability cap : caps)
+        {
+            if (cap.getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE))
+            {
+                if (!cap.getRevision().equals(revision))
+                {
+                    cap = new WrappedCapability(revision, (BundleCapabilityImpl) cap);
+                }
+                exports.put(
+                    (String) cap.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE),
+                    cap);
+            }
+        }
+        // Remove substitutable exports that were imported.
+        // For resolved revisions BundleWiring.getCapabilities()
+        // already excludes imported substitutable exports, but
+        // for resolving revisions we must look in the candidate
+        // map to determine which exports are substitutable.
+        if (!exports.isEmpty())
+        {
+            if (revision.getWiring() == null)
+            {
+                for (BundleRequirement req : revision.getDeclaredRequirements(null))
+                {
+                    if (req.getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE))
+                    {
+                        List<BundleCapability> cands = allCandidates.getCandidates(req);
+                        if ((cands != null) && !cands.isEmpty())
+                        {
+                            String pkgName = (String) cands.get(0)
+                                .getAttributes().get(BundleRevision.PACKAGE_NAMESPACE);
+                            exports.remove(pkgName);
+                        }
+                    }
+                }
+            }
+
+            // Add all non-substituted exports to the revisions's package space.
+            for (Entry<String, BundleCapability> entry : exports.entrySet())
+            {
+                packages.m_exportedPkgs.put(
+                    entry.getKey(), new Blame(entry.getValue(), null));
+            }
+        }
+
+        revisionPkgMap.put(revision, packages);
+    }
+
+    private boolean isCompatible(
+        BundleCapability currentCap, BundleCapability candCap,
+        Map<BundleRevision, Packages> revisionPkgMap)
+    {
+        if ((currentCap != null) && (candCap != null))
+        {
+            if (currentCap.equals(candCap))
+            {
+                return true;
+            }
+
+            List<BundleCapability> currentSources =
+                getPackageSources(
+                    currentCap,
+                    revisionPkgMap);
+            List<BundleCapability> candSources =
+                getPackageSources(
+                    candCap,
+                    revisionPkgMap);
+
+            return currentSources.containsAll(candSources)
+                || candSources.containsAll(currentSources);
+        }
+        return true;
+    }
+
+    private Map<BundleCapability, List<BundleCapability>> m_packageSourcesCache
+        = new HashMap();
+
+    private List<BundleCapability> getPackageSources(
+        BundleCapability cap, Map<BundleRevision, Packages> revisionPkgMap)
+    {
+        if (cap.getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE))
+        {
+            List<BundleCapability> sources = m_packageSourcesCache.get(cap);
+            if (sources == null)
+            {
+                sources = getPackageSourcesInternal(
+                    cap, revisionPkgMap, new ArrayList(), new HashSet());
+                m_packageSourcesCache.put(cap, sources);
+            }
+            return sources;
+        }
+
+        if (!((BundleCapabilityImpl) cap).getUses().isEmpty())
+        {
+            List<BundleCapability> caps = new ArrayList<BundleCapability>(1);
+            caps.add(cap);
+            return caps;
+        }
+
+        return Collections.EMPTY_LIST;
+    }
+
+    private static List<BundleCapability> getPackageSourcesInternal(
+        BundleCapability cap, Map<BundleRevision, Packages> revisionPkgMap,
+        List<BundleCapability> sources, Set<BundleCapability> cycleMap)
+    {
+        if (cap.getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE))
+        {
+            if (cycleMap.contains(cap))
+            {
+                return sources;
+            }
+            cycleMap.add(cap);
+
+            // Get the package name associated with the capability.
+            String pkgName = cap.getAttributes()
+                .get(BundleRevision.PACKAGE_NAMESPACE).toString();
+
+            // Since a revision can export the same package more than once, get
+            // all package capabilities for the specified package name.
+            List<BundleCapability> caps = (cap.getRevision().getWiring() != null)
+                ? cap.getRevision().getWiring().getCapabilities(null)
+                : cap.getRevision().getDeclaredCapabilities(null);
+            for (BundleCapability sourceCap : caps)
+            {
+                if (sourceCap.getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE)
+                    && sourceCap.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE).equals(pkgName))
+                {
+                    // Since capabilities may come from fragments, we need to check
+                    // for that case and wrap them.
+                    if (!cap.getRevision().equals(sourceCap.getRevision()))
+                    {
+                        sources.add(
+                            new WrappedCapability(cap.getRevision(), (BundleCapabilityImpl) sourceCap));
+                    }
+                    else
+                    {
+                        sources.add(sourceCap);
+                    }
+                }
+            }
+
+            // Then get any addition sources for the package from required bundles.
+            Packages pkgs = revisionPkgMap.get(cap.getRevision());
+            List<Blame> required = pkgs.m_requiredPkgs.get(pkgName);
+            if (required != null)
+            {
+                for (Blame blame : required)
+                {
+                    getPackageSourcesInternal(blame.m_cap, revisionPkgMap, sources, cycleMap);
+                }
+            }
+        }
+
+        return sources;
+    }
+
+    private static BundleRevision getDeclaringBundleRevision(BundleRevision br)
+    {
+        if (br instanceof WrappedRevision)
+        {
+            return ((WrappedRevision) br).getHost();
+        }
+        return br;
+    }
+
+    private static BundleCapability getDeclaredCapability(BundleCapability c)
+    {
+        if (c instanceof HostedCapability)
+        {
+            return ((HostedCapability) c).getDeclaredCapability();
+        }
+        return c;
+    }
+
+    private static BundleRequirement getDeclaredRequirement(BundleRequirement r)
+    {
+        if (r instanceof WrappedRequirement)
+        {
+            return ((WrappedRequirement) r).getOriginalRequirement();
+        }
+        return r;
+    }
+
+    private static Map<BundleRevision, List<ResolverWire>> populateWireMap(
+        BundleRevision revision, Map<BundleRevision, Packages> revisionPkgMap,
+        Map<BundleRevision, List<ResolverWire>> wireMap,
+        Candidates allCandidates)
+    {
+        BundleRevision unwrappedRevision = getDeclaringBundleRevision(revision);
+        if ((unwrappedRevision.getWiring() == null)
+            && !wireMap.containsKey(unwrappedRevision))
+        {
+            wireMap.put(unwrappedRevision, (List<ResolverWire>) Collections.EMPTY_LIST);
+
+            List<ResolverWire> packageWires = new ArrayList<ResolverWire>();
+            List<ResolverWire> bundleWires = new ArrayList<ResolverWire>();
+            List<ResolverWire> capabilityWires = new ArrayList<ResolverWire>();
+
+            for (BundleRequirement req : revision.getDeclaredRequirements(null))
+            {
+                List<BundleCapability> cands = allCandidates.getCandidates(req);
+                if ((cands != null) && (cands.size() > 0))
+                {
+                    BundleCapability cand = cands.get(0);
+                    // Ignore revisions that import themselves.
+                    if (!revision.equals(cand.getRevision()))
+                    {
+                        if (cand.getRevision().getWiring() == null)
+                        {
+                            populateWireMap(cand.getRevision(),
+                                revisionPkgMap, wireMap, allCandidates);
+                        }
+                        Packages candPkgs = revisionPkgMap.get(cand.getRevision());
+                        ResolverWire wire = new ResolverWireImpl(
+                            unwrappedRevision,
+                            getDeclaredRequirement(req),
+                            getDeclaringBundleRevision(cand.getRevision()),
+                            getDeclaredCapability(cand));
+                        if (req.getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE))
+                        {
+                            packageWires.add(wire);
+                        }
+                        else if (req.getNamespace().equals(BundleRevision.BUNDLE_NAMESPACE))
+                        {
+                            bundleWires.add(wire);
+                        }
+                        else
+                        {
+                            capabilityWires.add(wire);
+                        }
+                    }
+                }
+            }
+
+            // Combine package wires with require wires last.
+            packageWires.addAll(bundleWires);
+            packageWires.addAll(capabilityWires);
+            wireMap.put(unwrappedRevision, packageWires);
+
+            // Add host wire for any fragments.
+            if (revision instanceof WrappedRevision)
+            {
+                List<BundleRevision> fragments = ((WrappedRevision) revision).getFragments();
+                for (BundleRevision fragment : fragments)
+                {
+                    List<ResolverWire> hostWires = wireMap.get(fragment);
+                    if (hostWires == null)
+                    {
+                        hostWires = new ArrayList<ResolverWire>();
+                        wireMap.put(fragment, hostWires);
+                    }
+                    hostWires.add(
+                        new ResolverWireImpl(
+                            getDeclaringBundleRevision(fragment),
+                            fragment.getDeclaredRequirements(
+                                BundleRevision.HOST_NAMESPACE).get(0),
+                            unwrappedRevision,
+                            unwrappedRevision.getDeclaredCapabilities(
+                                BundleRevision.HOST_NAMESPACE).get(0)));
+                }
+            }
+        }
+
+        return wireMap;
+    }
+
+    private static Map<BundleRevision, List<ResolverWire>> populateDynamicWireMap(
+        BundleRevision revision, String pkgName, Map<BundleRevision, Packages> revisionPkgMap,
+        Map<BundleRevision, List<ResolverWire>> wireMap, Candidates allCandidates)
+    {
+        wireMap.put(revision, (List<ResolverWire>) Collections.EMPTY_LIST);
+
+        List<ResolverWire> packageWires = new ArrayList<ResolverWire>();
+
+        BundleRequirement dynReq = null;
+        BundleCapability dynCand = null;
+        for (BundleRequirement req
+            : Util.getDynamicRequirements(revision.getWiring().getRequirements(null)))
+        {
+            // Get the candidates for the current dynamic requirement.
+            List<BundleCapability> candCaps = allCandidates.getCandidates(req);
+            // Optional requirements may not have any candidates.
+            if ((candCaps == null) || candCaps.isEmpty())
+            {
+                continue;
+            }
+
+            // Record the dynamic requirement.
+            dynReq = req;
+            dynCand = candCaps.get(0);
+
+            // Can only dynamically import one at a time, so break
+            // out of the loop after the first.
+            break;
+        }
+
+        if (dynReq != null)
+        {
+            if (dynCand.getRevision().getWiring() == null)
+            {
+                populateWireMap(dynCand.getRevision(), revisionPkgMap, wireMap,
+                    allCandidates);
+            }
+
+            packageWires.add(
+                new ResolverWireImpl(
+                    revision,
+                    dynReq,
+                    getDeclaringBundleRevision(dynCand.getRevision()),
+                    getDeclaredCapability(dynCand)));
+        }
+
+        wireMap.put(revision, packageWires);
+
+        return wireMap;
+    }
+
+    private static void dumpRevisionPkgMap(Map<BundleRevision, Packages> revisionPkgMap)
+    {
+        System.out.println("+++BUNDLE REVISION PKG MAP+++");
+        for (Entry<BundleRevision, Packages> entry : revisionPkgMap.entrySet())
+        {
+            dumpRevisionPkgs(entry.getKey(), entry.getValue());
+        }
+    }
+
+    private static void dumpRevisionPkgs(BundleRevision revision, Packages packages)
+    {
+        System.out.println(revision
+            + " (" + ((revision.getWiring() != null) ? "RESOLVED)" : "UNRESOLVED)"));
+        System.out.println("  EXPORTED");
+        for (Entry<String, Blame> entry : packages.m_exportedPkgs.entrySet())
+        {
+            System.out.println("    " + entry.getKey() + " - " + entry.getValue());
+        }
+        System.out.println("  IMPORTED");
+        for (Entry<String, List<Blame>> entry : packages.m_importedPkgs.entrySet())
+        {
+            System.out.println("    " + entry.getKey() + " - " + entry.getValue());
+        }
+        System.out.println("  REQUIRED");
+        for (Entry<String, List<Blame>> entry : packages.m_requiredPkgs.entrySet())
+        {
+            System.out.println("    " + entry.getKey() + " - " + entry.getValue());
+        }
+        System.out.println("  USED");
+        for (Entry<String, List<Blame>> entry : packages.m_usedPkgs.entrySet())
+        {
+            System.out.println("    " + entry.getKey() + " - " + entry.getValue());
+        }
+    }
+
+    private static String toStringBlame(
+        ResolveContext rc, Candidates allCandidates, Blame blame)
+    {
+        StringBuffer sb = new StringBuffer();
+        if ((blame.m_reqs != null) && !blame.m_reqs.isEmpty())
+        {
+            for (int i = 0; i < blame.m_reqs.size(); i++)
+            {
+                BundleRequirement req = blame.m_reqs.get(i);
+                sb.append("  ");
+                sb.append(req.getRevision().getSymbolicName());
+                sb.append(" [");
+                sb.append(req.getRevision().toString());
+                sb.append("]\n");
+                if (req.getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE))
+                {
+                    sb.append("    import: ");
+                }
+                else
+                {
+                    sb.append("    require: ");
+                }
+                sb.append(((BundleRequirementImpl) req).getFilter().toString());
+                sb.append("\n     |");
+                if (req.getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE))
+                {
+                    sb.append("\n    export: ");
+                }
+                else
+                {
+                    sb.append("\n    provide: ");
+                }
+                if ((i + 1) < blame.m_reqs.size())
+                {
+                    BundleCapability cap = getSatisfyingCapability(
+                        rc,
+                        allCandidates,
+                        blame.m_reqs.get(i));
+                    if (cap.getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE))
+                    {
+                        sb.append(BundleRevision.PACKAGE_NAMESPACE);
+                        sb.append("=");
+                        sb.append(cap.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE).toString());
+                        BundleCapability usedCap =
+                            getSatisfyingCapability(
+                                rc,
+                                allCandidates,
+                                blame.m_reqs.get(i + 1));
+                        sb.append("; uses:=");
+                        sb.append(usedCap.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE));
+                    }
+                    else
+                    {
+                        sb.append(cap);
+                    }
+                    sb.append("\n");
+                }
+                else
+                {
+                    BundleCapability export = getSatisfyingCapability(
+                        rc,
+                        allCandidates,
+                        blame.m_reqs.get(i));
+                    sb.append(export.getNamespace());
+                    sb.append("=");
+                    sb.append(export.getAttributes().get(export.getNamespace()).toString());
+                    if (export.getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE)
+                        && !export.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE)
+                            .equals(blame.m_cap.getAttributes().get(
+                                BundleRevision.PACKAGE_NAMESPACE)))
+                    {
+                        sb.append("; uses:=");
+                        sb.append(blame.m_cap.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE));
+                        sb.append("\n    export: ");
+                        sb.append(BundleRevision.PACKAGE_NAMESPACE);
+                        sb.append("=");
+                        sb.append(blame.m_cap.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE).toString());
+                    }
+                    sb.append("\n  ");
+                    sb.append(blame.m_cap.getRevision().getSymbolicName());
+                    sb.append(" [");
+                    sb.append(blame.m_cap.getRevision().toString());
+                    sb.append("]");
+                }
+            }
+        }
+        else
+        {
+            sb.append(blame.m_cap.getRevision().toString());
+        }
+        return sb.toString();
+    }
+
+    private static BundleCapability getSatisfyingCapability(
+        ResolveContext rc, Candidates allCandidates, BundleRequirement req)
+    {
+        BundleCapability cap = null;
+
+        // If the requiring revision is not resolved, then check in the
+        // candidate map for its matching candidate.
+        List<BundleCapability> cands = allCandidates.getCandidates(req);
+        if (cands != null)
+        {
+            cap = cands.get(0);
+        }
+        // Otherwise, if the requiring revision is resolved then check
+        // in its wires for the capability satisfying the requirement.
+        else if (rc.getWirings().containsKey(req.getRevision()))
+        {
+            List<BundleWire> wires = rc.getWirings().get(req.getRevision()).getRequiredWires(null);
+            req = getDeclaredRequirement(req);
+            for (BundleWire w : wires)
+            {
+                if (w.getRequirement().equals(req))
+                {
+// TODO: RESOLVER - This is not 100% correct, since requirements for
+//       dynamic imports with wildcards will reside on many wires and
+//       this code only finds the first one, not necessarily the correct
+//       one. This is only used for the diagnostic message, but it still
+//       could confuse the user.
+                    cap = w.getCapability();
+                    break;
+                }
+            }
+        }
+
+        return cap;
+    }
+
+    private static class Packages
+    {
+        private final BundleRevision m_revision;
+        public final Map<String, Blame> m_exportedPkgs = new HashMap();
+        public final Map<String, List<Blame>> m_importedPkgs = new HashMap();
+        public final Map<String, List<Blame>> m_requiredPkgs = new HashMap();
+        public final Map<String, List<Blame>> m_usedPkgs = new HashMap();
+        public boolean m_isCalculated = false;
+
+        public Packages(BundleRevision revision)
+        {
+            m_revision = revision;
+        }
+    }
+
+    private static class Blame
+    {
+        public final BundleCapability m_cap;
+        public final List<BundleRequirement> m_reqs;
+
+        public Blame(BundleCapability cap, List<BundleRequirement> reqs)
+        {
+            m_cap = cap;
+            m_reqs = reqs;
+        }
+
+        @Override
+        public String toString()
+        {
+            return m_cap.getRevision()
+                + "." + m_cap.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE)
+                + (((m_reqs == null) || m_reqs.isEmpty())
+                    ? " NO BLAME"
+                    : " BLAMED ON " + m_reqs);
+        }
+
+        @Override
+        public boolean equals(Object o)
+        {
+            return (o instanceof Blame) && m_reqs.equals(((Blame) o).m_reqs)
+                && m_cap.equals(((Blame) o).m_cap);
+        }
+    }
+}
diff --git a/src/main/java/org/apache/felix/framework/resolver/ResolverWire.java b/src/main/java/org/apache/felix/framework/resolver/ResolverWire.java
new file mode 100644
index 0000000..69162b9
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/resolver/ResolverWire.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.resolver;
+
+import org.osgi.framework.wiring.BundleCapability;
+import org.osgi.framework.wiring.BundleRequirement;
+import org.osgi.framework.wiring.BundleRevision;
+
+public interface ResolverWire
+{
+    /**
+     * Returns the importing bundle revision.
+     * @return The importing bundle revision.
+    **/
+    public BundleRevision getRequirer();
+    /**
+     * Returns the associated requirement from the importing bundle revision
+     * that resulted in the creation of this wire.
+     * @return
+    **/
+    public BundleRequirement getRequirement();
+    /**
+     * Returns the exporting bundle revision.
+     * @return The exporting bundle revision.
+    **/
+    public BundleRevision getProvider();
+    /**
+     * Returns the associated capability from the exporting bundle revision
+     * that satisfies the requirement of the importing bundle revision.
+     * @return
+    **/
+    public BundleCapability getCapability();
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/felix/framework/resolver/ResolverWireImpl.java b/src/main/java/org/apache/felix/framework/resolver/ResolverWireImpl.java
new file mode 100644
index 0000000..4f8420f
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/resolver/ResolverWireImpl.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.resolver;
+
+import org.osgi.framework.wiring.BundleCapability;
+import org.osgi.framework.wiring.BundleRequirement;
+import org.osgi.framework.wiring.BundleRevision;
+
+class ResolverWireImpl implements ResolverWire
+{
+    private final BundleRevision m_requirer;
+    private final BundleRequirement m_req;
+    private final BundleRevision m_provider;
+    private final BundleCapability m_cap;
+
+    public ResolverWireImpl(
+        BundleRevision requirer, BundleRequirement req,
+        BundleRevision provider, BundleCapability cap)
+    {
+        m_requirer = requirer;
+        m_req = req;
+        m_provider = provider;
+        m_cap = cap;
+    }
+
+    public BundleRevision getRequirer()
+    {
+        return m_requirer;
+    }
+
+    public BundleRequirement getRequirement()
+    {
+        return m_req;
+    }
+
+    public BundleRevision getProvider()
+    {
+        return m_provider;
+    }
+
+    public BundleCapability getCapability()
+    {
+        return m_cap;
+    }
+
+    public String toString()
+    {
+        return m_req
+            + " -> "
+            + "[" + m_provider + "]";
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/felix/framework/resolver/ResourceNotFoundException.java b/src/main/java/org/apache/felix/framework/resolver/ResourceNotFoundException.java
new file mode 100644
index 0000000..b1903fb
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/resolver/ResourceNotFoundException.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.resolver;
+
+public class ResourceNotFoundException extends Exception
+{
+    public ResourceNotFoundException(String msg)
+    {
+        super(msg);
+    }
+
+    public ResourceNotFoundException(String msg, Throwable th)
+    {
+        super(msg, th);
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/felix/framework/resolver/ShadowList.java b/src/main/java/org/apache/felix/framework/resolver/ShadowList.java
new file mode 100644
index 0000000..e64b559
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/resolver/ShadowList.java
@@ -0,0 +1,157 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.resolver;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+
+public class ShadowList<T> implements List<T>
+{
+    private final List<T> m_original;
+    private final List<T> m_shadow;
+
+    public ShadowList(List<T> original)
+    {
+        m_original = original;
+        m_shadow = new ArrayList<T>(original);
+    }
+
+    public List<T> getOriginal()
+    {
+        return m_original;
+    }
+
+    public int size()
+    {
+        return m_shadow.size();
+    }
+
+    public boolean isEmpty()
+    {
+        return m_shadow.isEmpty();
+    }
+
+    public boolean contains(Object o)
+    {
+        return m_shadow.contains(o);
+    }
+
+    public Iterator<T> iterator()
+    {
+        return m_shadow.iterator();
+    }
+
+    public Object[] toArray()
+    {
+        return m_shadow.toArray();
+    }
+
+    public <T> T[] toArray(T[] ts)
+    {
+        return m_shadow.toArray(ts);
+    }
+
+    public boolean add(T e)
+    {
+        return m_shadow.add(e);
+    }
+
+    public boolean remove(Object o)
+    {
+        return m_shadow.remove(o);
+    }
+
+    public boolean containsAll(Collection<?> clctn)
+    {
+        return m_shadow.containsAll(clctn);
+    }
+
+    public boolean addAll(Collection<? extends T> clctn)
+    {
+        return m_shadow.addAll(clctn);
+    }
+
+    public boolean addAll(int i, Collection<? extends T> clctn)
+    {
+        return m_shadow.addAll(i, clctn);
+    }
+
+    public boolean removeAll(Collection<?> clctn)
+    {
+        return m_shadow.removeAll(clctn);
+    }
+
+    public boolean retainAll(Collection<?> clctn)
+    {
+        return m_shadow.retainAll(clctn);
+    }
+
+    public void clear()
+    {
+        m_shadow.clear();
+    }
+
+    public T get(int i)
+    {
+        return m_shadow.get(i);
+    }
+
+    public T set(int i, T e)
+    {
+        return m_shadow.set(i, e);
+    }
+
+    public void add(int i, T e)
+    {
+        m_shadow.add(i, e);
+    }
+
+    public T remove(int i)
+    {
+        return m_shadow.remove(i);
+    }
+
+    public int indexOf(Object o)
+    {
+        return m_shadow.indexOf(o);
+    }
+
+    public int lastIndexOf(Object o)
+    {
+        return m_shadow.lastIndexOf(o);
+    }
+
+    public ListIterator<T> listIterator()
+    {
+        return m_shadow.listIterator();
+    }
+
+    public ListIterator<T> listIterator(int i)
+    {
+        return m_shadow.listIterator(i);
+    }
+
+    public List<T> subList(int i, int i1)
+    {
+        return m_shadow.subList(i, i1);
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/felix/framework/resolver/SimpleHostedCapability.java b/src/main/java/org/apache/felix/framework/resolver/SimpleHostedCapability.java
new file mode 100644
index 0000000..89b2984
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/resolver/SimpleHostedCapability.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.resolver;
+
+import java.util.Map;
+import org.osgi.framework.wiring.BundleCapability;
+import org.osgi.framework.wiring.BundleRevision;
+
+class SimpleHostedCapability implements HostedCapability
+{
+    private final BundleRevision m_host;
+    private final BundleCapability m_cap;
+
+    SimpleHostedCapability(BundleRevision host, BundleCapability cap)
+    {
+        m_host = host;
+        m_cap = cap;
+    }
+
+    public BundleRevision getResource()
+    {
+        return m_host;
+    }
+
+    public BundleRevision getRevision()
+    {
+        return m_host;
+    }
+
+    public BundleCapability getDeclaredCapability()
+    {
+        return m_cap;
+    }
+
+    public String getNamespace()
+    {
+        return m_cap.getNamespace();
+    }
+
+    public Map<String, String> getDirectives()
+    {
+        return m_cap.getDirectives();
+    }
+
+    public Map<String, Object> getAttributes()
+    {
+        return m_cap.getAttributes();
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/felix/framework/resolver/WrappedCapability.java b/src/main/java/org/apache/felix/framework/resolver/WrappedCapability.java
new file mode 100644
index 0000000..ee159ff
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/resolver/WrappedCapability.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) OSGi Alliance (2012). All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.framework.resolver;
+
+import java.util.List;
+import java.util.Map;
+import org.apache.felix.framework.wiring.BundleCapabilityImpl;
+import org.osgi.framework.wiring.BundleCapability;
+import org.osgi.framework.wiring.BundleRevision;
+import org.osgi.resource.Resource;
+
+public class WrappedCapability extends BundleCapabilityImpl implements HostedCapability
+{
+    private final BundleRevision m_host;
+    private final BundleCapabilityImpl m_cap;
+
+    public WrappedCapability(BundleRevision host, BundleCapabilityImpl cap)
+    {
+        super(host, cap.getNamespace(), cap.getDirectives(), cap.getAttributes());
+        m_host = host;
+        m_cap = cap;
+    }
+
+    @Override
+    public boolean equals(Object obj)
+    {
+        if (obj == null)
+        {
+            return false;
+        }
+        if (getClass() != obj.getClass())
+        {
+            return false;
+        }
+        final WrappedCapability other = (WrappedCapability) obj;
+        if (m_host != other.m_host && (m_host == null || !m_host.equals(other.m_host)))
+        {
+            return false;
+        }
+        if (m_cap != other.m_cap && (m_cap == null || !m_cap.equals(other.m_cap)))
+        {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public int hashCode()
+    {
+        int hash = 7;
+        hash = 37 * hash + (m_host != null ? m_host.hashCode() : 0);
+        hash = 37 * hash + (m_cap != null ? m_cap.hashCode() : 0);
+        return hash;
+    }
+
+    public BundleCapability getDeclaredCapability()
+    {
+        return m_cap;
+    }
+
+    @Override
+    public BundleRevision getResource()
+    {
+        return m_host;
+    }
+
+    @Override
+    public BundleRevision getRevision()
+    {
+        return m_host;
+    }
+
+    @Override
+    public String getNamespace()
+    {
+        return m_cap.getNamespace();
+    }
+
+    @Override
+    public Map<String, String> getDirectives()
+    {
+        return m_cap.getDirectives();
+    }
+
+    @Override
+    public Map<String, Object> getAttributes()
+    {
+        return m_cap.getAttributes();
+    }
+
+    @Override
+    public List<String> getUses()
+    {
+        return m_cap.getUses();
+    }
+
+    @Override
+    public String toString()
+    {
+        if (m_host == null)
+        {
+            return getAttributes().toString();
+        }
+        if (getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE))
+        {
+            return "[" + m_host + "] "
+                + getNamespace()
+                + "; "
+                + getAttributes().get(BundleRevision.PACKAGE_NAMESPACE);
+        }
+        return "[" + m_host + "] " + getNamespace() + "; " + getAttributes();
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/felix/framework/resolver/WrappedRequirement.java b/src/main/java/org/apache/felix/framework/resolver/WrappedRequirement.java
new file mode 100644
index 0000000..c0d15e0
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/resolver/WrappedRequirement.java
@@ -0,0 +1,116 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.resolver;
+
+import java.util.Map;
+import org.apache.felix.framework.capabilityset.SimpleFilter;
+import org.apache.felix.framework.wiring.BundleRequirementImpl;
+import org.osgi.framework.wiring.BundleRevision;
+
+public class WrappedRequirement extends BundleRequirementImpl
+{
+    private final BundleRevision m_host;
+    private final BundleRequirementImpl m_req;
+
+    public WrappedRequirement(BundleRevision host, BundleRequirementImpl req)
+    {
+        super(host, req.getNamespace(), req.getDirectives(), req.getAttributes());
+        m_host = host;
+        m_req = req;
+    }
+
+    @Override
+    public boolean equals(Object obj)
+    {
+        if (obj == null)
+        {
+            return false;
+        }
+        if (getClass() != obj.getClass())
+        {
+            return false;
+        }
+        final WrappedRequirement other = (WrappedRequirement) obj;
+        if (m_host != other.m_host && (m_host == null || !m_host.equals(other.m_host)))
+        {
+            return false;
+        }
+        if (m_req != other.m_req && (m_req == null || !m_req.equals(other.m_req)))
+        {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public int hashCode()
+    {
+        int hash = 7;
+        hash = 37 * hash + (m_host != null ? m_host.hashCode() : 0);
+        hash = 37 * hash + (m_req != null ? m_req.hashCode() : 0);
+        return hash;
+    }
+
+    public BundleRequirementImpl getOriginalRequirement()
+    {
+        return m_req;
+    }
+
+    @Override
+    public BundleRevision getRevision()
+    {
+        return m_host;
+    }
+
+    @Override
+    public String getNamespace()
+    {
+        return m_req.getNamespace();
+    }
+
+    @Override
+    public SimpleFilter getFilter()
+    {
+        return m_req.getFilter();
+    }
+
+    @Override
+    public boolean isOptional()
+    {
+        return m_req.isOptional();
+    }
+
+    @Override
+    public Map<String, String> getDirectives()
+    {
+        return m_req.getDirectives();
+    }
+
+    @Override
+    public Map<String, Object> getAttributes()
+    {
+        return m_req.getAttributes();
+    }
+
+    @Override
+    public String toString()
+    {
+        return "[" + m_host + "] " + getNamespace() + "; " + getFilter().toString();
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/felix/framework/resolver/WrappedRevision.java b/src/main/java/org/apache/felix/framework/resolver/WrappedRevision.java
new file mode 100644
index 0000000..fa4ab5d
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/resolver/WrappedRevision.java
@@ -0,0 +1,169 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.resolver;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.felix.framework.util.ImmutableList;
+import org.apache.felix.framework.wiring.BundleCapabilityImpl;
+import org.apache.felix.framework.wiring.BundleRequirementImpl;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.Version;
+import org.osgi.framework.wiring.BundleCapability;
+import org.osgi.framework.wiring.BundleRequirement;
+import org.osgi.framework.wiring.BundleRevision;
+import org.osgi.framework.wiring.BundleWiring;
+import org.osgi.resource.Capability;
+import org.osgi.resource.Requirement;
+
+class WrappedRevision implements BundleRevision
+{
+    private final BundleRevision m_host;
+    private final List<BundleRevision> m_fragments;
+    private List<BundleCapability> m_cachedCapabilities = null;
+    private List<BundleRequirement> m_cachedRequirements = null;
+
+    public WrappedRevision(BundleRevision host, List<BundleRevision> fragments)
+    {
+        m_host = host;
+        m_fragments = fragments;
+    }
+
+    public BundleRevision getHost()
+    {
+        return m_host;
+    }
+
+    public List<BundleRevision> getFragments()
+    {
+        return m_fragments;
+    }
+
+    public String getSymbolicName()
+    {
+        return m_host.getSymbolicName();
+    }
+
+    public Version getVersion()
+    {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+    public List<Capability> getCapabilities(String namespace)
+    {
+        return asCapabilityList(getDeclaredCapabilities(namespace));
+    }
+
+    private static List<Capability> asCapabilityList(List caps)
+    {
+        return (List<Capability>) caps;
+    }
+
+    public List<BundleCapability> getDeclaredCapabilities(String namespace)
+    {
+        if (m_cachedCapabilities == null)
+        {
+            List<BundleCapability> caps = new ArrayList<BundleCapability>();
+
+            // Wrap host capabilities.
+            for (BundleCapability cap : m_host.getDeclaredCapabilities(null))
+            {
+                caps.add(new WrappedCapability(this, (BundleCapabilityImpl) cap));
+            }
+
+            // Wrap fragment capabilities.
+            if (m_fragments != null)
+            {
+                for (BundleRevision fragment : m_fragments)
+                {
+                    for (BundleCapability cap : fragment.getDeclaredCapabilities(null))
+                    {
+// TODO: OSGi R4.4 - OSGi R4.4 may introduce an identity capability, if so
+//       that will need to be excluded from here.
+                        caps.add(new WrappedCapability(this, (BundleCapabilityImpl) cap));
+                    }
+                }
+            }
+            m_cachedCapabilities = ImmutableList.newInstance(caps);
+        }
+        return m_cachedCapabilities;
+    }
+
+    public List<Requirement> getRequirements(String namespace)
+    {
+        return asRequirementList(getDeclaredRequirements(namespace));
+    }
+
+    private static List<Requirement> asRequirementList(List reqs)
+    {
+        return (List<Requirement>) reqs;
+    }
+
+    public List<BundleRequirement> getDeclaredRequirements(String namespace)
+    {
+        if (m_cachedRequirements == null)
+        {
+            List<BundleRequirement> reqs = new ArrayList<BundleRequirement>();
+
+            // Wrap host requirements.
+            for (BundleRequirement req : m_host.getDeclaredRequirements(null))
+            {
+                reqs.add(new WrappedRequirement(this, (BundleRequirementImpl) req));
+            }
+
+            // Wrap fragment requirements.
+            if (m_fragments != null)
+            {
+                for (BundleRevision fragment : m_fragments)
+                {
+                    for (BundleRequirement req : fragment.getDeclaredRequirements(null))
+                    {
+                        if (!req.getNamespace().equals(BundleRevision.HOST_NAMESPACE))
+                        {
+                            reqs.add(new WrappedRequirement(this, (BundleRequirementImpl) req));
+                        }
+                    }
+                }
+            }
+            m_cachedRequirements = ImmutableList.newInstance(reqs);
+        }
+        return m_cachedRequirements;
+    }
+
+    public int getTypes()
+    {
+        return m_host.getTypes();
+    }
+
+    public BundleWiring getWiring()
+    {
+        return null;
+    }
+
+    public Bundle getBundle()
+    {
+        return m_host.getBundle();
+    }
+
+    @Override
+    public String toString()
+    {
+        return m_host.toString();
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/felix/framework/util/CompoundEnumeration.java b/src/main/java/org/apache/felix/framework/util/CompoundEnumeration.java
new file mode 100644
index 0000000..8ea8ffb
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/util/CompoundEnumeration.java
@@ -0,0 +1,109 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.util;
+
+import java.util.Enumeration;
+import java.util.NoSuchElementException;
+
+public class CompoundEnumeration implements Enumeration
+{
+    private Enumeration[] m_enums = null;
+    private int index = 0;
+
+    public CompoundEnumeration(Enumeration[] enums)
+    {
+        m_enums = enums;
+    }
+
+    public boolean hasMoreElements()
+    {
+        // if the current enum is null that means this enum is finished
+        if (currentEnumeration() == null)
+        {
+            // No next enum
+            return false;
+        }
+        // If the current enum has more elements, lets go
+        return currentEnumeration().hasMoreElements();
+    }
+
+    private Enumeration findNextEnumeration(boolean moveCursor)
+    {
+        return findNextEnumeration(index, moveCursor);
+    }
+
+    private Enumeration findNextEnumeration(int cursor, boolean moveCursor)
+    {
+        // next place in the array
+        int next = cursor + 1;
+        // If the cursor is still in the array
+        if (next < m_enums.length)
+        {
+
+            // If there is something in that place
+            // AND the enum is not empty
+            if (m_enums[next] != null &&
+                m_enums[next].hasMoreElements())
+            {
+                // OK
+                if (moveCursor)
+                {
+                    index = next;
+                }
+                return m_enums[next];
+            }
+            // Try next element
+            return findNextEnumeration(next, moveCursor);
+        }
+        // No more elements available
+        return null;
+    }
+
+    public Object nextElement()
+    {
+        // ask for the next element of the current enum.
+        if (currentEnumeration() != null)
+        {
+            return currentEnumeration().nextElement();
+        }
+
+        // no more elements in this Enum
+        // We must throw a NoSuchElementException
+        throw new NoSuchElementException("No more elements");
+    }
+
+    private Enumeration currentEnumeration()
+    {
+        if (m_enums != null)
+        {
+            if (index < m_enums.length)
+            {
+                Enumeration e = m_enums[index];
+                if (e == null || !e.hasMoreElements())
+                {
+                    // the current enum is null or empty
+                    // we probably want to switch to the next one
+                    e = findNextEnumeration(true);
+                }
+                return e;
+            }
+        }
+        return null;
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/felix/framework/util/EventDispatcher.java b/src/main/java/org/apache/felix/framework/util/EventDispatcher.java
new file mode 100644
index 0000000..c5c264c
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/util/EventDispatcher.java
@@ -0,0 +1,1126 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.util;
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Dictionary;
+import java.util.EventListener;
+import java.util.EventObject;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import org.apache.felix.framework.Logger;
+import org.apache.felix.framework.ServiceRegistry;
+import org.osgi.framework.AllServiceListener;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.BundleListener;
+import org.osgi.framework.Filter;
+import org.osgi.framework.FrameworkEvent;
+import org.osgi.framework.FrameworkListener;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServicePermission;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.SynchronousBundleListener;
+import org.osgi.framework.UnfilteredServiceListener;
+import org.osgi.framework.hooks.service.ListenerHook;
+import org.osgi.framework.launch.Framework;
+
+public class EventDispatcher
+{
+    private final Logger m_logger;
+    private final ServiceRegistry m_registry;
+
+    private Map<BundleContext, List<ListenerInfo>>
+        m_fwkListeners = Collections.EMPTY_MAP;
+    private Map<BundleContext, List<ListenerInfo>>
+        m_bndlListeners = Collections.EMPTY_MAP;
+    private Map<BundleContext, List<ListenerInfo>>
+        m_syncBndlListeners = Collections.EMPTY_MAP;
+    private Map<BundleContext, List<ListenerInfo>>
+        m_svcListeners = Collections.EMPTY_MAP;
+
+    // A single thread is used to deliver events for all dispatchers.
+    private static Thread m_thread = null;
+    private final static String m_threadLock = new String("thread lock");
+    private static int m_references = 0;
+    private static volatile boolean m_stopping = false;
+
+    // List of requests.
+    private static final List<Request> m_requestList = new ArrayList<Request>();
+    // Pooled requests to avoid memory allocation.
+    private static final List<Request> m_requestPool = new ArrayList<Request>();
+
+    private static final SecureAction m_secureAction = new SecureAction();
+
+    public EventDispatcher(Logger logger, ServiceRegistry registry)
+    {
+        m_logger = logger;
+        m_registry = registry;
+    }
+
+    public void startDispatching()
+    {
+        synchronized (m_threadLock)
+        {
+            // Start event dispatching thread if necessary.
+            if (m_thread == null || !m_thread.isAlive())
+            {
+                m_stopping = false;
+
+                m_thread = new Thread(new Runnable() {
+                    public void run()
+                    {
+                        try
+                        {
+                            EventDispatcher.run();
+                        }
+                        finally
+                        {
+                            // Ensure we update state even if stopped by external cause
+                            // e.g. an Applet VM forceably killing threads
+                            synchronized (m_threadLock)
+                            {
+                                m_thread = null;
+                                m_stopping = false;
+                                m_references = 0;
+                                m_threadLock.notifyAll();
+                            }
+                        }
+                    }
+                }, "FelixDispatchQueue");
+                m_thread.start();
+            }
+
+            // reference counting and flags
+            m_references++;
+        }
+    }
+
+    public void stopDispatching()
+    {
+        synchronized (m_threadLock)
+        {
+            // Return if already dead or stopping.
+            if (m_thread == null || m_stopping)
+            {
+                return;
+            }
+
+            // decrement use counter, don't continue if there are users
+            m_references--;
+            if (m_references > 0)
+            {
+                return;
+            }
+
+            m_stopping = true;
+        }
+
+        // Signal dispatch thread.
+        synchronized (m_requestList)
+        {
+            m_requestList.notify();
+        }
+
+        // Use separate lock for shutdown to prevent any chance of nested lock deadlock
+        synchronized (m_threadLock)
+        {
+            while (m_thread != null)
+            {
+                try
+                {
+                    m_threadLock.wait();
+                }
+                catch (InterruptedException ex)
+                {
+                }
+            }
+        }
+    }
+
+    public Filter addListener(BundleContext bc, Class clazz, EventListener l, Filter filter)
+    {
+        // Verify the listener.
+        if (l == null)
+        {
+            throw new IllegalArgumentException("Listener is null");
+        }
+        else if (!clazz.isInstance(l))
+        {
+            throw new IllegalArgumentException(
+                "Listener not of type " + clazz.getName());
+        }
+
+        // See if we can simply update the listener, if so then
+        // return immediately.
+        Filter oldFilter = updateListener(bc, clazz, l, filter);
+        if (oldFilter != null)
+        {
+            return oldFilter;
+        }
+
+        // Lock the object to add the listener.
+        synchronized (this)
+        {
+            // Verify that the bundle context is still valid.
+            try
+            {
+                bc.getBundle();
+            }
+            catch (IllegalStateException ex)
+            {
+                // Bundle context is no longer valid, so just return.
+            }
+
+            Map<BundleContext, List<ListenerInfo>> listeners = null;
+            Object acc = null;
+
+            if (clazz == FrameworkListener.class)
+            {
+                listeners = m_fwkListeners;
+            }
+            else if (clazz == BundleListener.class)
+            {
+                if (SynchronousBundleListener.class.isInstance(l))
+                {
+                    listeners = m_syncBndlListeners;
+                }
+                else
+                {
+                    listeners = m_bndlListeners;
+                }
+            }
+            else if (clazz == ServiceListener.class)
+            {
+                // Remember security context for filtering service events.
+                Object sm = System.getSecurityManager();
+                if (sm != null)
+                {
+                    acc = ((SecurityManager) sm).getSecurityContext();
+                }
+                // We need to create a Set for keeping track of matching service
+                // registrations so we can fire ServiceEvent.MODIFIED_ENDMATCH
+                // events. We need a Set even if filter is null, since the
+                // listener can be updated and have a filter added later.
+                listeners = m_svcListeners;
+            }
+            else
+            {
+                throw new IllegalArgumentException("Unknown listener: " + l.getClass());
+            }
+
+            // Add listener.
+            ListenerInfo info =
+                new ListenerInfo(bc.getBundle(), bc, clazz, l, filter, acc, false);
+            listeners = addListenerInfo(listeners, info);
+
+            if (clazz == FrameworkListener.class)
+            {
+                m_fwkListeners = listeners;
+            }
+            else if (clazz == BundleListener.class)
+            {
+                if (SynchronousBundleListener.class.isInstance(l))
+                {
+                    m_syncBndlListeners = listeners;
+                }
+                else
+                {
+                    m_bndlListeners = listeners;
+                }
+            }
+            else if (clazz == ServiceListener.class)
+            {
+                m_svcListeners = listeners;
+            }
+        }
+        return null;
+    }
+
+    public ListenerHook.ListenerInfo removeListener(
+        BundleContext bc, Class clazz, EventListener l)
+    {
+        ListenerHook.ListenerInfo returnInfo = null;
+
+        // Verify listener.
+        if (l == null)
+        {
+            throw new IllegalArgumentException("Listener is null");
+        }
+        else if (!clazz.isInstance(l))
+        {
+            throw new IllegalArgumentException(
+                "Listener not of type " + clazz.getName());
+        }
+
+        // Lock the object to remove the listener.
+        synchronized (this)
+        {
+            Map<BundleContext, List<ListenerInfo>> listeners = null;
+
+            if (clazz == FrameworkListener.class)
+            {
+                listeners = m_fwkListeners;
+            }
+            else if (clazz == BundleListener.class)
+            {
+                if (SynchronousBundleListener.class.isInstance(l))
+                {
+                    listeners = m_syncBndlListeners;
+                }
+                else
+                {
+                    listeners = m_bndlListeners;
+                }
+            }
+            else if (clazz == ServiceListener.class)
+            {
+                listeners = m_svcListeners;
+            }
+            else
+            {
+                throw new IllegalArgumentException("Unknown listener: " + l.getClass());
+            }
+
+            // Try to find the instance in our list.
+            int idx = -1;
+            for (Entry<BundleContext, List<ListenerInfo>> entry : listeners.entrySet())
+            {
+                List<ListenerInfo> infos = entry.getValue();
+                for (int i = 0; i < infos.size(); i++)
+                {
+                    ListenerInfo info = infos.get(i);
+                    if (info.getBundleContext().equals(bc) &&
+                        (info.getListenerClass() == clazz) &&
+                        (info.getListener() == l))
+                    {
+                        // For service listeners, we must return some info about
+                        // the listener for the ListenerHook callback.
+                        if (ServiceListener.class == clazz)
+                        {
+                            returnInfo = new ListenerInfo(infos.get(i), true);
+                        }
+                        idx = i;
+                        break;
+                    }
+                }
+            }
+
+            // If we have the instance, then remove it.
+            if (idx >= 0)
+            {
+                listeners = removeListenerInfo(listeners, bc, idx);
+            }
+
+            if (clazz == FrameworkListener.class)
+            {
+                m_fwkListeners = listeners;
+            }
+            else if (clazz == BundleListener.class)
+            {
+                if (SynchronousBundleListener.class.isInstance(l))
+                {
+                    m_syncBndlListeners = listeners;
+                }
+                else
+                {
+                    m_bndlListeners = listeners;
+                }
+            }
+            else if (clazz == ServiceListener.class)
+            {
+                m_svcListeners = listeners;
+            }
+        }
+
+        // Return information about the listener; this is null
+        // for everything but service listeners.
+        return returnInfo;
+    }
+
+    public void removeListeners(BundleContext bc)
+    {
+        if (bc == null)
+        {
+            return;
+        }
+
+        synchronized (this)
+        {
+            // Remove all framework listeners associated with the specified bundle.
+            m_fwkListeners = removeListenerInfos(m_fwkListeners, bc);
+
+            // Remove all bundle listeners associated with the specified bundle.
+            m_bndlListeners = removeListenerInfos(m_bndlListeners, bc);
+
+            // Remove all synchronous bundle listeners associated with
+            // the specified bundle.
+            m_syncBndlListeners = removeListenerInfos(m_syncBndlListeners, bc);
+
+            // Remove all service listeners associated with the specified bundle.
+            m_svcListeners = removeListenerInfos(m_svcListeners, bc);
+        }
+    }
+
+    public Filter updateListener(BundleContext bc, Class clazz, EventListener l, Filter filter)
+    {
+        if (clazz == ServiceListener.class)
+        {
+            synchronized (this)
+            {
+                // Verify that the bundle context is still valid.
+                try
+                {
+                    bc.getBundle();
+                }
+                catch (IllegalStateException ex)
+                {
+                    // Bundle context is no longer valid, so just return.
+                }
+
+                // See if the service listener is already registered; if so then
+                // update its filter per the spec.
+                List<ListenerInfo> infos = m_svcListeners.get(bc);
+                for (int i = 0; (infos != null) && (i < infos.size()); i++)
+                {
+                    ListenerInfo info = infos.get(i);
+                    if (info.getBundleContext().equals(bc) &&
+                        (info.getListenerClass() == clazz) &&
+                        (info.getListener() == l))
+                    {
+                        // The spec says to update the filter in this case.
+                        Filter oldFilter = info.getParsedFilter();
+                        ListenerInfo newInfo = new ListenerInfo(
+                            info.getBundle(),
+                            info.getBundleContext(),
+                            info.getListenerClass(),
+                            info.getListener(),
+                            filter,
+                            info.getSecurityContext(),
+                            info.isRemoved());
+                        m_svcListeners = updateListenerInfo(m_svcListeners, i, newInfo);
+                        return oldFilter;
+                    }
+                }
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns all existing service listener information into a collection of
+     * ListenerHook.ListenerInfo objects. This is used the first time a listener
+     * hook is registered to synchronize it with the existing set of listeners.
+     * @return Returns all existing service listener information into a collection of
+     *         ListenerHook.ListenerInfo objects
+    **/
+    public Collection<ListenerHook.ListenerInfo> getAllServiceListeners()
+    {
+        List<ListenerHook.ListenerInfo> listeners = new ArrayList<ListenerHook.ListenerInfo>();
+        synchronized (this)
+        {
+            for (Entry<BundleContext, List<ListenerInfo>> entry : m_svcListeners.entrySet())
+            {
+                listeners.addAll(entry.getValue());
+            }
+        }
+        return listeners;
+    }
+
+    public void fireFrameworkEvent(FrameworkEvent event)
+    {
+        // Take a snapshot of the listener array.
+        Map<BundleContext, List<ListenerInfo>> listeners = null;
+        synchronized (this)
+        {
+            listeners = m_fwkListeners;
+        }
+
+        // Fire all framework listeners on a separate thread.
+        fireEventAsynchronously(this, Request.FRAMEWORK_EVENT, listeners, event);
+    }
+
+    public void fireBundleEvent(BundleEvent event, Framework felix)
+    {
+        // Take a snapshot of the listener array.
+        Map<BundleContext, List<ListenerInfo>> listeners = null;
+        Map<BundleContext, List<ListenerInfo>> syncListeners = null;
+        synchronized (this)
+        {
+            listeners = m_bndlListeners;
+            syncListeners = m_syncBndlListeners;
+        }
+
+        // Create a whitelist of bundle context for bundle listeners,
+        // if we have hooks.
+        Set<BundleContext> whitelist = createWhitelistFromHooks(event, felix,
+            listeners, syncListeners, org.osgi.framework.hooks.bundle.EventHook.class);
+
+        // If we have a whitelist, then create copies of only the whitelisted
+        // listeners.
+        if (whitelist != null)
+        {
+            Map<BundleContext, List<ListenerInfo>> copy =
+                new HashMap<BundleContext, List<ListenerInfo>>();
+            for (BundleContext bc : whitelist)
+            {
+                List<ListenerInfo> infos = listeners.get(bc);
+                if (infos != null)
+                {
+                    copy.put(bc, infos);
+                }
+            }
+            listeners = copy;
+            copy = new HashMap<BundleContext, List<ListenerInfo>>();
+            for (BundleContext bc : whitelist)
+            {
+                List<ListenerInfo> infos = syncListeners.get(bc);
+                if (infos != null)
+                {
+                    copy.put(bc, infos);
+                }
+            }
+            syncListeners = copy;
+        }
+
+        // Fire synchronous bundle listeners immediately on the calling thread.
+        fireEventImmediately(
+            this, Request.BUNDLE_EVENT, syncListeners, event, null);
+
+        // The spec says that asynchronous bundle listeners do not get events
+        // of types STARTING, STOPPING, or LAZY_ACTIVATION.
+        if ((event.getType() != BundleEvent.STARTING) &&
+            (event.getType() != BundleEvent.STOPPING) &&
+            (event.getType() != BundleEvent.LAZY_ACTIVATION))
+        {
+            // Fire asynchronous bundle listeners on a separate thread.
+            fireEventAsynchronously(
+                this, Request.BUNDLE_EVENT, listeners, event);
+        }
+    }
+
+    public void fireServiceEvent(
+        final ServiceEvent event, final Dictionary oldProps, final Framework felix)
+    {
+        // Take a snapshot of the listener array.
+        Map<BundleContext, List<ListenerInfo>> listeners = null;
+        synchronized (this)
+        {
+            listeners = m_svcListeners;
+        }
+
+        // Use service registry hooks to filter target listeners.
+        listeners = filterListenersUsingHooks(event, felix, listeners);
+
+        // Fire all service events immediately on the calling thread.
+        fireEventImmediately(
+            this, Request.SERVICE_EVENT, listeners, event, oldProps);
+    }
+
+// TODO: OSGi R4.3 - This is ugly and inefficient.
+    private Map<BundleContext, List<ListenerInfo>> filterListenersUsingHooks(
+        ServiceEvent event, Framework felix, Map<BundleContext, List<ListenerInfo>> listeners)
+    {
+        Set<ServiceReference<org.osgi.framework.hooks.service.EventHook>> ehs =
+            m_registry.getHooks(org.osgi.framework.hooks.service.EventHook.class);
+        if ((ehs != null) && !ehs.isEmpty())
+        {
+            // Create a whitelist of bundle context for bundle listeners,
+            // if we have hooks.
+            Set<BundleContext> whitelist = createWhitelistFromHooks(event, felix,
+                listeners, null, org.osgi.framework.hooks.service.EventHook.class);
+
+            // If we have a whitelist, then create copies of only the whitelisted
+            // listeners.
+            if (whitelist != null)
+            {
+                Map<BundleContext, List<ListenerInfo>> copy =
+                    new HashMap<BundleContext, List<ListenerInfo>>();
+                for (BundleContext bc : whitelist)
+                {
+                    copy.put(bc, listeners.get(bc));
+                }
+                listeners = copy;
+            }
+        }
+
+        Set<ServiceReference<org.osgi.framework.hooks.service.EventListenerHook>> elhs =
+            m_registry.getHooks(org.osgi.framework.hooks.service.EventListenerHook.class);
+        if ((elhs != null) && !elhs.isEmpty())
+        {
+            // Create shrinkable map with shrinkable collections.
+            Map<BundleContext, Collection<ListenerHook.ListenerInfo>> shrinkableMap =
+                new HashMap<BundleContext, Collection<ListenerHook.ListenerInfo>>();
+            for (Entry<BundleContext, List<ListenerInfo>> entry : listeners.entrySet())
+            {
+                Collection shrinkableCollection =
+                    new ShrinkableCollection(new ArrayList(entry.getValue()));
+                shrinkableMap.put(
+                    entry.getKey(),
+                    (Collection<ListenerHook.ListenerInfo>) shrinkableCollection);
+            }
+            shrinkableMap =
+                new ShrinkableMap<BundleContext, Collection<ListenerHook.ListenerInfo>>
+                    (shrinkableMap);
+
+            for (ServiceReference<org.osgi.framework.hooks.service.EventListenerHook> sr : elhs)
+            {
+                if (felix != null)
+                {
+                    org.osgi.framework.hooks.service.EventListenerHook elh = null;
+                    try
+                    {
+                        elh = m_registry.getService(felix, sr);
+                    }
+                    catch (Exception ex)
+                    {
+                        // If we can't get the hook, then ignore it.
+                    }
+                    if (elh != null)
+                    {
+                        try
+                        {
+                            m_secureAction.invokeServiceEventListenerHook(
+                                elh, event, shrinkableMap);
+                        }
+                        catch (Throwable th)
+                        {
+                            m_logger.log(sr, Logger.LOG_WARNING,
+                                "Problem invoking event hook", th);
+                        }
+                        finally
+                        {
+                            m_registry.ungetService(felix, sr);
+                        }
+                    }
+                }
+            }
+// TODO: OSGi R4.3 - Should check and only do this if there was a change.
+//       Also, it is inefficient to have to create new lists for the values.
+            Map<BundleContext, List<ListenerInfo>> newMap =
+                new HashMap<BundleContext, List<ListenerInfo>>();
+            for (Entry entry : shrinkableMap.entrySet())
+            {
+                if (!((Collection) entry.getValue()).isEmpty())
+                {
+                    newMap.put((BundleContext) entry.getKey(),
+                        new ArrayList<ListenerInfo>((Collection) entry.getValue()));
+                }
+            }
+            listeners = newMap;
+        }
+
+        return listeners;
+    }
+
+    private <T> Set<BundleContext> createWhitelistFromHooks(
+        EventObject event, Framework felix,
+        Map<BundleContext, List<ListenerInfo>> listeners1,
+        Map<BundleContext, List<ListenerInfo>> listeners2,
+        Class<T> hookClass)
+    {
+        // Create a whitelist of bundle context, if we have hooks.
+        Set<BundleContext> whitelist = null;
+        Set<ServiceReference<T>> hooks = m_registry.getHooks(hookClass);
+        if ((hooks != null) && !hooks.isEmpty())
+        {
+            whitelist = new HashSet<BundleContext>();
+            for (Entry<BundleContext, List<ListenerInfo>> entry : listeners1.entrySet())
+            {
+                whitelist.add(entry.getKey());
+            }
+            if (listeners2 != null)
+            {
+                for (Entry<BundleContext, List<ListenerInfo>> entry : listeners2.entrySet())
+                {
+                    whitelist.add(entry.getKey());
+                }
+            }
+
+            int originalSize = whitelist.size();
+            ShrinkableCollection<BundleContext> shrinkable =
+                new ShrinkableCollection<BundleContext>(whitelist);
+            for (ServiceReference<T> sr : hooks)
+            {
+                if (felix != null)
+                {
+                    T eh = null;
+                    try
+                    {
+                        eh = m_registry.getService(felix, sr);
+                    }
+                    catch (Exception ex)
+                    {
+                        // If we can't get the hook, then ignore it.
+                    }
+                    if (eh != null)
+                    {
+                        try
+                        {
+                            if (eh instanceof org.osgi.framework.hooks.service.EventHook)
+                            {
+                                m_secureAction.invokeServiceEventHook(
+                                    (org.osgi.framework.hooks.service.EventHook) eh,
+                                    (ServiceEvent) event, shrinkable);
+                            }
+                            else if (eh instanceof org.osgi.framework.hooks.bundle.EventHook)
+                            {
+                                m_secureAction.invokeBundleEventHook(
+                                    (org.osgi.framework.hooks.bundle.EventHook) eh,
+                                    (BundleEvent) event, shrinkable);
+                            }
+                        }
+                        catch (Throwable th)
+                        {
+                            m_logger.log(sr, Logger.LOG_WARNING,
+                                "Problem invoking event hook", th);
+                        }
+                        finally
+                        {
+                            m_registry.ungetService(felix, sr);
+                        }
+                    }
+                }
+            }
+            // If the whitelist hasn't changed, then null it to avoid having
+            // to do whitelist lookups during event delivery.
+            if (originalSize == whitelist.size())
+            {
+                whitelist = null;
+            }
+        }
+        return whitelist;
+    }
+
+    private static void fireEventAsynchronously(
+        EventDispatcher dispatcher, int type,
+        Map<BundleContext, List<ListenerInfo>> listeners,
+        EventObject event)
+    {
+        //TODO: should possibly check this within thread lock, seems to be ok though without
+        // If dispatch thread is stopped, then ignore dispatch request.
+        if (m_stopping || m_thread == null)
+        {
+            return;
+        }
+
+        // First get a request from the pool or create one if necessary.
+        Request req = null;
+        synchronized (m_requestPool)
+        {
+            if (m_requestPool.size() > 0)
+            {
+                req = m_requestPool.remove(0);
+            }
+            else
+            {
+                req = new Request();
+            }
+        }
+
+        // Initialize dispatch request.
+        req.m_dispatcher = dispatcher;
+        req.m_type = type;
+        req.m_listeners = listeners;
+        req.m_event = event;
+
+        // Lock the request list.
+        synchronized (m_requestList)
+        {
+            // Add our request to the list.
+            m_requestList.add(req);
+            // Notify the dispatch thread that there is work to do.
+            m_requestList.notify();
+        }
+    }
+
+    private static void fireEventImmediately(
+        EventDispatcher dispatcher, int type,
+        Map<BundleContext, List<ListenerInfo>> listeners,
+        EventObject event, Dictionary oldProps)
+    {
+        if (!listeners.isEmpty())
+        {
+            // Notify appropriate listeners.
+            for (Entry<BundleContext, List<ListenerInfo>> entry : listeners.entrySet())
+            {
+                for (ListenerInfo info : entry.getValue())
+                {
+                    Bundle bundle = info.getBundle();
+                    EventListener l = info.getListener();
+                    Filter filter = info.getParsedFilter();
+                    Object acc = info.getSecurityContext();
+
+                    try
+                    {
+                        if (type == Request.FRAMEWORK_EVENT)
+                        {
+                            invokeFrameworkListenerCallback(bundle, l, event);
+                        }
+                        else if (type == Request.BUNDLE_EVENT)
+                        {
+                            invokeBundleListenerCallback(bundle, l, event);
+                        }
+                        else if (type == Request.SERVICE_EVENT)
+                        {
+                            invokeServiceListenerCallback(
+                                bundle, l, filter, acc, event, oldProps);
+                        }
+                    }
+                    catch (Throwable th)
+                    {
+                        if ((type != Request.FRAMEWORK_EVENT)
+                            || (((FrameworkEvent) event).getType() != FrameworkEvent.ERROR))
+                        {
+                            dispatcher.m_logger.log(bundle,
+                                Logger.LOG_ERROR,
+                                "EventDispatcher: Error during dispatch.", th);
+                            dispatcher.fireFrameworkEvent(
+                                new FrameworkEvent(FrameworkEvent.ERROR, bundle, th));
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    private static void invokeFrameworkListenerCallback(
+        Bundle bundle, final EventListener l, final EventObject event)
+    {
+        // The spec says only active bundles receive asynchronous events,
+        // but we will include starting bundles too otherwise
+        // it is impossible to see everything.
+        if ((bundle.getState() == Bundle.STARTING) ||
+            (bundle.getState() == Bundle.ACTIVE))
+        {
+            if (System.getSecurityManager() != null)
+            {
+                AccessController.doPrivileged(new PrivilegedAction() {
+                    public Object run()
+                    {
+                        ((FrameworkListener) l).frameworkEvent((FrameworkEvent) event);
+                        return null;
+                    }
+                });
+            }
+            else
+            {
+                ((FrameworkListener) l).frameworkEvent((FrameworkEvent) event);
+            }
+        }
+    }
+
+    private static void invokeBundleListenerCallback(
+        Bundle bundle, final EventListener l, final EventObject event)
+    {
+        // A bundle listener is either synchronous or asynchronous.
+        // If the bundle listener is synchronous, then deliver the
+        // event to bundles with a state of STARTING, STOPPING, or
+        // ACTIVE. If the listener is asynchronous, then deliver the
+        // event only to bundles that are STARTING or ACTIVE.
+        if (((SynchronousBundleListener.class.isAssignableFrom(l.getClass())) &&
+            ((bundle.getState() == Bundle.STARTING) ||
+            (bundle.getState() == Bundle.STOPPING) ||
+            (bundle.getState() == Bundle.ACTIVE)))
+            ||
+            ((bundle.getState() == Bundle.STARTING) ||
+            (bundle.getState() == Bundle.ACTIVE)))
+        {
+            if (System.getSecurityManager() != null)
+            {
+                AccessController.doPrivileged(new PrivilegedAction() {
+                    public Object run()
+                    {
+                        ((BundleListener) l).bundleChanged((BundleEvent) event);
+                        return null;
+                    }
+                });
+            }
+            else
+            {
+                ((BundleListener) l).bundleChanged((BundleEvent) event);
+            }
+        }
+    }
+
+    private static void invokeServiceListenerCallback(
+        Bundle bundle, final EventListener l, Filter filter, Object acc,
+        final EventObject event, final Dictionary oldProps)
+    {
+        // Service events should be delivered to STARTING,
+        // STOPPING, and ACTIVE bundles.
+        if ((bundle.getState() != Bundle.STARTING) &&
+            (bundle.getState() != Bundle.STOPPING) &&
+            (bundle.getState() != Bundle.ACTIVE))
+        {
+            return;
+        }
+
+        // Check that the bundle has permission to get at least
+        // one of the service interfaces; the objectClass property
+        // of the service stores its service interfaces.
+        ServiceReference ref = ((ServiceEvent) event).getServiceReference();
+
+        boolean hasPermission = true;
+        Object sm = System.getSecurityManager();
+        if ((acc != null) && (sm != null))
+        {
+            try
+            {
+                ServicePermission perm =
+                    new ServicePermission(
+                        ref, ServicePermission.GET);
+                ((SecurityManager) sm).checkPermission(perm, acc);
+            }
+            catch (Exception ex)
+            {
+                hasPermission = false;
+            }
+        }
+
+        if (hasPermission)
+        {
+            // Dispatch according to the filter.
+            boolean matched;
+            if (l instanceof UnfilteredServiceListener)
+            {
+                // An UnfilteredServiceListener always matches, regardless of the filter.
+                // The filter is still passed on to the Service Registry Hooks.
+                matched = true;
+            }
+            else
+            {
+                matched = (filter == null)
+                        || filter.match(((ServiceEvent) event).getServiceReference());
+            }
+
+            if (matched)
+            {
+                if ((l instanceof AllServiceListener) ||
+                    Util.isServiceAssignable(bundle, ((ServiceEvent) event).getServiceReference()))
+                {
+                    if (System.getSecurityManager() != null)
+                    {
+                        AccessController.doPrivileged(new PrivilegedAction()
+                        {
+                            public Object run()
+                            {
+                                ((ServiceListener) l).serviceChanged((ServiceEvent) event);
+                                return null;
+                            }
+                        });
+                    }
+                    else
+                    {
+                        ((ServiceListener) l).serviceChanged((ServiceEvent) event);
+                    }
+                }
+            }
+            // We need to send an MODIFIED_ENDMATCH event if the listener
+            // matched previously.
+            else if (((ServiceEvent) event).getType() == ServiceEvent.MODIFIED)
+            {
+                if (filter.match(oldProps))
+                {
+                    final ServiceEvent se = new ServiceEvent(
+                        ServiceEvent.MODIFIED_ENDMATCH,
+                        ((ServiceEvent) event).getServiceReference());
+                    if (System.getSecurityManager() != null)
+                    {
+                        AccessController.doPrivileged(new PrivilegedAction()
+                        {
+                            public Object run()
+                            {
+                                ((ServiceListener) l).serviceChanged(se);
+                                return null;
+                            }
+                        });
+                    }
+                    else
+                    {
+                        ((ServiceListener) l).serviceChanged(se);
+                    }
+                }
+            }
+        }
+    }
+
+    private static Map<BundleContext, List<ListenerInfo>> addListenerInfo(
+        Map<BundleContext, List<ListenerInfo>> listeners, ListenerInfo info)
+    {
+        // Make a copy of the map, since we will be mutating it.
+        Map<BundleContext, List<ListenerInfo>> copy =
+            new HashMap<BundleContext, List<ListenerInfo>>(listeners);
+        // Remove the affected entry and make a copy so we can modify it.
+        List<ListenerInfo> infos = copy.remove(info.getBundleContext());
+        if (infos == null)
+        {
+            infos = new ArrayList<ListenerInfo>();
+        }
+        else
+        {
+            infos = new ArrayList<ListenerInfo>(infos);
+        }
+        // Add the new listener info.
+        infos.add(info);
+        // Put the listeners back into the copy of the map and return it.
+        copy.put(info.getBundleContext(), infos);
+        return copy;
+    }
+
+    private static Map<BundleContext, List<ListenerInfo>> updateListenerInfo(
+        Map<BundleContext, List<ListenerInfo>> listeners, int idx,
+        ListenerInfo info)
+    {
+        // Make a copy of the map, since we will be mutating it.
+        Map<BundleContext, List<ListenerInfo>> copy =
+            new HashMap<BundleContext, List<ListenerInfo>>(listeners);
+        // Remove the affected entry and make a copy so we can modify it.
+        List<ListenerInfo> infos = copy.remove(info.getBundleContext());
+        if (infos != null)
+        {
+            infos = new ArrayList<ListenerInfo>(infos);
+            // Update the new listener info.
+            infos.set(idx, info);
+            // Put the listeners back into the copy of the map and return it.
+            copy.put(info.getBundleContext(), infos);
+            return copy;
+        }
+        return listeners;
+    }
+
+    private static Map<BundleContext, List<ListenerInfo>> removeListenerInfo(
+        Map<BundleContext, List<ListenerInfo>> listeners, BundleContext bc, int idx)
+    {
+        // Make a copy of the map, since we will be mutating it.
+        Map<BundleContext, List<ListenerInfo>> copy =
+            new HashMap<BundleContext, List<ListenerInfo>>(listeners);
+        // Remove the affected entry and make a copy so we can modify it.
+        List<ListenerInfo> infos = copy.remove(bc);
+        if (infos != null)
+        {
+            infos = new ArrayList<ListenerInfo>(infos);
+            // Remove the listener info.
+            infos.remove(idx);
+            if (!infos.isEmpty())
+            {
+                // Put the listeners back into the copy of the map and return it.
+                copy.put(bc, infos);
+            }
+            return copy;
+        }
+        return listeners;
+    }
+
+    private static Map<BundleContext, List<ListenerInfo>> removeListenerInfos(
+        Map<BundleContext, List<ListenerInfo>> listeners, BundleContext bc)
+    {
+        // Make a copy of the map, since we will be mutating it.
+        Map<BundleContext, List<ListenerInfo>> copy =
+            new HashMap<BundleContext, List<ListenerInfo>>(listeners);
+        // Remove the affected entry and return the copy.
+        copy.remove(bc);
+        return copy;
+    }
+
+    /**
+     * This is the dispatching thread's main loop.
+    **/
+    private static void run()
+    {
+        Request req = null;
+        while (true)
+        {
+            // Lock the request list so we can try to get a
+            // dispatch request from it.
+            synchronized (m_requestList)
+            {
+                // Wait while there are no requests to dispatch. If the
+                // dispatcher thread is supposed to stop, then let the
+                // dispatcher thread exit the loop and stop.
+                while (m_requestList.isEmpty() && !m_stopping)
+                {
+                    // Wait until some signals us for work.
+                    try
+                    {
+                        m_requestList.wait();
+                    }
+                    catch (InterruptedException ex)
+                    {
+                        // Not much we can do here except for keep waiting.
+                    }
+                }
+
+                // If there are no events to dispatch and shutdown
+                // has been called then exit, otherwise dispatch event.
+                if (m_requestList.isEmpty() && m_stopping)
+                {
+                    return;
+                }
+
+                // Get the dispatch request.
+                req = m_requestList.remove(0);
+            }
+
+            // Deliver event outside of synchronized block
+            // so that we don't block other requests from being
+            // queued during event processing.
+            // NOTE: We don't catch any exceptions here, because
+            // the invoked method shields us from exceptions by
+            // catching Throwables when it invokes callbacks.
+            fireEventImmediately(
+                req.m_dispatcher, req.m_type, req.m_listeners,
+                req.m_event, null);
+
+            // Put dispatch request in cache.
+            synchronized (m_requestPool)
+            {
+                req.m_dispatcher = null;
+                req.m_type = -1;
+                req.m_listeners = null;
+                req.m_event = null;
+                m_requestPool.add(req);
+            }
+        }
+    }
+
+    private static class Request
+    {
+        public static final int FRAMEWORK_EVENT = 0;
+        public static final int BUNDLE_EVENT = 1;
+        public static final int SERVICE_EVENT = 2;
+
+        public EventDispatcher m_dispatcher = null;
+        public int m_type = -1;
+        public Map<BundleContext, List<ListenerInfo>> m_listeners = null;
+        public EventObject m_event = null;
+    }
+}
diff --git a/src/main/java/org/apache/felix/framework/util/FelixConstants.java b/src/main/java/org/apache/felix/framework/util/FelixConstants.java
new file mode 100644
index 0000000..511657c
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/util/FelixConstants.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.util;
+
+public interface FelixConstants extends org.osgi.framework.Constants
+{
+    String SYSTEM_BUNDLE_SYMBOLICNAME = "org.apache.felix.framework";
+    // Framework constants and values.
+    String FRAMEWORK_VERSION_VALUE = "1.5";
+    String FRAMEWORK_VENDOR_VALUE = "Apache Software Foundation";
+
+    // Framework constants and values.
+    String FELIX_VERSION_PROPERTY = "felix.version";
+
+    // Miscellaneous manifest constants.
+    String DIRECTIVE_SEPARATOR = ":=";
+    String ATTRIBUTE_SEPARATOR = "=";
+    String CLASS_PATH_SEPARATOR = ",";
+    String CLASS_PATH_DOT = ".";
+    String PACKAGE_SEPARATOR = ";";
+    String VERSION_SEGMENT_SEPARATOR = ".";
+    int VERSION_SEGMENT_COUNT = 3;
+    String BUNDLE_NATIVECODE_OPTIONAL = "*";
+
+    // Miscellaneous OSGi constants.
+    String BUNDLE_URL_PROTOCOL = "bundle";
+
+    // Miscellaneous framework configuration property names.
+    String FRAMEWORK_BUNDLECACHE_IMPL = "felix.bundlecache.impl";
+    String LOG_LEVEL_PROP = "felix.log.level";
+    String LOG_LOGGER_PROP = "felix.log.logger";
+    String SYSTEMBUNDLE_ACTIVATORS_PROP = "felix.systembundle.activators";
+    String BUNDLE_STARTLEVEL_PROP = "felix.startlevel.bundle";
+    String SERVICE_URLHANDLERS_PROP = "felix.service.urlhandlers";
+    String IMPLICIT_BOOT_DELEGATION_PROP = "felix.bootdelegation.implicit";
+    String BOOT_CLASSLOADERS_PROP = "felix.bootdelegation.classloaders";
+    String USE_LOCALURLS_PROP = "felix.jarurls";
+
+    // Missing OSGi constant for resolution directive.
+    String RESOLUTION_DYNAMIC = "dynamic";
+
+    // Start level-related constants.
+    int FRAMEWORK_INACTIVE_STARTLEVEL = 0;
+    int FRAMEWORK_DEFAULT_STARTLEVEL = 1;
+    int SYSTEMBUNDLE_DEFAULT_STARTLEVEL = 0;
+    int BUNDLE_DEFAULT_STARTLEVEL = 1;
+
+    // Miscellaneous properties values.
+    String FAKE_URL_PROTOCOL_VALUE = "location:";
+    String FELIX_EXTENSION_ACTIVATOR = "Felix-Activator";
+    String SECURITY_DEFAULT_POLICY = "felix.security.defaultpolicy";
+}
diff --git a/src/main/java/org/apache/felix/framework/util/ImmutableList.java b/src/main/java/org/apache/felix/framework/util/ImmutableList.java
new file mode 100644
index 0000000..f0a1a9c
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/util/ImmutableList.java
@@ -0,0 +1,146 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.util;
+
+import java.util.AbstractList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.ListIterator;
+import java.util.RandomAccess;
+
+public class ImmutableList<E> extends AbstractList<E> implements RandomAccess
+{
+    final Object[] elements;
+
+    public static <E> ImmutableList<E> newInstance(E... elements)
+    {
+        return new ImmutableList<E>(elements);
+    }
+
+    public static <E> ImmutableList<E> newInstance(Collection<? extends E> elements)
+    {
+        if (elements instanceof ImmutableList)
+        {
+            return (ImmutableList<E>) elements;
+        }
+        else
+        {
+            return new ImmutableList<E>(elements);
+        }
+    }
+
+    protected ImmutableList(E... elements)
+    {
+        this.elements = elements.clone();
+    }
+
+    protected ImmutableList(Collection<? extends E> elements)
+    {
+        this.elements = elements.toArray();
+    }
+
+    public E get(int index)
+    {
+        return (E) elements[index];
+    }
+
+    public int size()
+    {
+        return elements.length;
+    }
+
+    @Override
+    public boolean remove(Object o)
+    {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean removeAll(Collection<?> clctn)
+    {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Iterator<E> iterator()
+    {
+        return listIterator();
+    }
+
+    @Override
+    public ListIterator<E> listIterator(int index)
+    {
+        return new ListItr(index);
+    }
+
+    private class ListItr implements ListIterator<E>
+    {
+        int cursor;
+
+        private ListItr(int cursor)
+        {
+            this.cursor = cursor;
+        }
+
+        public boolean hasNext()
+        {
+            return cursor != size();
+        }
+
+        public E next()
+        {
+            return (E) elements[cursor++];
+        }
+
+        public boolean hasPrevious()
+        {
+            return cursor != 0;
+        }
+
+        public E previous()
+        {
+            return (E) elements[--cursor];
+        }
+
+        public int nextIndex()
+        {
+            return cursor;
+        }
+
+        public int previousIndex()
+        {
+            return cursor - 1;
+        }
+
+        public void remove()
+        {
+            throw new UnsupportedOperationException();
+        }
+
+        public void set(E e)
+        {
+            throw new UnsupportedOperationException();
+        }
+
+        public void add(E e)
+        {
+            throw new UnsupportedOperationException();
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/felix/framework/util/ImmutableMap.java b/src/main/java/org/apache/felix/framework/util/ImmutableMap.java
new file mode 100644
index 0000000..1ee3140
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/util/ImmutableMap.java
@@ -0,0 +1,129 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.util;
+
+import java.util.AbstractMap;
+import java.util.AbstractSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+public class ImmutableMap<K, V> extends AbstractMap<K, V>
+{
+    final Entry<K, V>[] entries;
+
+    public static <K, V> ImmutableMap<K, V> newInstance(Entry<K, V>... entries)
+    {
+        return new ImmutableMap<K, V>(entries);
+    }
+
+    public static <K, V> ImmutableMap<K, V> newInstance(Map<K, V> entries)
+    {
+        if (entries instanceof ImmutableMap)
+        {
+            return (ImmutableMap<K, V>) entries;
+        }
+        else
+        {
+            return new ImmutableMap<K, V>(entries);
+        }
+    }
+
+    protected ImmutableMap(Entry<K, V>[] entries)
+    {
+        this.entries = entries.clone();
+    }
+
+    protected ImmutableMap(Map<K, V> map)
+    {
+        this.entries = map.entrySet().toArray(new Entry[map.size()]);
+    }
+
+    @Override
+    public V get(Object key)
+    {
+        if (key == null)
+        {
+            for (int i = 0; i < entries.length; i++)
+            {
+                if (entries[i].getKey() == null)
+                {
+                    return entries[i].getValue();
+                }
+            }
+        }
+        else
+        {
+            for (int i = 0; i < entries.length; i++)
+            {
+                if (key.equals(entries[i].getKey()))
+                {
+                    return entries[i].getValue();
+                }
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public Set<Entry<K, V>> entrySet()
+    {
+        return new EntrySet();
+    }
+
+    private class EntrySet extends AbstractSet<Entry<K, V>>
+    {
+        @Override
+        public Iterator<Entry<K, V>> iterator()
+        {
+            return new EntryItr(0);
+        }
+
+        @Override
+        public int size()
+        {
+            return entries.length;
+        }
+    }
+
+    private class EntryItr implements Iterator<Entry<K, V>>
+    {
+        int cursor;
+
+        private EntryItr(int cursor)
+        {
+            this.cursor = cursor;
+        }
+
+        public boolean hasNext()
+        {
+            return cursor != size();
+        }
+
+        public Entry<K, V> next()
+        {
+            return entries[cursor++];
+        }
+
+        public void remove()
+        {
+            throw new UnsupportedOperationException();
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/felix/framework/util/ListenerInfo.java b/src/main/java/org/apache/felix/framework/util/ListenerInfo.java
new file mode 100644
index 0000000..b7ad8ae
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/util/ListenerInfo.java
@@ -0,0 +1,141 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.util;
+
+import java.util.EventListener;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Filter;
+import org.osgi.framework.hooks.service.ListenerHook;
+
+
+public class ListenerInfo implements ListenerHook.ListenerInfo
+{
+    private final Bundle m_bundle;
+    private final BundleContext m_context;
+    private final Class m_listenerClass;
+    private final EventListener m_listener;
+    private final Filter m_filter;
+    private final Object m_acc;
+    private final boolean m_removed;
+
+    public ListenerInfo(
+        Bundle bundle, BundleContext context, Class listenerClass, EventListener listener,
+        Filter filter, Object acc, boolean removed)
+    {
+        // Technically, we could get the bundle from the bundle context, but
+        // there are some corner cases where the bundle context might become
+        // invalid and we still need the bundle.
+        m_bundle = bundle;
+        m_context = context;
+        m_listenerClass = listenerClass;
+        m_listener = listener;
+        m_filter = filter;
+        m_acc = acc;
+        m_removed = removed;
+    }
+
+    public ListenerInfo(ListenerInfo info, boolean removed)
+    {
+        m_bundle = info.m_bundle;
+        m_context = info.m_context;
+        m_listenerClass = info.m_listenerClass;
+        m_listener = info.m_listener;
+        m_filter = info.m_filter;
+        m_acc = info.m_acc;
+        m_removed = removed;
+    }
+
+    public Bundle getBundle()
+    {
+        return m_bundle;
+    }
+
+    public BundleContext getBundleContext()
+    {
+        return m_context;
+    }
+
+    public Class getListenerClass()
+    {
+        return m_listenerClass;
+    }
+
+    public EventListener getListener()
+    {
+        return m_listener;
+    }
+
+    public Filter getParsedFilter()
+    {
+        return m_filter;
+    }
+
+    public String getFilter()
+    {
+        if (m_filter != null)
+        {
+            return m_filter.toString();
+        }
+        return null;
+    }
+
+    public Object getSecurityContext()
+    {
+        return m_acc;
+    }
+
+    public boolean isRemoved()
+    {
+        return m_removed;
+    }
+
+    @Override
+    public boolean equals(Object obj)
+    {
+        if (obj == this)
+        {
+            return true;
+        }
+
+        if (!(obj instanceof ListenerInfo))
+        {
+            return false;
+        }
+
+        ListenerInfo other = (ListenerInfo) obj;
+        return (other.m_bundle == m_bundle)
+            && (other.m_context == m_context)
+            && (other.m_listenerClass == m_listenerClass)
+            && (other.m_listener == m_listener)
+            && (m_filter == null ? other.m_filter == null : m_filter.equals(other.m_filter));
+    }
+
+    @Override
+    public int hashCode()
+    {
+        int hash = 7;
+        hash = 59 * hash + (this.m_bundle != null ? this.m_bundle.hashCode() : 0);
+        hash = 59 * hash + (this.m_context != null ? this.m_context.hashCode() : 0);
+        hash = 59 * hash + (this.m_listenerClass != null ? this.m_listenerClass.hashCode() : 0);
+        hash = 59 * hash + (this.m_listener != null ? this.m_listener.hashCode() : 0);
+        hash = 59 * hash + (this.m_filter != null ? this.m_filter.hashCode() : 0);
+        return hash;
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/felix/framework/util/MapToDictionary.java b/src/main/java/org/apache/felix/framework/util/MapToDictionary.java
new file mode 100644
index 0000000..7b955c0
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/util/MapToDictionary.java
@@ -0,0 +1,83 @@
+/* 
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.util;
+
+import java.util.*;
+
+
+/**
+ * This is a simple class that implements a <tt>Dictionary</tt>
+ * from a <tt>Map</tt>. The resulting dictionary is immutable.
+**/
+public class MapToDictionary extends Dictionary
+{
+    /**
+     * Map source.
+    **/
+    private Map m_map = null;
+
+    public MapToDictionary(Map map)
+    {
+        if (map == null)
+        {
+            throw new IllegalArgumentException("Source map cannot be null.");
+        }
+        m_map = map;
+    }
+
+    public Enumeration elements()
+    {
+        return Collections.enumeration(m_map.values());
+    }
+
+    public Object get(Object key)
+    {
+        return m_map.get(key);
+    }
+
+    public boolean isEmpty()
+    {
+        return m_map.isEmpty();
+    }
+
+    public Enumeration keys()
+    {
+        return Collections.enumeration(m_map.keySet());
+    }
+
+    public Object put(Object key, Object value)
+    {
+        throw new UnsupportedOperationException();
+    }
+
+    public Object remove(Object key)
+    {
+        throw new UnsupportedOperationException();
+    }
+
+    public int size()
+    {
+        return m_map.size();
+    }
+
+    public String toString()
+    {
+        return m_map.toString();
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/felix/framework/util/Mutex.java b/src/main/java/org/apache/felix/framework/util/Mutex.java
new file mode 100644
index 0000000..fdec057
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/util/Mutex.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.util;
+
+public class Mutex
+{
+    private int m_count;
+
+    public Mutex()
+    {
+        m_count = 1;
+    }
+
+    public synchronized void down() throws InterruptedException
+    {
+        while (m_count <= 0)
+        {
+            wait();
+        }
+        m_count = 0;
+    }
+
+    public synchronized void up()
+    {
+        m_count = 1;
+        notifyAll();
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/felix/framework/util/SecureAction.java b/src/main/java/org/apache/felix/framework/util/SecureAction.java
new file mode 100644
index 0000000..a6a2584
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/util/SecureAction.java
@@ -0,0 +1,1710 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.util;
+
+import java.io.*;
+import java.lang.reflect.*;
+import java.net.*;
+import java.security.*;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.zip.ZipFile;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.hooks.resolver.ResolverHook;
+import org.osgi.framework.hooks.service.ListenerHook;
+import org.osgi.framework.wiring.BundleCapability;
+import org.osgi.framework.wiring.BundleRequirement;
+import org.osgi.framework.wiring.BundleRevision;
+
+/**
+ * <p>
+ * This is a utility class to centralize all action that should be performed
+ * in a <tt>doPrivileged()</tt> block. To perform a secure action, simply
+ * create an instance of this class and use the specific method to perform
+ * the desired action. When an instance is created, this class will capture
+ * the security context and will then use that context when checking for
+ * permission to perform the action. Instances of this class should not be
+ * passed around since they may grant the receiver a capability to perform
+ * privileged actions.
+ * </p>
+**/
+public class SecureAction
+{
+    private static final ThreadLocal m_actions = new ThreadLocal()
+    {
+        public Object initialValue()
+        {
+            return new Actions();
+        }
+    };
+
+    protected static transient int BUFSIZE = 4096;
+
+    private AccessControlContext m_acc = null;
+
+    public SecureAction()
+    {
+        if (System.getSecurityManager() != null)
+        {
+            try
+            {
+                Actions actions = (Actions) m_actions.get();
+                actions.set(Actions.INITIALIZE_CONTEXT_ACTION, null);
+                m_acc = (AccessControlContext) AccessController.doPrivileged(actions);
+            }
+            catch (PrivilegedActionException ex)
+            {
+                throw (RuntimeException) ex.getException();
+            }
+        }
+        else
+        {
+            m_acc = AccessController.getContext();
+        }
+    }
+
+    public String getSystemProperty(String name, String def)
+    {
+        if (System.getSecurityManager() != null)
+        {
+            try
+            {
+                Actions actions = (Actions) m_actions.get();
+                actions.set(Actions.GET_PROPERTY_ACTION, name, def);
+                return (String) AccessController.doPrivileged(actions, m_acc);
+            }
+            catch (PrivilegedActionException ex)
+            {
+                throw (RuntimeException) ex.getException();
+            }
+        }
+        else
+        {
+            return System.getProperty(name, def);
+        }
+    }
+
+    public ClassLoader getParentClassLoader(ClassLoader loader)
+    {
+        if (System.getSecurityManager() != null)
+        {
+            try
+            {
+                Actions actions = (Actions) m_actions.get();
+                actions.set(Actions.GET_PARENT_CLASS_LOADER_ACTION, loader);
+                return (ClassLoader) AccessController.doPrivileged(actions, m_acc);
+            }
+            catch (PrivilegedActionException ex)
+            {
+                throw (RuntimeException) ex.getException();
+            }
+        }
+        else
+        {
+            return loader.getParent();
+        }
+    }
+
+    public ClassLoader getSystemClassLoader()
+    {
+        if (System.getSecurityManager() != null)
+        {
+            try
+            {
+                Actions actions = (Actions) m_actions.get();
+                actions.set(Actions.GET_SYSTEM_CLASS_LOADER_ACTION);
+                return (ClassLoader) AccessController.doPrivileged(actions, m_acc);
+            }
+            catch (PrivilegedActionException ex)
+            {
+                throw (RuntimeException) ex.getException();
+            }
+        }
+        else
+        {
+            return ClassLoader.getSystemClassLoader();
+        }
+    }
+
+    public ClassLoader getClassLoader(Class clazz)
+    {
+        if (System.getSecurityManager() != null)
+        {
+            try
+            {
+                Actions actions = (Actions) m_actions.get();
+                actions.set(Actions.GET_CLASS_LOADER_ACTION, clazz);
+                return (ClassLoader) AccessController.doPrivileged(actions, m_acc);
+            }
+            catch (PrivilegedActionException ex)
+            {
+                throw (RuntimeException) ex.getException();
+            }
+        }
+        else
+        {
+            return clazz.getClassLoader();
+        }
+    }
+
+    public Class forName(String name, ClassLoader classloader) throws ClassNotFoundException
+    {
+        if (System.getSecurityManager() != null)
+        {
+            try
+            {
+                Actions actions = (Actions) m_actions.get();
+                actions.set(Actions.FOR_NAME_ACTION, name, classloader);
+                return (Class) AccessController.doPrivileged(actions, m_acc);
+            }
+            catch (PrivilegedActionException ex)
+            {
+                if (ex.getException() instanceof ClassNotFoundException)
+                {
+                    throw (ClassNotFoundException) ex.getException();
+                }
+                throw (RuntimeException) ex.getException();
+            }
+        }
+        else if (classloader != null)
+        {
+            return Class.forName(name, true, classloader);
+        }
+        else
+        {
+            return Class.forName(name);
+        }
+    }
+
+    public URL createURL(String protocol, String host,
+        int port, String path, URLStreamHandler handler)
+        throws MalformedURLException
+    {
+        if (System.getSecurityManager() != null)
+        {
+            try
+            {
+                Actions actions = (Actions) m_actions.get();
+                actions.set(Actions.CREATE_URL_ACTION, protocol, host,
+                    new Integer(port), path, handler);
+                return (URL) AccessController.doPrivileged(actions, m_acc);
+            }
+            catch (PrivilegedActionException ex)
+            {
+                if (ex.getException() instanceof MalformedURLException)
+                {
+                    throw (MalformedURLException) ex.getException();
+                }
+                throw (RuntimeException) ex.getException();
+            }
+        }
+        else
+        {
+            return new URL(protocol, host, port, path, handler);
+        }
+    }
+
+    public URL createURL(URL context, String spec, URLStreamHandler handler)
+        throws MalformedURLException
+    {
+        if (System.getSecurityManager() != null)
+        {
+            try
+            {
+                Actions actions = (Actions) m_actions.get();
+                actions.set(Actions.CREATE_URL_WITH_CONTEXT_ACTION, context,
+                    spec, handler);
+                return (URL) AccessController.doPrivileged(actions, m_acc);
+            }
+            catch (PrivilegedActionException ex)
+            {
+                if (ex.getException() instanceof MalformedURLException)
+                {
+                    throw (MalformedURLException) ex.getException();
+                }
+                throw (RuntimeException) ex.getException();
+            }
+        }
+        else
+        {
+            return new URL(context, spec, handler);
+        }
+    }
+
+    public Process exec(String command) throws IOException
+    {
+        if (System.getSecurityManager() != null)
+        {
+            try
+            {
+                Actions actions = (Actions) m_actions.get();
+                actions.set(Actions.EXEC_ACTION, command);
+                return (Process) AccessController.doPrivileged(actions, m_acc);
+            }
+            catch (PrivilegedActionException ex)
+            {
+                throw (RuntimeException) ex.getException();
+            }
+        }
+        else
+        {
+            return Runtime.getRuntime().exec(command);
+        }
+    }
+
+    public String getAbsolutePath(File file)
+    {
+        if (System.getSecurityManager() != null)
+        {
+            try
+            {
+                Actions actions = (Actions) m_actions.get();
+                actions.set(Actions.GET_ABSOLUTE_PATH_ACTION, file);
+                return (String) AccessController.doPrivileged(actions, m_acc);
+            }
+            catch (PrivilegedActionException ex)
+            {
+                throw (RuntimeException) ex.getException();
+            }
+        }
+        else
+        {
+            return file.getAbsolutePath();
+        }
+    }
+
+    public boolean fileExists(File file)
+    {
+        if (System.getSecurityManager() != null)
+        {
+            try
+            {
+                Actions actions = (Actions) m_actions.get();
+                actions.set(Actions.FILE_EXISTS_ACTION, file);
+                return ((Boolean) AccessController.doPrivileged(actions, m_acc))
+                    .booleanValue();
+            }
+            catch (PrivilegedActionException ex)
+            {
+                throw (RuntimeException) ex.getException();
+            }
+        }
+        else
+        {
+            return file.exists();
+        }
+    }
+
+    public boolean isFileDirectory(File file)
+    {
+        if (System.getSecurityManager() != null)
+        {
+            try
+            {
+                Actions actions = (Actions) m_actions.get();
+                actions.set(Actions.FILE_IS_DIRECTORY_ACTION, file);
+                return ((Boolean) AccessController.doPrivileged(actions, m_acc))
+                    .booleanValue();
+            }
+            catch (PrivilegedActionException ex)
+            {
+                throw (RuntimeException) ex.getException();
+            }
+        }
+        else
+        {
+            return file.isDirectory();
+        }
+    }
+
+    public boolean mkdir(File file)
+    {
+        if (System.getSecurityManager() != null)
+        {
+            try
+            {
+                Actions actions = (Actions) m_actions.get();
+                actions.set(Actions.MAKE_DIRECTORY_ACTION, file);
+                return ((Boolean) AccessController.doPrivileged(actions, m_acc))
+                    .booleanValue();
+            }
+            catch (PrivilegedActionException ex)
+            {
+                throw (RuntimeException) ex.getException();
+            }
+        }
+        else
+        {
+            return file.mkdir();
+        }
+    }
+
+    public boolean mkdirs(File file)
+    {
+        if (System.getSecurityManager() != null)
+        {
+            try
+            {
+                Actions actions = (Actions) m_actions.get();
+                actions.set(Actions.MAKE_DIRECTORIES_ACTION, file);
+                return ((Boolean) AccessController.doPrivileged(actions, m_acc))
+                    .booleanValue();
+            }
+            catch (PrivilegedActionException ex)
+            {
+                throw (RuntimeException) ex.getException();
+            }
+        }
+        else
+        {
+            return file.mkdirs();
+        }
+    }
+
+    public File[] listDirectory(File file)
+    {
+        if (System.getSecurityManager() != null)
+        {
+            try
+            {
+                Actions actions = (Actions) m_actions.get();
+                actions.set(Actions.LIST_DIRECTORY_ACTION, file);
+                return (File[]) AccessController.doPrivileged(actions, m_acc);
+            }
+            catch (PrivilegedActionException ex)
+            {
+                throw (RuntimeException) ex.getException();
+            }
+        }
+        else
+        {
+            return file.listFiles();
+        }
+    }
+
+    public boolean renameFile(File oldFile, File newFile)
+    {
+        if (System.getSecurityManager() != null)
+        {
+            try
+            {
+                Actions actions = (Actions) m_actions.get();
+                actions.set(Actions.RENAME_FILE_ACTION, oldFile, newFile);
+                return ((Boolean) AccessController.doPrivileged(actions, m_acc))
+                    .booleanValue();
+            }
+            catch (PrivilegedActionException ex)
+            {
+                throw (RuntimeException) ex.getException();
+            }
+        }
+        else
+        {
+            return oldFile.renameTo(newFile);
+        }
+    }
+
+    public FileInputStream getFileInputStream(File file) throws IOException
+    {
+        if (System.getSecurityManager() != null)
+        {
+            try
+            {
+                Actions actions = (Actions) m_actions.get();
+                actions.set(Actions.GET_FILE_INPUT_ACTION, file);
+                return (FileInputStream) AccessController.doPrivileged(actions, m_acc);
+            }
+            catch (PrivilegedActionException ex)
+            {
+                if (ex.getException() instanceof IOException)
+                {
+                    throw (IOException) ex.getException();
+                }
+                throw (RuntimeException) ex.getException();
+            }
+        }
+        else
+        {
+            return new FileInputStream(file);
+        }
+    }
+
+    public FileOutputStream getFileOutputStream(File file) throws IOException
+    {
+        if (System.getSecurityManager() != null)
+        {
+            try
+            {
+                Actions actions = (Actions) m_actions.get();
+                actions.set(Actions.GET_FILE_OUTPUT_ACTION, file);
+                return (FileOutputStream) AccessController.doPrivileged(actions, m_acc);
+            }
+            catch (PrivilegedActionException ex)
+            {
+                if (ex.getException() instanceof IOException)
+                {
+                    throw (IOException) ex.getException();
+                }
+                throw (RuntimeException) ex.getException();
+            }
+        }
+        else
+        {
+            return new FileOutputStream(file);
+        }
+    }
+
+    public URI toURI(File file)
+    {
+        if (System.getSecurityManager() != null)
+        {
+            try
+            {
+                Actions actions = (Actions) m_actions.get();
+                actions.set(Actions.TO_URI_ACTION, file);
+                return (URI) AccessController.doPrivileged(actions, m_acc);
+            }
+            catch (PrivilegedActionException ex)
+            {
+                throw (RuntimeException) ex.getException();
+            }
+        }
+        else
+        {
+            return file.toURI();
+        }
+    }
+
+    public InputStream getURLConnectionInputStream(URLConnection conn)
+        throws IOException
+    {
+        if (System.getSecurityManager() != null)
+        {
+            try
+            {
+                Actions actions = (Actions) m_actions.get();
+                actions.set(Actions.GET_URL_INPUT_ACTION, conn);
+                return (InputStream) AccessController.doPrivileged(actions, m_acc);
+            }
+            catch (PrivilegedActionException ex)
+            {
+                if (ex.getException() instanceof IOException)
+                {
+                    throw (IOException) ex.getException();
+                }
+                throw (RuntimeException) ex.getException();
+            }
+        }
+        else
+        {
+            return conn.getInputStream();
+        }
+    }
+
+    public boolean deleteFile(File target)
+    {
+        if (System.getSecurityManager() != null)
+        {
+            try
+            {
+                Actions actions = (Actions) m_actions.get();
+                actions.set(Actions.DELETE_FILE_ACTION, target);
+                return ((Boolean) AccessController.doPrivileged(actions, m_acc))
+                    .booleanValue();
+            }
+            catch (PrivilegedActionException ex)
+            {
+                throw (RuntimeException) ex.getException();
+            }
+        }
+        else
+        {
+            return target.delete();
+        }
+    }
+
+    public File createTempFile(String prefix, String suffix, File dir)
+        throws IOException
+    {
+        if (System.getSecurityManager() != null)
+        {
+            try
+            {
+                Actions actions = (Actions) m_actions.get();
+                actions.set(Actions.CREATE_TMPFILE_ACTION, prefix, suffix, dir);
+                return (File) AccessController.doPrivileged(actions, m_acc);
+            }
+            catch (PrivilegedActionException ex)
+            {
+                if (ex.getException() instanceof IOException)
+                {
+                    throw (IOException) ex.getException();
+                }
+                throw (RuntimeException) ex.getException();
+            }
+        }
+        else
+        {
+            return File.createTempFile(prefix, suffix, dir);
+        }
+    }
+
+    public URLConnection openURLConnection(URL url) throws IOException
+    {
+        if (System.getSecurityManager() != null)
+        {
+            try
+            {
+                Actions actions = (Actions) m_actions.get();
+                actions.set(Actions.OPEN_URLCONNECTION_ACTION, url);
+                return (URLConnection) AccessController.doPrivileged(actions,
+                    m_acc);
+            }
+            catch (PrivilegedActionException ex)
+            {
+                if (ex.getException() instanceof IOException)
+                {
+                    throw (IOException) ex.getException();
+                }
+                throw (RuntimeException) ex.getException();
+            }
+        }
+        else
+        {
+            return url.openConnection();
+        }
+    }
+
+    public ZipFile openZipFile(File file) throws IOException
+    {
+        if (System.getSecurityManager() != null)
+        {
+            try
+            {
+                Actions actions = (Actions) m_actions.get();
+                actions.set(Actions.OPEN_ZIPFILE_ACTION, file);
+                return (ZipFile) AccessController.doPrivileged(actions, m_acc);
+            }
+            catch (PrivilegedActionException ex)
+            {
+                if (ex.getException() instanceof IOException)
+                {
+                    throw (IOException) ex.getException();
+                }
+                throw (RuntimeException) ex.getException();
+            }
+        }
+        else
+        {
+            return new ZipFile(file);
+        }
+    }
+
+    public void startActivator(BundleActivator activator, BundleContext context)
+        throws Exception
+    {
+        if (System.getSecurityManager() != null)
+        {
+            try
+            {
+                Actions actions = (Actions) m_actions.get();
+                actions.set(Actions.START_ACTIVATOR_ACTION, activator, context);
+                AccessController.doPrivileged(actions, m_acc);
+            }
+            catch (PrivilegedActionException ex)
+            {
+                throw ex.getException();
+            }
+        }
+        else
+        {
+            activator.start(context);
+        }
+    }
+
+    public void stopActivator(BundleActivator activator, BundleContext context)
+        throws Exception
+    {
+        if (System.getSecurityManager() != null)
+        {
+            try
+            {
+                Actions actions = (Actions) m_actions.get();
+                actions.set(Actions.STOP_ACTIVATOR_ACTION, activator, context);
+                AccessController.doPrivileged(actions, m_acc);
+            }
+            catch (PrivilegedActionException ex)
+            {
+                throw ex.getException();
+            }
+        }
+        else
+        {
+            activator.stop(context);
+        }
+    }
+
+    public Policy getPolicy()
+    {
+        if (System.getSecurityManager() != null)
+        {
+            try
+            {
+                Actions actions = (Actions) m_actions.get();
+                actions.set(Actions.GET_POLICY_ACTION, null);
+                return (Policy) AccessController.doPrivileged(actions, m_acc);
+            }
+            catch (PrivilegedActionException ex)
+            {
+                throw (RuntimeException) ex.getException();
+            }
+        }
+        else
+        {
+            return Policy.getPolicy();
+        }
+    }
+
+    public void addURLToURLClassLoader(URL extension, ClassLoader loader) throws Exception
+    {
+        if (System.getSecurityManager() != null)
+        {
+            Actions actions = (Actions) m_actions.get();
+            actions.set(Actions.ADD_EXTENSION_URL_ACTION, extension, loader);
+            try
+            {
+                AccessController.doPrivileged(actions, m_acc);
+            }
+            catch (PrivilegedActionException e)
+            {
+                throw e.getException();
+            }
+        }
+        else
+        {
+            Method addURL =
+                URLClassLoader.class.getDeclaredMethod("addURL",
+                new Class[] {URL.class});
+            addURL.setAccessible(true);
+            addURL.invoke(loader, new Object[]{extension});
+        }
+    }
+
+    public Constructor getConstructor(Class target, Class[] types) throws Exception
+    {
+        if (System.getSecurityManager() != null)
+        {
+            Actions actions = (Actions) m_actions.get();
+            actions.set(Actions.GET_CONSTRUCTOR_ACTION, target, types);
+            try
+            {
+                return (Constructor) AccessController.doPrivileged(actions, m_acc);
+            }
+            catch (PrivilegedActionException e)
+            {
+                throw e.getException();
+            }
+        }
+        else
+        {
+            return target.getConstructor(types);
+        }
+    }
+
+    public Constructor getDeclaredConstructor(Class target, Class[] types) throws Exception
+    {
+        if (System.getSecurityManager() != null)
+        {
+            Actions actions = (Actions) m_actions.get();
+            actions.set(Actions.GET_DECLARED_CONSTRUCTOR_ACTION, target, types);
+            try
+            {
+                return (Constructor) AccessController.doPrivileged(actions, m_acc);
+            }
+            catch (PrivilegedActionException e)
+            {
+                throw e.getException();
+            }
+        }
+        else
+        {
+            return target.getDeclaredConstructor(types);
+        }
+    }
+
+    public Method getMethod(Class target, String method, Class[] types) throws Exception
+    {
+        if (System.getSecurityManager() != null)
+        {
+            Actions actions = (Actions) m_actions.get();
+            actions.set(Actions.GET_METHOD_ACTION, target, method, types);
+            try
+            {
+                return (Method) AccessController.doPrivileged(actions, m_acc);
+            }
+            catch (PrivilegedActionException e)
+            {
+                throw e.getException();
+            }
+        }
+        else
+        {
+            return target.getMethod(method, types);
+        }
+    }
+
+    public Method getDeclaredMethod(Class target, String method, Class[] types) throws Exception
+    {
+        if (System.getSecurityManager() != null)
+        {
+            Actions actions = (Actions) m_actions.get();
+            actions.set(Actions.GET_DECLARED_METHOD_ACTION, target, method, types);
+            try
+            {
+                return (Method) AccessController.doPrivileged(actions, m_acc);
+            }
+            catch (PrivilegedActionException e)
+            {
+                throw e.getException();
+            }
+        }
+        else
+        {
+            return target.getDeclaredMethod(method, types);
+        }
+    }
+
+    public void setAccesssible(AccessibleObject ao)
+    {
+        if (System.getSecurityManager() != null)
+        {
+            Actions actions = (Actions) m_actions.get();
+            actions.set(Actions.SET_ACCESSIBLE_ACTION, ao);
+            try
+            {
+                AccessController.doPrivileged(actions, m_acc);
+            }
+            catch (PrivilegedActionException e)
+            {
+                throw (RuntimeException) e.getException();
+            }
+        }
+        else
+        {
+            ao.setAccessible(true);
+        }
+    }
+
+    public Object invoke(Method method, Object target, Object[] params) throws Exception
+    {
+        if (System.getSecurityManager() != null)
+        {
+            Actions actions = (Actions) m_actions.get();
+            actions.set(Actions.INVOKE_METHOD_ACTION, method, target, params);
+            try
+            {
+                return AccessController.doPrivileged(actions, m_acc);
+            }
+            catch (PrivilegedActionException e)
+            {
+                throw e.getException();
+            }
+        }
+        else
+        {
+            method.setAccessible(true);
+            return method.invoke(target, params);
+        }
+    }
+
+    public Object invokeDirect(Method method, Object target, Object[] params) throws Exception
+    {
+        if (System.getSecurityManager() != null)
+        {
+            Actions actions = (Actions) m_actions.get();
+            actions.set(Actions.INVOKE_DIRECTMETHOD_ACTION, method, target, params);
+            try
+            {
+                return AccessController.doPrivileged(actions, m_acc);
+            }
+            catch (PrivilegedActionException e)
+            {
+                throw e.getException();
+            }
+        }
+        else
+        {
+            return method.invoke(target, params);
+        }
+    }
+
+    public Object invoke(Constructor constructor, Object[] params) throws Exception
+    {
+        if (System.getSecurityManager() != null)
+        {
+            Actions actions = (Actions) m_actions.get();
+            actions.set(Actions.INVOKE_CONSTRUCTOR_ACTION, constructor, params);
+            try
+            {
+                return AccessController.doPrivileged(actions, m_acc);
+            }
+            catch (PrivilegedActionException e)
+            {
+                throw e.getException();
+            }
+        }
+        else
+        {
+            return constructor.newInstance(params);
+        }
+    }
+
+    public Object getDeclaredField(Class targetClass, String name, Object target)
+        throws Exception
+    {
+        if (System.getSecurityManager() != null)
+        {
+            Actions actions = (Actions) m_actions.get();
+            actions.set(Actions.GET_FIELD_ACTION, targetClass, name, target);
+            try
+            {
+                return AccessController.doPrivileged(actions, m_acc);
+            }
+            catch (PrivilegedActionException e)
+            {
+                throw e.getException();
+            }
+        }
+        else
+        {
+            Field field = targetClass.getDeclaredField(name);
+            field.setAccessible(true);
+
+            return field.get(target);
+        }
+    }
+
+    public Object swapStaticFieldIfNotClass(Class targetClazz,
+        Class targetType, Class condition, String lockName) throws Exception
+    {
+        if (System.getSecurityManager() != null)
+        {
+            Actions actions = (Actions) m_actions.get();
+            actions.set(Actions.SWAP_FIELD_ACTION, targetClazz, targetType,
+                condition, lockName);
+            try
+            {
+                return AccessController.doPrivileged(actions, m_acc);
+            }
+            catch (PrivilegedActionException e)
+            {
+                throw e.getException();
+            }
+        }
+        else
+        {
+            return _swapStaticFieldIfNotClass(targetClazz, targetType,
+                condition, lockName);
+        }
+    }
+
+    private static Object _swapStaticFieldIfNotClass(Class targetClazz,
+        Class targetType, Class condition, String lockName) throws Exception
+    {
+        Object lock = null;
+        if (lockName != null)
+        {
+            try
+            {
+                Field lockField =
+                    targetClazz.getDeclaredField(lockName);
+                lockField.setAccessible(true);
+                lock = lockField.get(null);
+            }
+            catch (NoSuchFieldException ex)
+            {
+            }
+        }
+        if (lock == null)
+        {
+            lock = targetClazz;
+        }
+        synchronized (lock)
+        {
+            Field[] fields = targetClazz.getDeclaredFields();
+
+            Object result = null;
+            for (int i = 0; (i < fields.length) && (result == null); i++)
+            {
+                if (Modifier.isStatic(fields[i].getModifiers()) &&
+                    (fields[i].getType() == targetType))
+                {
+                    fields[i].setAccessible(true);
+
+                    result = fields[i].get(null);
+
+                    if (result != null)
+                    {
+                        if ((condition == null) ||
+                            !result.getClass().getName().equals(condition.getName()))
+                        {
+                            fields[i].set(null, null);
+                        }
+                    }
+                }
+            }
+            if (result != null)
+            {
+                if ((condition == null) || !result.getClass().getName().equals(condition.getName()))
+                {
+                    // reset cache
+                    for (int i = 0; i < fields.length; i++)
+                    {
+                        if (Modifier.isStatic(fields[i].getModifiers()) &&
+                            (fields[i].getType() == Hashtable.class))
+                        {
+                            fields[i].setAccessible(true);
+                            Hashtable cache = (Hashtable) fields[i].get(null);
+                            if (cache != null)
+                            {
+                                cache.clear();
+                            }
+                        }
+                    }
+                }
+                return result;
+            }
+        }
+        return null;
+    }
+
+    public void flush(Class targetClazz, Object lock) throws Exception
+    {
+        if (System.getSecurityManager() != null)
+        {
+            Actions actions = (Actions) m_actions.get();
+            actions.set(Actions.FLUSH_FIELD_ACTION, targetClazz, lock);
+            try
+            {
+                AccessController.doPrivileged(actions, m_acc);
+            }
+            catch (PrivilegedActionException e)
+            {
+                throw e.getException();
+            }
+        }
+        else
+        {
+            _flush(targetClazz, lock);
+        }
+    }
+
+    private static void _flush(Class targetClazz, Object lock) throws Exception
+    {
+        synchronized (lock)
+        {
+            Field[] fields = targetClazz.getDeclaredFields();
+            // reset cache
+            for (int i = 0; i < fields.length; i++)
+            {
+                if (Modifier.isStatic(fields[i].getModifiers()) &&
+                    ((fields[i].getType() == Hashtable.class) || (fields[i].getType() == HashMap.class)))
+                {
+                    fields[i].setAccessible(true);
+                    if (fields[i].getType() == Hashtable.class)
+                    {
+                        Hashtable cache = (Hashtable) fields[i].get(null);
+                        if (cache != null)
+                        {
+                            cache.clear();
+                        }
+                    }
+                    else
+                    {
+                        HashMap cache = (HashMap) fields[i].get(null);
+                        if (cache != null)
+                        {
+                            cache.clear();
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    public void invokeBundleCollisionHook(
+        org.osgi.framework.hooks.bundle.CollisionHook ch, int operationType,
+        Bundle targetBundle, Collection<Bundle> collisionCandidates)
+        throws Exception
+    {
+        if (System.getSecurityManager() != null)
+        {
+            Actions actions = (Actions) m_actions.get();
+            actions.set(Actions.INVOKE_BUNDLE_COLLISION_HOOK, ch, operationType, targetBundle, collisionCandidates);
+            try
+            {
+                AccessController.doPrivileged(actions, m_acc);
+            }
+            catch (PrivilegedActionException e)
+            {
+                throw e.getException();
+            }
+        }
+        else
+        {
+            ch.filterCollisions(operationType, targetBundle, collisionCandidates);
+        }
+    }
+
+    public void invokeBundleFindHook(
+        org.osgi.framework.hooks.bundle.FindHook fh,
+        BundleContext bc, Collection<Bundle> bundles)
+        throws Exception
+    {
+        if (System.getSecurityManager() != null)
+        {
+            Actions actions = (Actions) m_actions.get();
+            actions.set(Actions.INVOKE_BUNDLE_FIND_HOOK, fh, bc, bundles);
+            try
+            {
+                AccessController.doPrivileged(actions, m_acc);
+            }
+            catch (PrivilegedActionException e)
+            {
+                throw e.getException();
+            }
+        }
+        else
+        {
+            fh.find(bc, bundles);
+        }
+    }
+
+    public void invokeBundleEventHook(
+        org.osgi.framework.hooks.bundle.EventHook eh,
+        BundleEvent event, Collection<BundleContext> contexts)
+        throws Exception
+    {
+        if (System.getSecurityManager() != null)
+        {
+            Actions actions = (Actions) m_actions.get();
+            actions.set(Actions.INVOKE_BUNDLE_EVENT_HOOK, eh, event, contexts);
+            try
+            {
+                AccessController.doPrivileged(actions, m_acc);
+            }
+            catch (PrivilegedActionException e)
+            {
+                throw e.getException();
+            }
+        }
+        else
+        {
+            eh.event(event, contexts);
+        }
+    }
+
+    public void invokeWeavingHook(
+        org.osgi.framework.hooks.weaving.WeavingHook wh,
+        org.osgi.framework.hooks.weaving.WovenClass wc)
+        throws Exception
+    {
+        if (System.getSecurityManager() != null)
+        {
+            Actions actions = (Actions) m_actions.get();
+            actions.set(Actions.INVOKE_WEAVING_HOOK, wh, wc);
+            try
+            {
+                AccessController.doPrivileged(actions, m_acc);
+            }
+            catch (PrivilegedActionException e)
+            {
+                throw e.getException();
+            }
+        }
+        else
+        {
+            wh.weave(wc);
+        }
+    }
+
+    public void invokeServiceEventHook(
+        org.osgi.framework.hooks.service.EventHook eh,
+        ServiceEvent event, Collection<BundleContext> contexts)
+        throws Exception
+    {
+        if (System.getSecurityManager() != null)
+        {
+            Actions actions = (Actions) m_actions.get();
+            actions.set(Actions.INVOKE_SERVICE_EVENT_HOOK, eh, event, contexts);
+            try
+            {
+                AccessController.doPrivileged(actions, m_acc);
+            }
+            catch (PrivilegedActionException e)
+            {
+                throw e.getException();
+            }
+        }
+        else
+        {
+            eh.event(event, contexts);
+        }
+    }
+
+    public void invokeServiceFindHook(
+        org.osgi.framework.hooks.service.FindHook fh,
+        BundleContext context, String name, String filter,
+        boolean allServices, Collection<ServiceReference<?>> references)
+        throws Exception
+    {
+        if (System.getSecurityManager() != null)
+        {
+            Actions actions = (Actions) m_actions.get();
+            actions.set(
+                Actions.INVOKE_SERVICE_FIND_HOOK, fh, context, name, filter,
+                (allServices) ? Boolean.TRUE : Boolean.FALSE, references);
+            try
+            {
+                AccessController.doPrivileged(actions, m_acc);
+            }
+            catch (PrivilegedActionException e)
+            {
+                throw e.getException();
+            }
+        }
+        else
+        {
+            fh.find(context, name, filter, allServices, references);
+        }
+    }
+
+    public void invokeServiceListenerHookAdded(
+        org.osgi.framework.hooks.service.ListenerHook lh,
+        Collection<ListenerHook.ListenerInfo> listeners)
+        throws Exception
+    {
+        if (System.getSecurityManager() != null)
+        {
+            Actions actions = (Actions) m_actions.get();
+            actions.set(Actions.INVOKE_SERVICE_LISTENER_HOOK_ADDED, lh, listeners);
+            try
+            {
+                AccessController.doPrivileged(actions, m_acc);
+            }
+            catch (PrivilegedActionException e)
+            {
+                throw e.getException();
+            }
+        }
+        else
+        {
+            lh.added(listeners);
+        }
+    }
+
+    public void invokeServiceListenerHookRemoved(
+        org.osgi.framework.hooks.service.ListenerHook lh,
+        Collection<ListenerHook.ListenerInfo> listeners)
+        throws Exception
+    {
+        if (System.getSecurityManager() != null)
+        {
+            Actions actions = (Actions) m_actions.get();
+            actions.set(Actions.INVOKE_SERVICE_LISTENER_HOOK_REMOVED, lh, listeners);
+            try
+            {
+                AccessController.doPrivileged(actions, m_acc);
+            }
+            catch (PrivilegedActionException e)
+            {
+                throw e.getException();
+            }
+        }
+        else
+        {
+            lh.removed(listeners);
+        }
+    }
+
+    public void invokeServiceEventListenerHook(
+        org.osgi.framework.hooks.service.EventListenerHook elh,
+        ServiceEvent event,
+        Map<BundleContext, Collection<ListenerHook.ListenerInfo>> listeners)
+        throws Exception
+    {
+        if (System.getSecurityManager() != null)
+        {
+            Actions actions = (Actions) m_actions.get();
+            actions.set(Actions.INVOKE_SERVICE_EVENT_LISTENER_HOOK, elh, event, listeners);
+            try
+            {
+                AccessController.doPrivileged(actions, m_acc);
+            }
+            catch (PrivilegedActionException e)
+            {
+                throw e.getException();
+            }
+        }
+        else
+        {
+            elh.event(event, listeners);
+        }
+    }
+
+    public ResolverHook invokeResolverHookFactory(
+        org.osgi.framework.hooks.resolver.ResolverHookFactory rhf,
+        Collection<BundleRevision> triggers)
+        throws Exception
+    {
+        if (System.getSecurityManager() != null)
+        {
+            Actions actions = (Actions) m_actions.get();
+            actions.set(Actions.INVOKE_RESOLVER_HOOK_FACTORY, rhf, triggers);
+            try
+            {
+                return (ResolverHook) AccessController.doPrivileged(actions, m_acc);
+            }
+            catch (PrivilegedActionException e)
+            {
+                throw e.getException();
+            }
+        }
+        else
+        {
+            return rhf.begin(triggers);
+        }
+    }
+
+    public void invokeResolverHookResolvable(
+        org.osgi.framework.hooks.resolver.ResolverHook rh,
+        Collection<BundleRevision> candidates)
+        throws Exception
+    {
+        if (System.getSecurityManager() != null)
+        {
+            Actions actions = (Actions) m_actions.get();
+            actions.set(Actions.INVOKE_RESOLVER_HOOK_RESOLVABLE, rh, candidates);
+            try
+            {
+                AccessController.doPrivileged(actions, m_acc);
+            }
+            catch (PrivilegedActionException e)
+            {
+                throw e.getException();
+            }
+        }
+        else
+        {
+            rh.filterResolvable(candidates);
+        }
+    }
+
+    public void invokeResolverHookSingleton(
+        org.osgi.framework.hooks.resolver.ResolverHook rh,
+        BundleCapability singleton,
+        Collection<BundleCapability> collisions)
+        throws Exception
+    {
+        if (System.getSecurityManager() != null)
+        {
+            Actions actions = (Actions) m_actions.get();
+            actions.set(Actions.INVOKE_RESOLVER_HOOK_SINGLETON, rh, singleton, collisions);
+            try
+            {
+                AccessController.doPrivileged(actions, m_acc);
+            }
+            catch (PrivilegedActionException e)
+            {
+                throw e.getException();
+            }
+        }
+        else
+        {
+            rh.filterSingletonCollisions(singleton, collisions);
+        }
+    }
+
+    public void invokeResolverHookMatches(
+        org.osgi.framework.hooks.resolver.ResolverHook rh,
+        BundleRequirement req,
+        Collection<BundleCapability> candidates)
+        throws Exception
+    {
+        if (System.getSecurityManager() != null)
+        {
+            Actions actions = (Actions) m_actions.get();
+            actions.set(Actions.INVOKE_RESOLVER_HOOK_MATCHES, rh, req, candidates);
+            try
+            {
+                AccessController.doPrivileged(actions, m_acc);
+            }
+            catch (PrivilegedActionException e)
+            {
+                throw e.getException();
+            }
+        }
+        else
+        {
+            rh.filterMatches(req, candidates);
+        }
+    }
+
+    public void invokeResolverHookEnd(
+        org.osgi.framework.hooks.resolver.ResolverHook rh)
+        throws Exception
+    {
+        if (System.getSecurityManager() != null)
+        {
+            Actions actions = (Actions) m_actions.get();
+            actions.set(Actions.INVOKE_RESOLVER_HOOK_END, rh);
+            try
+            {
+                AccessController.doPrivileged(actions, m_acc);
+            }
+            catch (PrivilegedActionException e)
+            {
+                throw e.getException();
+            }
+        }
+        else
+        {
+            rh.end();
+        }
+    }
+
+    private static class Actions implements PrivilegedExceptionAction
+    {
+        public static final int INITIALIZE_CONTEXT_ACTION = 0;
+        public static final int ADD_EXTENSION_URL_ACTION = 1;
+        public static final int CREATE_TMPFILE_ACTION = 2;
+        public static final int CREATE_URL_ACTION = 3;
+        public static final int CREATE_URL_WITH_CONTEXT_ACTION = 4;
+        public static final int DELETE_FILE_ACTION = 5;
+        public static final int EXEC_ACTION = 6;
+        public static final int FILE_EXISTS_ACTION = 7;
+        public static final int FILE_IS_DIRECTORY_ACTION = 8;
+        public static final int FOR_NAME_ACTION = 9;
+        public static final int GET_ABSOLUTE_PATH_ACTION = 10;
+        public static final int GET_CONSTRUCTOR_ACTION = 11;
+        public static final int GET_DECLARED_CONSTRUCTOR_ACTION = 12;
+        public static final int GET_DECLARED_METHOD_ACTION = 13;
+        public static final int GET_FIELD_ACTION = 14;
+        public static final int GET_FILE_INPUT_ACTION = 15;
+        public static final int GET_FILE_OUTPUT_ACTION = 16;
+        public static final int TO_URI_ACTION = 17;
+        public static final int GET_METHOD_ACTION = 18;
+        public static final int GET_POLICY_ACTION = 19;
+        public static final int GET_PROPERTY_ACTION = 20;
+        public static final int GET_PARENT_CLASS_LOADER_ACTION = 21;
+        public static final int GET_SYSTEM_CLASS_LOADER_ACTION = 22;
+        public static final int GET_URL_INPUT_ACTION = 23;
+        public static final int INVOKE_CONSTRUCTOR_ACTION = 24;
+        public static final int INVOKE_DIRECTMETHOD_ACTION = 25;
+        public static final int INVOKE_METHOD_ACTION = 26;
+        public static final int LIST_DIRECTORY_ACTION = 27;
+        public static final int MAKE_DIRECTORIES_ACTION = 28;
+        public static final int MAKE_DIRECTORY_ACTION = 29;
+        public static final int OPEN_ZIPFILE_ACTION = 30;
+        public static final int OPEN_URLCONNECTION_ACTION = 31;
+        public static final int RENAME_FILE_ACTION = 32;
+        public static final int SET_ACCESSIBLE_ACTION = 33;
+        public static final int START_ACTIVATOR_ACTION = 34;
+        public static final int STOP_ACTIVATOR_ACTION = 35;
+        public static final int SWAP_FIELD_ACTION = 36;
+        public static final int SYSTEM_EXIT_ACTION = 37;
+        public static final int FLUSH_FIELD_ACTION = 38;
+        public static final int GET_CLASS_LOADER_ACTION = 39;
+        public static final int INVOKE_BUNDLE_FIND_HOOK = 40;
+        public static final int INVOKE_BUNDLE_EVENT_HOOK = 41;
+        public static final int INVOKE_WEAVING_HOOK = 42;
+        public static final int INVOKE_SERVICE_EVENT_HOOK = 43;
+        public static final int INVOKE_SERVICE_FIND_HOOK = 44;
+        public static final int INVOKE_SERVICE_LISTENER_HOOK_ADDED = 45;
+        public static final int INVOKE_SERVICE_LISTENER_HOOK_REMOVED = 46;
+        public static final int INVOKE_SERVICE_EVENT_LISTENER_HOOK = 47;
+        public static final int INVOKE_RESOLVER_HOOK_FACTORY = 48;
+        public static final int INVOKE_RESOLVER_HOOK_RESOLVABLE = 49;
+        public static final int INVOKE_RESOLVER_HOOK_SINGLETON = 50;
+        public static final int INVOKE_RESOLVER_HOOK_MATCHES = 51;
+        public static final int INVOKE_RESOLVER_HOOK_END = 52;
+        public static final int INVOKE_BUNDLE_COLLISION_HOOK = 53;
+
+        private int m_action = -1;
+        private Object m_arg1 = null;
+        private Object m_arg2 = null;
+        private Object m_arg3 = null;
+        private Object m_arg4 = null;
+        private Object m_arg5 = null;
+        private Object m_arg6 = null;
+
+        public void set(int action)
+        {
+            m_action = action;
+        }
+
+        public void set(int action, Object arg1)
+        {
+            m_action = action;
+            m_arg1 = arg1;
+        }
+
+        public void set(int action, Object arg1, Object arg2)
+        {
+            m_action = action;
+            m_arg1 = arg1;
+            m_arg2 = arg2;
+        }
+
+        public void set(int action, Object arg1, Object arg2, Object arg3)
+        {
+            m_action = action;
+            m_arg1 = arg1;
+            m_arg2 = arg2;
+            m_arg3 = arg3;
+        }
+
+        public void set(int action, Object arg1, Object arg2, Object arg3,
+            Object arg4)
+        {
+            m_action = action;
+            m_arg1 = arg1;
+            m_arg2 = arg2;
+            m_arg3 = arg3;
+            m_arg4 = arg4;
+        }
+
+        public void set(int action, Object arg1, Object arg2, Object arg3,
+            Object arg4, Object arg5)
+        {
+            m_action = action;
+            m_arg1 = arg1;
+            m_arg2 = arg2;
+            m_arg3 = arg3;
+            m_arg4 = arg4;
+            m_arg5 = arg5;
+        }
+
+        public void set(int action, Object arg1, Object arg2, Object arg3,
+            Object arg4, Object arg5, Object arg6)
+        {
+            m_action = action;
+            m_arg1 = arg1;
+            m_arg2 = arg2;
+            m_arg3 = arg3;
+            m_arg4 = arg4;
+            m_arg5 = arg5;
+            m_arg6 = arg6;
+        }
+
+        private void unset()
+        {
+            m_action = -1;
+            m_arg1 = null;
+            m_arg2 = null;
+            m_arg3 = null;
+            m_arg4 = null;
+            m_arg5 = null;
+            m_arg6 = null;
+        }
+
+        public Object run() throws Exception
+        {
+            int action =  m_action;
+            Object arg1 = m_arg1;
+            Object arg2 = m_arg2;
+            Object arg3 = m_arg3;
+            Object arg4 = m_arg4;
+            Object arg5 = m_arg5;
+            Object arg6 = m_arg6;
+
+            unset();
+
+            switch (action)
+            {
+                case INITIALIZE_CONTEXT_ACTION:
+                    return AccessController.getContext();
+                case ADD_EXTENSION_URL_ACTION:
+                    Method addURL =
+                        URLClassLoader.class.getDeclaredMethod("addURL",
+                        new Class[] {URL.class});
+                    addURL.setAccessible(true);
+                    addURL.invoke(arg2, new Object[]{arg1});
+                    return null;
+                case CREATE_TMPFILE_ACTION:
+                    return File.createTempFile((String) arg1, (String) arg2, (File) arg3);
+                case CREATE_URL_ACTION:
+                    return new URL((String) arg1, (String) arg2,
+                        ((Integer) arg3).intValue(), (String) arg4,
+                        (URLStreamHandler) arg5);
+                case CREATE_URL_WITH_CONTEXT_ACTION:
+                    return new URL((URL) arg1, (String) arg2, (URLStreamHandler) arg3);
+                case DELETE_FILE_ACTION:
+                    return ((File) arg1).delete() ? Boolean.TRUE : Boolean.FALSE;
+                case EXEC_ACTION:
+                    return Runtime.getRuntime().exec((String) arg1);
+                case FILE_EXISTS_ACTION:
+                    return ((File) arg1).exists() ? Boolean.TRUE : Boolean.FALSE;
+                case FILE_IS_DIRECTORY_ACTION:
+                    return ((File) arg1).isDirectory() ? Boolean.TRUE : Boolean.FALSE;
+                case FOR_NAME_ACTION:
+                    return (arg2 == null) ? Class.forName((String) arg1) : Class.forName((String) arg1, true,
+                        (ClassLoader) arg2);
+                case GET_ABSOLUTE_PATH_ACTION:
+                    return ((File) arg1).getAbsolutePath();
+                case GET_CONSTRUCTOR_ACTION:
+                    return ((Class) arg1).getConstructor((Class[]) arg2);
+                case GET_DECLARED_CONSTRUCTOR_ACTION:
+                    return ((Class) arg1).getDeclaredConstructor((Class[]) arg2);
+                case GET_DECLARED_METHOD_ACTION:
+                    return ((Class) arg1).getDeclaredMethod((String) arg2, (Class[]) arg3);
+                case GET_FIELD_ACTION:
+                    Field field = ((Class) arg1).getDeclaredField((String) arg2);
+                    field.setAccessible(true);
+                    return field.get(arg3);
+                case GET_FILE_INPUT_ACTION:
+                    return new FileInputStream((File) arg1);
+                case GET_FILE_OUTPUT_ACTION:
+                    return new FileOutputStream((File) arg1);
+                case TO_URI_ACTION:
+                    return ((File) arg1).toURI();
+                case GET_METHOD_ACTION:
+                    return ((Class) arg1).getMethod((String) arg2, (Class[]) arg3);
+                case GET_POLICY_ACTION:
+                    return Policy.getPolicy();
+                case GET_PROPERTY_ACTION:
+                    return System.getProperty((String) arg1, (String) arg2);
+                case GET_PARENT_CLASS_LOADER_ACTION:
+                    return ((ClassLoader) arg1).getParent();
+                case GET_SYSTEM_CLASS_LOADER_ACTION:
+                    return ClassLoader.getSystemClassLoader();
+                case GET_URL_INPUT_ACTION:
+                    return ((URLConnection) arg1).getInputStream();
+                case INVOKE_CONSTRUCTOR_ACTION:
+                    return ((Constructor) arg1).newInstance((Object[]) arg2);
+                case INVOKE_DIRECTMETHOD_ACTION:
+                    return ((Method) arg1).invoke(arg2, (Object[]) arg3);
+                case INVOKE_METHOD_ACTION:
+                    ((Method) arg1).setAccessible(true);
+                    return ((Method) arg1).invoke(arg2, (Object[]) arg3);
+                case LIST_DIRECTORY_ACTION:
+                    return ((File) arg1).listFiles();
+                case MAKE_DIRECTORIES_ACTION:
+                    return ((File) arg1).mkdirs() ? Boolean.TRUE : Boolean.FALSE;
+                case MAKE_DIRECTORY_ACTION:
+                    return ((File) arg1).mkdir() ? Boolean.TRUE : Boolean.FALSE;
+                case OPEN_ZIPFILE_ACTION:
+                    return new ZipFile((File) arg1);
+                case OPEN_URLCONNECTION_ACTION:
+                    return ((URL) arg1).openConnection();
+                case RENAME_FILE_ACTION:
+                    return ((File) arg1).renameTo((File) arg2) ? Boolean.TRUE : Boolean.FALSE;
+                case SET_ACCESSIBLE_ACTION:
+                    ((AccessibleObject) arg1).setAccessible(true);
+                    return null;
+                case START_ACTIVATOR_ACTION:
+                    ((BundleActivator) arg1).start((BundleContext) arg2);
+                    return null;
+                case STOP_ACTIVATOR_ACTION:
+                    ((BundleActivator) arg1).stop((BundleContext) arg2);
+                    return null;
+                case SWAP_FIELD_ACTION:
+                    return _swapStaticFieldIfNotClass((Class) arg1,
+                        (Class) arg2, (Class) arg3, (String) arg4);
+                case SYSTEM_EXIT_ACTION:
+                    System.exit(((Integer) arg1).intValue());
+                case FLUSH_FIELD_ACTION:
+                    _flush(((Class) arg1), arg2);
+                    return null;
+                case GET_CLASS_LOADER_ACTION:
+                    return ((Class) arg1).getClassLoader();
+                case INVOKE_BUNDLE_FIND_HOOK:
+                    ((org.osgi.framework.hooks.bundle.FindHook) arg1).find(
+                        (BundleContext) arg2, (Collection<Bundle>) arg3);
+                    return null;
+                case INVOKE_BUNDLE_EVENT_HOOK:
+                    ((org.osgi.framework.hooks.bundle.EventHook) arg1).event(
+                        (BundleEvent) arg2, (Collection<BundleContext>) arg3);
+                    return null;
+                case INVOKE_WEAVING_HOOK:
+                    ((org.osgi.framework.hooks.weaving.WeavingHook) arg1).weave(
+                        (org.osgi.framework.hooks.weaving.WovenClass) arg2);
+                    return null;
+                case INVOKE_SERVICE_EVENT_HOOK:
+                    ((org.osgi.framework.hooks.service.EventHook) arg1).event(
+                        (ServiceEvent) arg2, (Collection<BundleContext>) arg3);
+                    return null;
+                case INVOKE_SERVICE_FIND_HOOK:
+                    ((org.osgi.framework.hooks.service.FindHook) arg1).find(
+                        (BundleContext) arg2, (String) arg3, (String) arg4,
+                        ((Boolean) arg5).booleanValue(),
+                        (Collection<ServiceReference<?>>) arg6);
+                    return null;
+                case INVOKE_SERVICE_LISTENER_HOOK_ADDED:
+                    ((org.osgi.framework.hooks.service.ListenerHook) arg1).added(
+                        (Collection<ListenerHook.ListenerInfo>) arg2);
+                    return null;
+                case INVOKE_SERVICE_LISTENER_HOOK_REMOVED:
+                    ((org.osgi.framework.hooks.service.ListenerHook) arg1).removed(
+                        (Collection<ListenerHook.ListenerInfo>) arg2);
+                    return null;
+                case INVOKE_SERVICE_EVENT_LISTENER_HOOK:
+                    ((org.osgi.framework.hooks.service.EventListenerHook) arg1).event(
+                        (ServiceEvent) arg2,
+                        (Map<BundleContext, Collection<ListenerHook.ListenerInfo>>) arg3);
+                    return null;
+                case INVOKE_RESOLVER_HOOK_FACTORY:
+                    return ((org.osgi.framework.hooks.resolver.ResolverHookFactory) arg1).begin(
+                        (Collection<BundleRevision>) arg2);
+                case INVOKE_RESOLVER_HOOK_RESOLVABLE:
+                    ((org.osgi.framework.hooks.resolver.ResolverHook) arg1).filterResolvable(
+                        (Collection<BundleRevision>) arg2);
+                    return null;
+                case INVOKE_RESOLVER_HOOK_SINGLETON:
+                    ((org.osgi.framework.hooks.resolver.ResolverHook) arg1)
+                        .filterSingletonCollisions(
+                            (BundleCapability) arg2,
+                            (Collection<BundleCapability>) arg3);
+                    return null;
+                case INVOKE_RESOLVER_HOOK_MATCHES:
+                    ((org.osgi.framework.hooks.resolver.ResolverHook) arg1).filterMatches(
+                        (BundleRequirement) arg2,
+                        (Collection<BundleCapability>) arg3);
+                    return null;
+                case INVOKE_RESOLVER_HOOK_END:
+                    ((org.osgi.framework.hooks.resolver.ResolverHook) arg1).end();
+                    return null;
+                case INVOKE_BUNDLE_COLLISION_HOOK:
+                    ((org.osgi.framework.hooks.bundle.CollisionHook) arg1).filterCollisions((Integer) arg2,
+                        (Bundle) arg3, (Collection<Bundle>) arg4);
+                    return null;
+            }
+
+            return null;
+        }
+    }
+}
diff --git a/src/main/java/org/apache/felix/framework/util/SecurityManagerEx.java b/src/main/java/org/apache/felix/framework/util/SecurityManagerEx.java
new file mode 100644
index 0000000..abe97ff
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/util/SecurityManagerEx.java
@@ -0,0 +1,33 @@
+/* 
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.util;
+
+/**
+ * <p>
+ * Simple utility class used to provide public access to the protected
+ * <tt>getClassContext()</tt> method of <tt>SecurityManager</tt>
+ * </p>
+**/
+public class SecurityManagerEx extends SecurityManager
+{
+    public Class[] getClassContext()
+    {
+        return super.getClassContext();
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/felix/framework/util/ShrinkableCollection.java b/src/main/java/org/apache/felix/framework/util/ShrinkableCollection.java
new file mode 100644
index 0000000..4d9bd96
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/util/ShrinkableCollection.java
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.util;
+
+import java.util.Collection;
+import java.util.Iterator;
+
+/**
+ * A collection wrapper that only permits clients to shrink the collection.
+**/
+public class ShrinkableCollection<T> implements Collection<T>
+{
+    private final Collection<T> m_delegate;
+
+    public ShrinkableCollection(Collection<T> delegate)
+    {
+        m_delegate = delegate;
+    }
+
+    public boolean add(T o)
+    {
+        throw new UnsupportedOperationException();
+    }
+
+    public boolean addAll(Collection<? extends T> c)
+    {
+        throw new UnsupportedOperationException();
+    }
+
+    public void clear()
+    {
+        m_delegate.clear();
+    }
+
+    public boolean contains(Object o)
+    {
+        return m_delegate.contains(o);
+    }
+
+    public boolean containsAll(Collection<?> c)
+    {
+        return m_delegate.containsAll(c);
+    }
+
+    @Override
+    public boolean equals(Object o)
+    {
+        return m_delegate.equals(o);
+    }
+
+    @Override
+    public int hashCode()
+    {
+        return m_delegate.hashCode();
+    }
+
+    public boolean isEmpty()
+    {
+        return m_delegate.isEmpty();
+    }
+
+    public Iterator iterator()
+    {
+        return m_delegate.iterator();
+    }
+
+    public boolean remove(Object o)
+    {
+        return m_delegate.remove(o);
+    }
+
+    public boolean removeAll(Collection<?> c)
+    {
+        return m_delegate.removeAll(c);
+    }
+
+    public boolean retainAll(Collection<?> c)
+    {
+        return m_delegate.retainAll(c);
+    }
+
+    public int size()
+    {
+        return m_delegate.size();
+    }
+
+    public Object[] toArray()
+    {
+        return m_delegate.toArray();
+    }
+
+    public <A> A[] toArray(A[] a)
+    {
+        return m_delegate.toArray(a);
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/felix/framework/util/ShrinkableMap.java b/src/main/java/org/apache/felix/framework/util/ShrinkableMap.java
new file mode 100644
index 0000000..0923f1d
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/util/ShrinkableMap.java
@@ -0,0 +1,94 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.util;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+public class ShrinkableMap<K, V> implements Map<K, V>
+{
+    private final Map<K, V> m_delegate;
+
+    public ShrinkableMap(Map<K, V> delegate)
+    {
+        m_delegate = delegate;
+    }
+
+    public int size()
+    {
+        return m_delegate.size();
+    }
+
+    public boolean isEmpty()
+    {
+        return m_delegate.isEmpty();
+    }
+
+    public boolean containsKey(Object o)
+    {
+        return m_delegate.containsKey(o);
+    }
+
+    public boolean containsValue(Object o)
+    {
+        return m_delegate.containsValue(o);
+    }
+
+    public V get(Object o)
+    {
+        return m_delegate.get(o);
+    }
+
+    public V put(K k, V v)
+    {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+    public V remove(Object o)
+    {
+        return m_delegate.remove(o);
+    }
+
+    public void putAll(Map<? extends K, ? extends V> map)
+    {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+    public void clear()
+    {
+        m_delegate.clear();
+    }
+
+    public Set<K> keySet()
+    {
+        return m_delegate.keySet();
+    }
+
+    public Collection<V> values()
+    {
+        return m_delegate.values();
+    }
+
+    public Set<Entry<K, V>> entrySet()
+    {
+        return m_delegate.entrySet();
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/felix/framework/util/StringComparator.java b/src/main/java/org/apache/felix/framework/util/StringComparator.java
new file mode 100644
index 0000000..03af305
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/util/StringComparator.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.util;
+
+import java.util.Comparator;
+
+public class StringComparator implements Comparator
+{
+    private final boolean m_isCaseSensitive;
+
+    public StringComparator(boolean b)
+    {
+        m_isCaseSensitive = b;
+    }
+
+    public int compare(Object o1, Object o2)
+    {
+        if (m_isCaseSensitive)
+        {
+            return o1.toString().compareTo(o2.toString());
+        }
+        else
+        {
+            return o1.toString().compareToIgnoreCase(o2.toString());
+        }
+    }
+
+    public boolean isCaseSensitive()
+    {
+        return m_isCaseSensitive;
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/felix/framework/util/StringMap.java b/src/main/java/org/apache/felix/framework/util/StringMap.java
new file mode 100644
index 0000000..39c2eb6
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/util/StringMap.java
@@ -0,0 +1,217 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.util;
+
+import java.util.*;
+
+/**
+ * Simple utility class that creates a map for string-based keys.
+ * This map can be set to use case-sensitive or case-insensitive
+ * comparison when searching for the key.  Any keys put into this
+ * map will be converted to a <tt>String</tt> using the
+ * <tt>toString()</tt> method, since it is only intended to
+ * compare strings.
+ **/
+public class StringMap extends AbstractMap<String, Object>
+{
+    private static final CharArrayComparator COMPARATOR = new CharArrayComparator();
+
+    private final TreeMap<char[], KeyValueEntry> m_map = new TreeMap<char[], KeyValueEntry>(COMPARATOR);
+
+    public StringMap()
+    {
+    }
+
+    public StringMap(Map<? extends Object, ? extends Object> map)
+    {
+        for (Map.Entry<? extends Object, ? extends Object> e : map.entrySet())
+        {
+            KeyValueEntry kve = (KeyValueEntry) m_map.put(
+                toUpperCase(e.getKey().toString()),
+                new KeyValueEntry(e.getKey().toString(), e.getValue()));
+        }
+    }
+
+    @Override
+    public int size()
+    {
+        return m_map.size();
+    }
+
+    @Override
+    public boolean isEmpty()
+    {
+        return m_map.isEmpty();
+    }
+
+    @Override
+    public boolean containsKey(Object arg0)
+    {
+        return m_map.containsKey(toUpperCase(arg0.toString()));
+    }
+
+    @Override
+    public boolean containsValue(Object arg0)
+    {
+        return m_map.containsValue(arg0);
+    }
+
+    @Override
+    public Object get(Object arg0)
+    {
+        KeyValueEntry kve = m_map.get(toUpperCase(arg0.toString()));
+        return (kve != null) ? kve.value : null;
+    }
+
+    @Override
+    public Object put(String key, Object value)
+    {
+        KeyValueEntry kve = (KeyValueEntry) m_map.put(toUpperCase(key), new KeyValueEntry(key, value));
+        return (kve != null) ? kve.value : null;
+    }
+
+    @Override
+    public void putAll(Map<? extends String, ? extends Object> map)
+    {
+        for (Map.Entry<? extends String, ? extends Object> e : map.entrySet())
+        {
+            put(e.getKey().toString(), e.getValue());
+        }
+    }
+
+    @Override
+    public Object remove(Object arg0)
+    {
+        KeyValueEntry kve = m_map.remove(toUpperCase(arg0.toString()));
+        return (kve != null) ? kve.value : null;
+    }
+
+    @Override
+    public void clear()
+    {
+        m_map.clear();
+    }
+
+    public Set<Entry<String, Object>> entrySet()
+    {
+        return new AbstractSet<Entry<String, Object>>()
+        {
+            @Override
+            public Iterator<Entry<String, Object>> iterator()
+            {
+                return new Iterator<Entry<String, Object>>()
+                {
+                    Iterator<Entry<char[], KeyValueEntry>> it = m_map.entrySet().iterator();
+
+                    public boolean hasNext()
+                    {
+                        return it.hasNext();
+                    }
+
+                    public Entry<String, Object> next()
+                    {
+                        return it.next().getValue();
+                    }
+
+                    public void remove()
+                    {
+                        throw new UnsupportedOperationException();
+                    }
+                };
+            }
+
+            @Override
+            public int size()
+            {
+                return m_map.size();
+            }
+        };
+    }
+
+    private static char[] toUpperCase(String str)
+    {
+        char[] ch = str.toCharArray();
+        for (int i = 0; i < ch.length; i++)
+        {
+            char c = ch[i];
+            if (c < 128)
+            {
+                if ('a' <= c && c <= 'z')
+                {
+                    ch[i] = (char)(c - ('a' - 'A'));
+                }
+            }
+            else
+            {
+                ch[i] = Character.toUpperCase(c);
+            }
+        }
+        return ch;
+    }
+
+    private static class CharArrayComparator implements Comparator<char[]>
+    {
+        public int compare(char[] v1, char[] v2)
+        {
+            int len1 = v1.length;
+            int len2 = v2.length;
+            int n = Math.min(len1, len2);
+            int k = 0;
+            while (k < n)
+            {
+                char c1 = v1[k];
+                char c2 = v2[k];
+                if (c1 != c2)
+                {
+                    return c1 - c2;
+                }
+                k++;
+            }
+            return len1 - len2;
+        }
+    }
+
+    private static class KeyValueEntry implements Map.Entry<String, Object>
+    {
+        private KeyValueEntry(String key, Object value)
+        {
+            this.key = key;
+            this.value = value;
+        }
+
+        public String getKey()
+        {
+            return key;
+        }
+
+        public Object getValue()
+        {
+            return value;
+        }
+
+        public Object setValue(Object value)
+        {
+            Object v = this.value;
+            this.value = value;
+            return v;
+        }
+        String key;
+        Object value;
+    }
+}
diff --git a/src/main/java/org/apache/felix/framework/util/ThreadGate.java b/src/main/java/org/apache/felix/framework/util/ThreadGate.java
new file mode 100644
index 0000000..f9900a3
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/util/ThreadGate.java
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.util;
+
+/**
+ * This class implements a simple one-shot gate for threads. The gate
+ * starts closed and will block any threads that try to wait on it. Once
+ * opened, all waiting threads will be released. The gate cannot be reused.
+**/
+public class ThreadGate
+{
+    private boolean m_open = false;
+    private Object m_msg = null;
+    private boolean m_initialized = false;
+
+    /**
+     * Open the gate and release any waiting threads.
+    **/
+    public synchronized void open()
+    {
+        m_open = true;
+        notifyAll();
+    }
+
+    /**
+     * Returns the message object associated with the gate; the
+     * message is just an arbitrary object used to pass information
+     * to the waiting threads.
+     * @return the message object associated with the gate.
+    **/
+    public synchronized Object getMessage()
+    {
+        return m_msg;
+    }
+
+    /**
+     * Sets the message object associated with the gate. The message
+     * object can only be set once, subsequent calls to this method
+     * are ignored.
+     * @param msg the message object to associate with this gate.
+    **/
+    public synchronized void setMessage(Object msg)
+    {
+        if (!m_initialized)
+        {
+            m_msg = msg;
+            m_initialized = true;
+        }
+    }
+
+    /**
+     * Wait for the gate to open.
+     * @return <tt>true</tt> if the gate was opened or <tt>false</tt> if the timeout expired.
+     * @throws java.lang.InterruptedException If the calling thread is interrupted;
+     *         the gate still remains closed until opened.
+    **/
+    public synchronized boolean await(long timeout) throws InterruptedException
+    {
+        long start = System.currentTimeMillis();
+        long remaining = timeout;
+        while (!m_open)
+        {
+            wait(remaining);
+            if (timeout > 0)
+            {
+                remaining = timeout - (System.currentTimeMillis() - start);
+                if (remaining <= 0)
+                {
+                    break;
+                }
+            }
+        }
+        return m_open;
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/felix/framework/util/Util.java b/src/main/java/org/apache/felix/framework/util/Util.java
new file mode 100644
index 0000000..ac9500b
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/util/Util.java
@@ -0,0 +1,789 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.util;
+
+import java.io.*;
+import java.net.URL;
+
+import java.security.SecureRandom;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Properties;
+import org.apache.felix.framework.Logger;
+import org.apache.felix.framework.capabilityset.CapabilitySet;
+import org.apache.felix.framework.wiring.BundleCapabilityImpl;
+import org.apache.felix.framework.wiring.BundleRequirementImpl;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.wiring.BundleCapability;
+import org.osgi.framework.wiring.BundleRequirement;
+import org.osgi.framework.wiring.BundleRevision;
+import org.osgi.framework.wiring.BundleWire;
+import org.osgi.framework.wiring.BundleWiring;
+
+public class Util
+{
+    /**
+     * The default name used for the default configuration properties file.
+    **/
+    private static final String DEFAULT_PROPERTIES_FILE = "default.properties";
+
+    public static String getDefaultProperty(Logger logger, String name)
+    {
+        String value = null;
+
+        URL propURL = Util.class.getClassLoader().getResource(DEFAULT_PROPERTIES_FILE);
+        if (propURL != null)
+        {
+            InputStream is = null;
+            try
+            {
+                // Load properties from URL.
+                is = propURL.openConnection().getInputStream();
+                Properties props = new Properties();
+                props.load(is);
+                is.close();
+                // Perform variable substitution for property.
+                value = props.getProperty(name);
+                value = (value != null)
+                    ? Util.substVars(value, name, null, props)
+                    : null;
+            }
+            catch (Exception ex)
+            {
+                // Try to close input stream if we have one.
+                try
+                {
+                    if (is != null) is.close();
+                }
+                catch (IOException ex2)
+                {
+                    // Nothing we can do.
+                }
+
+                logger.log(
+                    Logger.LOG_ERROR, "Unable to load any configuration properties.", ex);
+            }
+        }
+        return value;
+    }
+
+    /**
+     * Converts a revision identifier to a bundle identifier. Revision IDs
+     * are typically <tt><bundle-id>.<revision></tt>; this
+     * method returns only the portion corresponding to the bundle ID.
+    **/
+    public static long getBundleIdFromRevisionId(String id)
+    {
+        try
+        {
+            String bundleId = (id.indexOf('.') >= 0)
+                ? id.substring(0, id.indexOf('.')) : id;
+            return Long.parseLong(bundleId);
+        }
+        catch (NumberFormatException ex)
+        {
+            return -1;
+        }
+    }
+
+    /**
+     * Converts a module identifier to a bundle identifier. Module IDs
+     * are typically <tt><bundle-id>.<revision></tt>; this
+     * method returns only the portion corresponding to the revision.
+    **/
+    public static int getModuleRevisionFromModuleId(String id)
+    {
+        try
+        {
+            int index = id.indexOf('.');
+            if (index >= 0)
+            {
+                return Integer.parseInt(id.substring(index + 1));
+            }
+        }
+        catch (NumberFormatException ex)
+        {
+        }
+        return -1;
+    }
+
+    public static String getClassName(String className)
+    {
+        if (className == null)
+        {
+            className = "";
+        }
+        return (className.lastIndexOf('.') < 0)
+            ? "" : className.substring(className.lastIndexOf('.') + 1);
+    }
+
+    public static String getClassPackage(String className)
+    {
+        if (className == null)
+        {
+            className = "";
+        }
+        return (className.lastIndexOf('.') < 0)
+            ? "" : className.substring(0, className.lastIndexOf('.'));
+    }
+
+    public static String getResourcePackage(String resource)
+    {
+        if (resource == null)
+        {
+            resource = "";
+        }
+        // NOTE: The package of a resource is tricky to determine since
+        // resources do not follow the same naming conventions as classes.
+        // This code is pessimistic and assumes that the package of a
+        // resource is everything up to the last '/' character. By making
+        // this choice, it will not be possible to load resources from
+        // imports using relative resource names. For example, if a
+        // bundle exports "foo" and an importer of "foo" tries to load
+        // "/foo/bar/myresource.txt", this will not be found in the exporter
+        // because the following algorithm assumes the package name is
+        // "foo.bar", not just "foo". This only affects imported resources,
+        // local resources will work as expected.
+        String pkgName = (resource.startsWith("/")) ? resource.substring(1) : resource;
+        pkgName = (pkgName.lastIndexOf('/') < 0)
+            ? "" : pkgName.substring(0, pkgName.lastIndexOf('/'));
+        pkgName = pkgName.replace('/', '.');
+        return pkgName;
+    }
+
+    /**
+     * <p>
+     * This is a simple utility class that attempts to load the named
+     * class using the class loader of the supplied class or
+     * the class loader of one of its super classes or their implemented
+     * interfaces. This is necessary during service registration to test
+     * whether a given service object implements its declared service
+     * interfaces.
+     * </p>
+     * <p>
+     * To perform this test, the framework must try to load
+     * the classes associated with the declared service interfaces, so
+     * it must choose a class loader. The class loader of the registering
+     * bundle cannot be used, since this disallows third parties to
+     * register service on behalf of another bundle. Consequently, the
+     * class loader of the service object must be used. However, this is
+     * also not sufficient since the class loader of the service object
+     * may not have direct access to the class in question.
+     * </p>
+     * <p>
+     * The service object's class loader may not have direct access to
+     * its service interface if it extends a super class from another
+     * bundle which implements the service interface from an imported
+     * bundle or if it implements an extension of the service interface
+     * from another bundle which imports the base interface from another
+     * bundle. In these cases, the service object's class loader only has
+     * access to the super class's class or the extended service interface,
+     * respectively, but not to the actual service interface.
+     * </p>
+     * <p>
+     * Thus, it is necessary to not only try to load the service interface
+     * class from the service object's class loader, but from the class
+     * loaders of any interfaces it implements and the class loaders of
+     * all super classes.
+     * </p>
+     * @param svcObj the class that is the root of the search.
+     * @param name the name of the class to load.
+     * @return the loaded class or <tt>null</tt> if it could not be
+     *         loaded.
+    **/
+    public static Class loadClassUsingClass(Class clazz, String name, SecureAction action)
+    {
+        Class loadedClass = null;
+
+        while (clazz != null)
+        {
+            // Get the class loader of the current class object.
+            ClassLoader loader = action.getClassLoader(clazz);
+            // A null class loader represents the system class loader.
+            loader = (loader == null) ? action.getSystemClassLoader() : loader;
+            try
+            {
+                return loader.loadClass(name);
+            }
+            catch (ClassNotFoundException ex)
+            {
+                // Ignore and try interface class loaders.
+            }
+
+            // Try to see if we can load the class from
+            // one of the class's implemented interface
+            // class loaders.
+            Class[] ifcs = clazz.getInterfaces();
+            for (int i = 0; i < ifcs.length; i++)
+            {
+                loadedClass = loadClassUsingClass(ifcs[i], name, action);
+                if (loadedClass != null)
+                {
+                    return loadedClass;
+                }
+            }
+
+            // Try to see if we can load the class from
+            // the super class class loader.
+            clazz = clazz.getSuperclass();
+        }
+
+        return null;
+    }
+
+    /**
+     * This method determines if the requesting bundle is able to cast
+     * the specified service reference based on class visibility rules
+     * of the underlying modules.
+     * @param requester The bundle requesting the service.
+     * @param ref The service in question.
+     * @return <tt>true</tt> if the requesting bundle is able to case
+     *         the service object to a known type.
+    **/
+    public static boolean isServiceAssignable(Bundle requester, ServiceReference ref)
+    {
+        // Boolean flag.
+        boolean allow = true;
+        // Get the service's objectClass property.
+        String[] objectClass = (String[]) ref.getProperty(FelixConstants.OBJECTCLASS);
+
+        // The the service reference is not assignable when the requesting
+        // bundle is wired to a different version of the service object.
+        // NOTE: We are pessimistic here, if any class in the service's
+        // objectClass is not usable by the requesting bundle, then we
+        // disallow the service reference.
+        for (int classIdx = 0; (allow) && (classIdx < objectClass.length); classIdx++)
+        {
+            if (!ref.isAssignableTo(requester, objectClass[classIdx]))
+            {
+                allow = false;
+            }
+        }
+        return allow;
+    }
+
+    /**
+     * Returns all the capabilities from a module that has a specified namespace.
+     *
+     * @param br    module providing capabilities
+     * @param namespace capability namespace
+     * @return array of matching capabilities or empty if none found
+     */
+    public static List<BundleCapability> getCapabilityByNamespace(
+        BundleRevision br, String namespace)
+    {
+        final List<BundleCapability> matching = new ArrayList();
+        final List<BundleCapability> caps = (br.getWiring() != null)
+            ? br.getWiring().getCapabilities(null)
+            : br.getDeclaredCapabilities(null);
+        if (caps != null)
+        {
+            for (BundleCapability cap : caps)
+            {
+                if (cap.getNamespace().equals(namespace))
+                {
+                    matching.add(cap);
+                }
+            }
+        }
+        return matching;
+    }
+
+    public static List<BundleRequirement> getDynamicRequirements(
+        List<BundleRequirement> reqs)
+    {
+        List<BundleRequirement> result = new ArrayList<BundleRequirement>();
+        if (reqs != null)
+        {
+            for (BundleRequirement req : reqs)
+            {
+                String resolution = req.getDirectives().get(Constants.RESOLUTION_DIRECTIVE);
+                if ((resolution != null) && resolution.equals("dynamic"))
+                {
+                    result.add(req);
+                }
+            }
+        }
+        return result;
+    }
+
+    public static BundleWire getWire(BundleRevision br, String name)
+    {
+        if (br.getWiring() != null)
+        {
+            List<BundleWire> wires = br.getWiring().getRequiredWires(null);
+            if (wires != null)
+            {
+                for (BundleWire w : wires)
+                {
+                    if (w.getCapability().getNamespace()
+                            .equals(BundleRevision.PACKAGE_NAMESPACE) &&
+                            w.getCapability().getAttributes()
+                                    .get(BundleRevision.PACKAGE_NAMESPACE).equals(name))
+                    {
+                        return w;
+                    }
+                }
+            }
+        }
+        return null;
+    }
+
+    public static BundleCapability getPackageCapability(BundleRevision br, String name)
+    {
+        if (br.getWiring() != null)
+        {
+            List<BundleCapability> capabilities = br.getWiring().getCapabilities(null);
+            if (capabilities != null)
+            {
+                for (BundleCapability c : capabilities)
+                {
+                    if (c.getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE)
+                        && c.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE).equals(name))
+                    {
+                        return c;
+                    }
+                }
+            }
+        }
+        return null;
+    }
+
+    private static final byte encTab[] = { 0x41, 0x42, 0x43, 0x44, 0x45, 0x46,
+        0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52,
+        0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x61, 0x62, 0x63, 0x64,
+        0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70,
+        0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x30, 0x31,
+        0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x2b, 0x2f };
+
+    private static final byte decTab[] = { -1, -1, -1, -1, -1, -1, -1, -1, -1,
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1,
+        -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1,
+        -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
+        18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29,
+        30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
+        48, 49, 50, 51, -1, -1, -1, -1, -1 };
+
+    public static String base64Encode(String s) throws IOException
+    {
+        return encode(s.getBytes(), 0);
+    }
+
+    /**
+     * Encode a raw byte array to a Base64 String.
+     *
+     * @param in Byte array to encode.
+     * @param len Length of Base64 lines. 0 means no line breaks.
+    **/
+    public static String encode(byte[] in, int len) throws IOException
+    {
+        ByteArrayOutputStream baos = null;
+        ByteArrayInputStream bais = null;
+        try
+        {
+            baos = new ByteArrayOutputStream();
+            bais = new ByteArrayInputStream(in);
+            encode(bais, baos, len);
+            // ASCII byte array to String
+            return (new String(baos.toByteArray()));
+        }
+        finally
+        {
+            if (baos != null)
+            {
+                baos.close();
+            }
+            if (bais != null)
+            {
+                bais.close();
+            }
+        }
+    }
+
+    public static void encode(InputStream in, OutputStream out, int len)
+        throws IOException
+    {
+
+        // Check that length is a multiple of 4 bytes
+        if (len % 4 != 0)
+        {
+            throw new IllegalArgumentException("Length must be a multiple of 4");
+        }
+
+        // Read input stream until end of file
+        int bits = 0;
+        int nbits = 0;
+        int nbytes = 0;
+        int b;
+
+        while ((b = in.read()) != -1)
+        {
+            bits = (bits << 8) | b;
+            nbits += 8;
+            while (nbits >= 6)
+            {
+                nbits -= 6;
+                out.write(encTab[0x3f & (bits >> nbits)]);
+                nbytes++;
+                // New line
+                if (len != 0 && nbytes >= len)
+                {
+                    out.write(0x0d);
+                    out.write(0x0a);
+                    nbytes -= len;
+                }
+            }
+        }
+
+        switch (nbits)
+        {
+            case 2:
+                out.write(encTab[0x3f & (bits << 4)]);
+                out.write(0x3d); // 0x3d = '='
+                out.write(0x3d);
+                break;
+            case 4:
+                out.write(encTab[0x3f & (bits << 2)]);
+                out.write(0x3d);
+                break;
+        }
+
+        if (len != 0)
+        {
+            if (nbytes != 0)
+            {
+                out.write(0x0d);
+                out.write(0x0a);
+            }
+            out.write(0x0d);
+            out.write(0x0a);
+        }
+    }
+
+
+    private static final String DELIM_START = "${";
+    private static final String DELIM_STOP  = "}";
+
+    /**
+     * <p>
+     * This method performs property variable substitution on the
+     * specified value. If the specified value contains the syntax
+     * <tt>${<prop-name>}</tt>, where <tt><prop-name></tt>
+     * refers to either a configuration property or a system property,
+     * then the corresponding property value is substituted for the variable
+     * placeholder. Multiple variable placeholders may exist in the
+     * specified value as well as nested variable placeholders, which
+     * are substituted from inner most to outer most. Configuration
+     * properties override system properties.
+     * </p>
+     * @param val The string on which to perform property substitution.
+     * @param currentKey The key of the property being evaluated used to
+     *        detect cycles.
+     * @param cycleMap Map of variable references used to detect nested cycles.
+     * @param configProps Set of configuration properties.
+     * @return The value of the specified string after system property substitution.
+     * @throws IllegalArgumentException If there was a syntax error in the
+     *         property placeholder syntax or a recursive variable reference.
+    **/
+    public static String substVars(String val, String currentKey,
+        Map cycleMap, Properties configProps)
+        throws IllegalArgumentException
+    {
+        // If there is currently no cycle map, then create
+        // one for detecting cycles for this invocation.
+        if (cycleMap == null)
+        {
+            cycleMap = new HashMap();
+        }
+
+        // Put the current key in the cycle map.
+        cycleMap.put(currentKey, currentKey);
+
+        // Assume we have a value that is something like:
+        // "leading ${foo.${bar}} middle ${baz} trailing"
+
+        // Find the first ending '}' variable delimiter, which
+        // will correspond to the first deepest nested variable
+        // placeholder.
+        int stopDelim = -1;
+        int startDelim = -1;
+
+        do
+        {
+            stopDelim = val.indexOf(DELIM_STOP, stopDelim + 1);
+            // If there is no stopping delimiter, then just return
+            // the value since there is no variable declared.
+            if (stopDelim < 0)
+            {
+                return val;
+            }
+            // Try to find the matching start delimiter by
+            // looping until we find a start delimiter that is
+            // greater than the stop delimiter we have found.
+            startDelim = val.indexOf(DELIM_START);
+            // If there is no starting delimiter, then just return
+            // the value since there is no variable declared.
+            if (startDelim < 0)
+            {
+                return val;
+            }
+            while (stopDelim >= 0)
+            {
+                int idx = val.indexOf(DELIM_START, startDelim + DELIM_START.length());
+                if ((idx < 0) || (idx > stopDelim))
+                {
+                    break;
+                }
+                else if (idx < stopDelim)
+                {
+                    startDelim = idx;
+                }
+            }
+        }
+        while ((startDelim > stopDelim) && (stopDelim >= 0));
+
+        // At this point, we have found a variable placeholder so
+        // we must perform a variable substitution on it.
+        // Using the start and stop delimiter indices, extract
+        // the first, deepest nested variable placeholder.
+        String variable =
+            val.substring(startDelim + DELIM_START.length(), stopDelim);
+
+        // Verify that this is not a recursive variable reference.
+        if (cycleMap.get(variable) != null)
+        {
+            throw new IllegalArgumentException(
+                "recursive variable reference: " + variable);
+        }
+
+        // Get the value of the deepest nested variable placeholder.
+        // Try to configuration properties first.
+        String substValue = (configProps != null)
+            ? configProps.getProperty(variable, null)
+            : null;
+        if (substValue == null)
+        {
+            // Ignore unknown property values.
+            substValue = System.getProperty(variable, "");
+        }
+
+        // Remove the found variable from the cycle map, since
+        // it may appear more than once in the value and we don't
+        // want such situations to appear as a recursive reference.
+        cycleMap.remove(variable);
+
+        // Append the leading characters, the substituted value of
+        // the variable, and the trailing characters to get the new
+        // value.
+        val = val.substring(0, startDelim)
+            + substValue
+            + val.substring(stopDelim + DELIM_STOP.length(), val.length());
+
+        // Now perform substitution again, since there could still
+        // be substitutions to make.
+        val = substVars(val, currentKey, cycleMap, configProps);
+
+        // Return the value.
+        return val;
+    }
+
+    /**
+     * Returns true if the specified bundle revision is a singleton
+     * (i.e., directive singleton:=true in Bundle-SymbolicName).
+     *
+     * @param revision the revision to check for singleton status.
+     * @return true if the revision is a singleton, false otherwise.
+    **/
+    public static boolean isSingleton(BundleRevision revision)
+    {
+        final List<BundleCapability> caps = revision.getDeclaredCapabilities(null);
+        for (BundleCapability cap : caps)
+        {
+            // Find the bundle capability and check its directives.
+            if (cap.getNamespace().equals(BundleRevision.BUNDLE_NAMESPACE))
+            {
+                for (Entry<String, String> entry : cap.getDirectives().entrySet())
+                {
+                    if (entry.getKey().equalsIgnoreCase(Constants.SINGLETON_DIRECTIVE))
+                    {
+                        return Boolean.valueOf((String) entry.getValue());
+                    }
+                }
+                // Can only have one bundle capability, so break.
+                break;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Checks if the provided module definition declares a fragment host.
+     *
+     * @param module the module to check
+     * @return <code>true</code> if the module declares a fragment host, <code>false</code>
+     *      otherwise.
+     */
+    public static boolean isFragment(BundleRevision revision)
+    {
+        return ((revision.getTypes() & BundleRevision.TYPE_FRAGMENT) > 0);
+    }
+
+    public static List<BundleRevision> getFragments(BundleWiring wiring)
+    {
+        List<BundleRevision> fragments = Collections.EMPTY_LIST;
+        if (wiring != null)
+        {
+            List<BundleWire> wires = wiring.getProvidedWires(null);
+            if (wires != null)
+            {
+                for (BundleWire w : wires)
+                {
+                    if (w.getCapability().getNamespace()
+                        .equals(BundleRevision.HOST_NAMESPACE))
+                    {
+                        // Create array list if needed.
+                        if (fragments.isEmpty())
+                        {
+                            fragments = new ArrayList<BundleRevision>();
+                        }
+                        fragments.add(w.getRequirerWiring().getRevision());
+                    }
+                }
+            }
+        }
+        return fragments;
+    }
+
+    //
+    // UUID code copied from Apache Harmony java.util.UUID
+    //
+
+    /**
+     * <p>
+     * Generates a variant 2, version 4 (randomly generated number) UUID as per
+     * <a href="http://www.ietf.org/rfc/rfc4122.txt">RFC 4122</a>.
+     *
+     * @return an UUID instance.
+     */
+    public static String randomUUID() {
+        byte[] data;
+        // lock on the class to protect lazy init
+        SecureRandom rng = new SecureRandom();
+        rng.nextBytes(data = new byte[16]);
+        long mostSigBits = (data[0] & 0xFFL) << 56;
+        mostSigBits |= (data[1] & 0xFFL) << 48;
+        mostSigBits |= (data[2] & 0xFFL) << 40;
+        mostSigBits |= (data[3] & 0xFFL) << 32;
+        mostSigBits |= (data[4] & 0xFFL) << 24;
+        mostSigBits |= (data[5] & 0xFFL) << 16;
+        mostSigBits |= (data[6] & 0x0FL) << 8;
+        mostSigBits |= (0x4L << 12); // set the version to 4
+        mostSigBits |= (data[7] & 0xFFL);
+
+        long leastSigBits = (data[8] & 0x3FL) << 56;
+        leastSigBits |= (0x2L << 62); // set the variant to bits 01
+        leastSigBits |= (data[9] & 0xFFL) << 48;
+        leastSigBits |= (data[10] & 0xFFL) << 40;
+        leastSigBits |= (data[11] & 0xFFL) << 32;
+        leastSigBits |= (data[12] & 0xFFL) << 24;
+        leastSigBits |= (data[13] & 0xFFL) << 16;
+        leastSigBits |= (data[14] & 0xFFL) << 8;
+        leastSigBits |= (data[15] & 0xFFL);
+
+        //
+        // UUID.init()
+        //
+
+        int variant;
+        int version;
+        long timestamp;
+        int clockSequence;
+        long node;
+        int hash;
+
+        // setup hash field
+        int msbHash = (int) (mostSigBits ^ (mostSigBits >>> 32));
+        int lsbHash = (int) (leastSigBits ^ (leastSigBits >>> 32));
+        hash = msbHash ^ lsbHash;
+
+        // setup variant field
+        if ((leastSigBits & 0x8000000000000000L) == 0) {
+            // MSB0 not set, NCS backwards compatibility variant
+            variant = 0;
+        } else if ((leastSigBits & 0x4000000000000000L) != 0) {
+            // MSB1 set, either MS reserved or future reserved
+            variant = (int) ((leastSigBits & 0xE000000000000000L) >>> 61);
+        } else {
+            // MSB1 not set, RFC 4122 variant
+            variant = 2;
+        }
+
+        // setup version field
+        version = (int) ((mostSigBits & 0x000000000000F000) >>> 12);
+
+        if (!(variant != 2 && version != 1)) {
+            // setup timestamp field
+            long timeLow = (mostSigBits & 0xFFFFFFFF00000000L) >>> 32;
+            long timeMid = (mostSigBits & 0x00000000FFFF0000L) << 16;
+            long timeHigh = (mostSigBits & 0x0000000000000FFFL) << 48;
+            timestamp = timeLow | timeMid | timeHigh;
+
+            // setup clock sequence field
+            clockSequence = (int) ((leastSigBits & 0x3FFF000000000000L) >>> 48);
+
+            // setup node field
+            node = (leastSigBits & 0x0000FFFFFFFFFFFFL);
+        }
+
+        //
+        // UUID.toString()
+        //
+
+        StringBuffer builder = new StringBuffer(36);
+        String msbStr = Long.toHexString(mostSigBits);
+        if (msbStr.length() < 16) {
+            int diff = 16 - msbStr.length();
+            for (int i = 0; i < diff; i++) {
+                builder.append('0');
+            }
+        }
+        builder.append(msbStr);
+        builder.insert(8, '-');
+        builder.insert(13, '-');
+        builder.append('-');
+        String lsbStr = Long.toHexString(leastSigBits);
+        if (lsbStr.length() < 16) {
+            int diff = 16 - lsbStr.length();
+            for (int i = 0; i < diff; i++) {
+                builder.append('0');
+            }
+        }
+        builder.append(lsbStr);
+        builder.insert(23, '-');
+        return builder.toString();
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/felix/framework/util/VersionRange.java b/src/main/java/org/apache/felix/framework/util/VersionRange.java
new file mode 100644
index 0000000..f8481b5
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/util/VersionRange.java
@@ -0,0 +1,159 @@
+/* 
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.util;
+
+import org.osgi.framework.Version;
+
+public class VersionRange
+{
+    private final Version m_floor;
+    private final boolean m_isFloorInclusive;
+    private final Version m_ceiling;
+    private final boolean m_isCeilingInclusive;
+    public static final VersionRange infiniteRange
+        = new VersionRange(Version.emptyVersion, true, null, true);
+
+    public VersionRange(
+        Version low, boolean isLowInclusive,
+        Version high, boolean isHighInclusive)
+    {
+        m_floor = low;
+        m_isFloorInclusive = isLowInclusive;
+        m_ceiling = high;
+        m_isCeilingInclusive = isHighInclusive;
+    }
+
+    public Version getFloor()
+    {
+        return m_floor;
+    }
+
+    public boolean isFloorInclusive()
+    {
+        return m_isFloorInclusive;
+    }
+
+    public Version getCeiling()
+    {
+        return m_ceiling;
+    }
+
+    public boolean isCeilingInclusive()
+    {
+        return m_isCeilingInclusive;
+    }
+
+    public boolean isInRange(Version version)
+    {
+        // We might not have an upper end to the range.
+        if (m_ceiling == null)
+        {
+            return (version.compareTo(m_floor) >= 0);
+        }
+        else if (isFloorInclusive() && isCeilingInclusive())
+        {
+            return (version.compareTo(m_floor) >= 0) && (version.compareTo(m_ceiling) <= 0);
+        }
+        else if (isCeilingInclusive())
+        {
+            return (version.compareTo(m_floor) > 0) && (version.compareTo(m_ceiling) <= 0);
+        }
+        else if (isFloorInclusive())
+        {
+            return (version.compareTo(m_floor) >= 0) && (version.compareTo(m_ceiling) < 0);
+        }
+        return (version.compareTo(m_floor) > 0) && (version.compareTo(m_ceiling) < 0);
+    }
+
+    public static VersionRange parse(String range)
+    {
+        // Check if the version is an interval.
+        if (range.indexOf(',') >= 0)
+        {
+            String s = range.substring(1, range.length() - 1);
+            String vlo = s.substring(0, s.indexOf(',')).trim();
+            String vhi = s.substring(s.indexOf(',') + 1, s.length()).trim();
+            return new VersionRange (
+                new Version(vlo), (range.charAt(0) == '['),
+                new Version(vhi), (range.charAt(range.length() - 1) == ']'));
+        }
+        else
+        {
+            return new VersionRange(new Version(range), true, null, false);
+        }
+    }
+
+    public boolean equals(Object obj)
+    {
+        if (obj == null)
+        {
+            return false;
+        }
+        if (getClass() != obj.getClass())
+        {
+            return false;
+        }
+        final VersionRange other = (VersionRange) obj;
+        if (m_floor != other.m_floor && (m_floor == null || !m_floor.equals(other.m_floor)))
+        {
+            return false;
+        }
+        if (m_isFloorInclusive != other.m_isFloorInclusive)
+        {
+            return false;
+        }
+        if (m_ceiling != other.m_ceiling && (m_ceiling == null || !m_ceiling.equals(other.m_ceiling)))
+        {
+            return false;
+        }
+        if (m_isCeilingInclusive != other.m_isCeilingInclusive)
+        {
+            return false;
+        }
+        return true;
+    }
+
+    public int hashCode()
+    {
+        int hash = 5;
+        hash = 97 * hash + (m_floor != null ? m_floor.hashCode() : 0);
+        hash = 97 * hash + (m_isFloorInclusive ? 1 : 0);
+        hash = 97 * hash + (m_ceiling != null ? m_ceiling.hashCode() : 0);
+        hash = 97 * hash + (m_isCeilingInclusive ? 1 : 0);
+        return hash;
+    }
+
+    public String toString()
+    {
+        if (m_ceiling != null)
+        {
+            StringBuffer sb = new StringBuffer();
+            sb.append(m_isFloorInclusive ? '[' : '(');
+            sb.append(m_floor.toString());
+            sb.append(',');
+            sb.append(m_ceiling.toString());
+            sb.append(m_isCeilingInclusive ? ']' : ')');
+            return sb.toString();
+        }
+        else
+        {
+            return m_floor.toString();
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/felix/framework/util/WeakZipFileFactory.java b/src/main/java/org/apache/felix/framework/util/WeakZipFileFactory.java
new file mode 100644
index 0000000..aa2e157
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/util/WeakZipFileFactory.java
@@ -0,0 +1,746 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.util;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+/**
+ * This class implements a factory for creating weak zip files, which behave
+ * mostly like a ZipFile, but can be weakly closed to limit the number of
+ * open files.
+ */
+public class WeakZipFileFactory
+{
+    private static final int WEAKLY_CLOSED = 0;
+    private static final int OPEN = 1;
+    private static final int CLOSED = 2;
+
+    private static final SecureAction m_secureAction = new SecureAction();
+
+    private final List<WeakZipFile> m_zipFiles = new ArrayList<WeakZipFile>();
+    private final List<WeakZipFile> m_openFiles = new ArrayList<WeakZipFile>();
+    private final Mutex m_globalMutex = new Mutex();
+    private final int m_limit;
+
+    /**
+     * Constructs a weak zip file factory with the specified file limit. A limit
+     * of zero signifies no limit.
+     * @param limit maximum number of open zip files at any given time.
+     */
+    public WeakZipFileFactory(int limit)
+    {
+        if (limit < 0)
+        {
+            throw new IllegalArgumentException("Limit must be non-negative.");
+        }
+        m_limit = limit;
+    }
+
+    /**
+     * Factory method used to create weak zip files.
+     * @param file the target zip file.
+     * @return the created weak zip file.
+     * @throws IOException if the zip file could not be opened.
+     */
+    public WeakZipFile create(File file) throws IOException
+    {
+        WeakZipFile wzf = new WeakZipFile(file);
+
+        if (m_limit > 0)
+        {
+            try
+            {
+                m_globalMutex.down();
+            }
+            catch (InterruptedException ex)
+            {
+                Thread.currentThread().interrupt();
+                throw new IOException("Interrupted while acquiring global zip file mutex.");
+            }
+
+            try
+            {
+                m_zipFiles.add(wzf);
+                m_openFiles.add(wzf);
+                if (m_openFiles.size() > m_limit)
+                {
+                    WeakZipFile candidate = m_openFiles.get(0);
+                    for (WeakZipFile tmp : m_openFiles)
+                    {
+                        if (candidate.m_timestamp > tmp.m_timestamp)
+                        {
+                            candidate = tmp;
+                        }
+                    }
+                    candidate._closeWeakly();
+                }
+            }
+            finally
+            {
+                m_globalMutex.up();
+            }
+        }
+
+        return wzf;
+    }
+
+    /**
+     * Only used for testing.
+     * @return unclosed weak zip files.
+     **/
+    List<WeakZipFile> getZipZiles()
+    {
+        try
+        {
+            m_globalMutex.down();
+        }
+        catch (InterruptedException ex)
+        {
+            Thread.currentThread().interrupt();
+            return Collections.EMPTY_LIST;
+        }
+
+        try
+        {
+            return m_zipFiles;
+        }
+        finally
+        {
+            m_globalMutex.up();
+        }
+    }
+
+    /**
+     * Only used for testing.
+     * @return open weak zip files.
+     **/
+    List<WeakZipFile> getOpenZipZiles()
+    {
+        try
+        {
+            m_globalMutex.down();
+        }
+        catch (InterruptedException ex)
+        {
+            Thread.currentThread().interrupt();
+            return Collections.EMPTY_LIST;
+        }
+
+        try
+        {
+            return m_openFiles;
+        }
+        finally
+        {
+            m_globalMutex.up();
+        }
+    }
+
+    /**
+     * This class wraps a ZipFile to making it possible to weakly close it;
+     * this means the underlying zip file will be automatically reopened on demand
+     * if anyone tries to use it.
+     */
+    public class WeakZipFile
+    {
+        private final File m_file;
+        private final Mutex m_localMutex = new Mutex();
+        private ZipFile m_zipFile;
+        private int m_status = OPEN;
+        private long m_timestamp;
+
+        /**
+         * Constructor is private since instances need to be centrally
+         * managed.
+         * @param file the target zip file.
+         * @throws IOException if the zip file could not be opened.
+         */
+        private WeakZipFile(File file) throws IOException
+        {
+            m_file = file;
+            m_zipFile = m_secureAction.openZipFile(m_file);
+            m_timestamp = System.currentTimeMillis();
+        }
+
+        /**
+         * Returns the specified entry from the zip file.
+         * @param name the name of the entry to return.
+         * @return the zip entry associated with the specified name or null
+         *         if it does not exist.
+         */
+        public ZipEntry getEntry(String name)
+        {
+            ensureZipFileIsOpen();
+
+            try
+            {
+                ZipEntry ze = null;
+                ze = m_zipFile.getEntry(name);
+                if ((ze != null) && (ze.getSize() == 0) && !ze.isDirectory())
+                {
+                    //The attempts to fix an apparent bug in the JVM in versions
+                    // 1.4.2 and lower where directory entries in ZIP/JAR files are
+                    // not correctly identified.
+                    ZipEntry dirEntry = m_zipFile.getEntry(name + '/');
+                    if (dirEntry != null)
+                    {
+                        ze = dirEntry;
+                    }
+                }
+                return ze;
+            }
+            finally
+            {
+                m_localMutex.up();
+            }
+        }
+
+        /**
+         * Returns an enumeration of zip entries from the zip file.
+         * @return an enumeration of zip entries.
+         */
+        public Enumeration<ZipEntry> entries()
+        {
+            ensureZipFileIsOpen();
+
+            try
+            {
+                // We need to suck in all of the entries since the zip
+                // file may get weakly closed during iteration. Technically,
+                // this may not be 100% correct either since if the zip file
+                // gets weakly closed and reopened, then the zip entries
+                // will be from a different zip file. It is not clear if this
+                // will cause any issues.
+                Enumeration<? extends ZipEntry> e = m_zipFile.entries();
+                List<ZipEntry> entries = new ArrayList<ZipEntry>();
+                while (e.hasMoreElements())
+                {
+                    entries.add(e.nextElement());
+                }
+                return Collections.enumeration(entries);
+            }
+            finally
+            {
+                m_localMutex.up();
+            }
+        }
+
+        /**
+         * Returns an input stream for the specified zip entry.
+         * @param ze the zip entry whose input stream is to be retrieved.
+         * @return an input stream to the zip entry.
+         * @throws IOException if the input stream cannot be opened.
+         */
+        public InputStream getInputStream(ZipEntry ze) throws IOException
+        {
+            ensureZipFileIsOpen();
+
+            try
+            {
+                InputStream is = m_zipFile.getInputStream(ze);
+                return new WeakZipInputStream(ze.getName(), is);
+            }
+            finally
+            {
+                m_localMutex.up();
+            }
+        }
+
+        /**
+         * Weakly closes the zip file, which means that it will be reopened
+         * if anyone tries to use it again.
+         */
+        void closeWeakly()
+        {
+            try
+            {
+                m_globalMutex.down();
+            }
+            catch (InterruptedException ex)
+            {
+                Thread.currentThread().interrupt();
+                throw new IllegalStateException(
+                    "Interrupted while acquiring global zip file mutex.");
+            }
+
+            try
+            {
+                _closeWeakly();
+            }
+            finally
+            {
+                m_globalMutex.up();
+            }
+        }
+
+        /**
+         * This method is used internally to weakly close a zip file. It should
+         * only be called when already holding the global lock, otherwise use
+         * closeWeakly().
+         */
+        private void _closeWeakly()
+        {
+            try
+            {
+                m_localMutex.down();
+            }
+            catch (InterruptedException ex)
+            {
+                Thread.currentThread().interrupt();
+                throw new IllegalStateException(
+                    "Interrupted while acquiring local zip file mutex.");
+            }
+
+            try
+            {
+                if (m_status == OPEN)
+                {
+                    try
+                    {
+                        m_status = WEAKLY_CLOSED;
+                        if (m_zipFile != null)
+                        {
+                            m_zipFile.close();
+                            m_zipFile = null;
+                        }
+                        m_openFiles.remove(this);
+                    }
+                    catch (IOException ex)
+                    {
+                        __close();
+                    }
+                }
+            }
+            finally
+            {
+                m_localMutex.up();
+            }
+        }
+
+        /**
+         * This method permanently closes the zip file.
+         * @throws IOException if any error occurs while trying to close the
+         *         zip file.
+         */
+        public void close() throws IOException
+        {
+            if (m_limit > 0)
+            {
+                try
+                {
+                    m_globalMutex.down();
+                }
+                catch (InterruptedException ex)
+                {
+                    Thread.currentThread().interrupt();
+                    throw new IllegalStateException(
+                        "Interrupted while acquiring global zip file mutex.");
+                }
+                try
+                {
+                    m_localMutex.down();
+                }
+                catch (InterruptedException ex)
+                {
+                    m_globalMutex.up();
+                    Thread.currentThread().interrupt();
+                    throw new IllegalStateException(
+                        "Interrupted while acquiring local zip file mutex.");
+                }
+            }
+
+            try
+            {
+                ZipFile tmp = m_zipFile;
+                __close();
+                if (tmp != null)
+                {
+                    tmp.close();
+                }
+            }
+            finally
+            {
+                m_localMutex.up();
+                m_globalMutex.up();
+            }
+        }
+
+        /**
+         * This internal method is used to clear the zip file from the data
+         * structures and reset its state. It should only be called when
+         * holding the global and local mutexes.
+         */
+        private void __close()
+        {
+            m_status = CLOSED;
+            m_zipFile = null;
+            m_zipFiles.remove(this);
+            m_openFiles.remove(this);
+        }
+
+        /**
+         * This method ensures that the zip file associated with this
+         * weak zip file instance is actually open and acquires the
+         * local weak zip file mutex. If the underlying zip file is closed,
+         * then the local mutex is released and an IllegalStateException is
+         * thrown. If the zip file is weakly closed, then it is reopened.
+         * If the zip file is already opened, then no additional action is
+         * necessary. If this method does not throw an exception, then
+         * the end result is the zip file member field is non-null and the
+         * local mutex has been acquired.
+         */
+        private void ensureZipFileIsOpen()
+        {
+            if (m_limit == 0)
+            {
+                return;
+            }
+
+            // Get mutex for zip file.
+            try
+            {
+                m_localMutex.down();
+            }
+            catch (InterruptedException ex)
+            {
+                Thread.currentThread().interrupt();
+                throw new IllegalStateException(
+                    "Interrupted while acquiring local zip file mutex.");
+            }
+
+            // If zip file is closed, then just return null.
+            if (m_status == CLOSED)
+            {
+                m_localMutex.up();
+                throw new IllegalStateException("Zip file is closed: " + m_file);
+            }
+
+            // If zip file is weakly closed, we need to reopen it,
+            // but we have to release the zip mutex to acquire the
+            // global mutex, then reacquire the zip mutex. This
+            // ensures that the global mutex is always acquired
+            // before any local mutex to avoid deadlocks.
+            IOException cause = null;
+            if (m_status == WEAKLY_CLOSED)
+            {
+                m_localMutex.up();
+
+                try
+                {
+                    m_globalMutex.down();
+                }
+                catch (InterruptedException ex)
+                {
+                    Thread.currentThread().interrupt();
+                    throw new IllegalStateException(
+                        "Interrupted while acquiring global zip file mutex.");
+                }
+                try
+                {
+                    m_localMutex.down();
+                }
+                catch (InterruptedException ex)
+                {
+                    m_globalMutex.up();
+                    Thread.currentThread().interrupt();
+                    throw new IllegalStateException(
+                        "Interrupted while acquiring local zip file mutex.");
+                }
+
+                // Double check status since it may have changed.
+                if (m_status == CLOSED)
+                {
+                    m_localMutex.up();
+                    m_globalMutex.up();
+                    throw new IllegalStateException("Zip file is closed: " + m_file);
+                }
+                else if (m_status == WEAKLY_CLOSED)
+                {
+                    try
+                    {
+                        __reopenZipFile();
+                    }
+                    catch (IOException ex)
+                    {
+                        cause = ex;
+                    }
+                }
+
+                // Release the global mutex, since it should no longer be necessary.
+                m_globalMutex.up();
+            }
+
+            // It is possible that reopening the zip file failed, so we check
+            // for that case and throw an exception.
+            if (m_zipFile == null)
+            {
+                m_localMutex.up();
+                IllegalStateException ise =
+                    new IllegalStateException("Zip file is closed: " + m_file);
+                if (cause != null)
+                {
+                    ise.initCause(cause);
+                }
+                throw ise;
+            }
+        }
+
+        /**
+         * Thie internal method is used to reopen a weakly closed zip file.
+         * It makes a best effort, but may fail and leave the zip file member
+         * field null. Any failure reopening a zip file results in it being
+         * permanently closed. This method should only be invoked when holding
+         * the global and local mutexes.
+         */
+        private void __reopenZipFile() throws IOException
+        {
+            if (m_status == WEAKLY_CLOSED)
+            {
+                try
+                {
+                    m_zipFile = m_secureAction.openZipFile(m_file);
+                    m_status = OPEN;
+                    m_timestamp = System.currentTimeMillis();
+                }
+                catch (IOException ex)
+                {
+                    __close();
+                    throw ex;
+                }
+
+                if (m_zipFile != null)
+                {
+                    m_openFiles.add(this);
+                    if (m_openFiles.size() > m_limit)
+                    {
+                        WeakZipFile candidate = m_openFiles.get(0);
+                        for (WeakZipFile tmp : m_openFiles)
+                        {
+                            if (candidate.m_timestamp > tmp.m_timestamp)
+                            {
+                                candidate = tmp;
+                            }
+                        }
+                        candidate._closeWeakly();
+                    }
+                }
+            }
+        }
+
+        /**
+         * This is an InputStream wrapper that will properly reopen the underlying
+         * zip file if it is weakly closed and create the underlying input stream.
+         */
+        class WeakZipInputStream extends InputStream
+        {
+            private final String m_entryName;
+            private InputStream m_is;
+            private int m_currentPos = 0;
+            private ZipFile m_zipFileSnapshot;
+
+            WeakZipInputStream(String entryName, InputStream is)
+            {
+                m_entryName = entryName;
+                m_is = is;
+                m_zipFileSnapshot = m_zipFile;
+            }
+
+            /**
+             * This internal method ensures that the zip file is open and that
+             * the underlying input stream is valid. Upon successful completion,
+             * the underlying input stream will be valid and the local mutex
+             * will be held.
+             * @throws IOException if the was an error handling the input stream.
+             */
+            private void ensureInputStreamIsValid() throws IOException
+            {
+                if (m_limit == 0)
+                {
+                    return;
+                }
+
+                ensureZipFileIsOpen();
+
+                // If the underlying zip file changed, then we need
+                // to get the input stream again.
+                if (m_zipFileSnapshot != m_zipFile)
+                {
+                    m_zipFileSnapshot = m_zipFile;
+
+                    if (m_is != null)
+                    {
+                        try
+                        {
+                            m_is.close();
+                        }
+                        catch (Exception ex)
+                        {
+                            // Not much we can do.
+                        }
+                    }
+                    try
+                    {
+                        m_is = m_zipFile.getInputStream(m_zipFile.getEntry(m_entryName));
+                        m_is.skip(m_currentPos);
+                    }
+                    catch (IOException ex)
+                    {
+                        m_localMutex.up();
+                        throw ex;
+                    }
+                }
+            }
+
+            @Override
+            public int available() throws IOException
+            {
+                ensureInputStreamIsValid();
+                try
+                {
+                    return m_is.available();
+                }
+                finally
+                {
+                    m_localMutex.up();
+                }
+            }
+
+            @Override
+            public void close() throws IOException
+            {
+                ensureInputStreamIsValid();
+                try
+                {
+                    InputStream is = m_is;
+                    m_is = null;
+                    if (is != null)
+                    {
+                        is.close();
+                    }
+                }
+                finally
+                {
+                    m_localMutex.up();
+                }
+            }
+
+            @Override
+            public void mark(int i)
+            {
+                // Not supported.
+            }
+
+            @Override
+            public boolean markSupported()
+            {
+                // Not supported.
+                return false;
+            }
+
+            public int read() throws IOException
+            {
+                ensureInputStreamIsValid();
+                try
+                {
+                    int len = m_is.read();
+                    if (len > 0)
+                    {
+                        m_currentPos++;
+                    }
+                    return len;
+                }
+                finally
+                {
+                    m_localMutex.up();
+                }
+            }
+
+            @Override
+            public int read(byte[] bytes) throws IOException
+            {
+                ensureInputStreamIsValid();
+                try
+                {
+                    int len = m_is.read(bytes);
+                    if (len > 0)
+                    {
+                        m_currentPos += len;
+                    }
+                    return len;
+                }
+                finally
+                {
+                    m_localMutex.up();
+                }
+            }
+
+            @Override
+            public int read(byte[] bytes, int i, int i1) throws IOException
+            {
+                ensureInputStreamIsValid();
+                try
+                {
+                    int len = m_is.read(bytes, i, i1);
+                    if (len > 0)
+                    {
+                        m_currentPos += len;
+                    }
+                    return len;
+                }
+                finally
+                {
+                    m_localMutex.up();
+                }
+            }
+
+            @Override
+            public void reset() throws IOException
+            {
+                throw new IOException("Unsupported operation");
+            }
+
+            @Override
+            public long skip(long l) throws IOException
+            {
+                ensureInputStreamIsValid();
+                try
+                {
+                    long len = m_is.skip(l);
+                    if (len > 0)
+                    {
+                        m_currentPos += len;
+                    }
+                    return len;
+                }
+                finally
+                {
+                    m_localMutex.up();
+                }
+            }
+        }
+    }
+}
diff --git a/src/main/java/org/apache/felix/framework/util/manifestparser/ManifestParser.java b/src/main/java/org/apache/felix/framework/util/manifestparser/ManifestParser.java
new file mode 100644
index 0000000..672fd2f
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/util/manifestparser/ManifestParser.java
@@ -0,0 +1,2056 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.util.manifestparser;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import org.apache.felix.framework.BundleRevisionImpl;
+import org.apache.felix.framework.Logger;
+import org.apache.felix.framework.capabilityset.SimpleFilter;
+import org.apache.felix.framework.util.FelixConstants;
+import org.apache.felix.framework.util.VersionRange;
+import org.apache.felix.framework.wiring.BundleCapabilityImpl;
+import org.apache.felix.framework.wiring.BundleRequirementImpl;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Version;
+import org.osgi.framework.namespace.BundleNamespace;
+import org.osgi.framework.namespace.ExecutionEnvironmentNamespace;
+import org.osgi.framework.namespace.IdentityNamespace;
+import org.osgi.framework.wiring.BundleCapability;
+import org.osgi.framework.wiring.BundleRequirement;
+import org.osgi.framework.wiring.BundleRevision;
+
+public class ManifestParser
+{
+    private static final String BUNDLE_LICENSE_HEADER = "Bundle-License"; // No constant defined by OSGi...
+
+    private final Logger m_logger;
+    private final Map m_configMap;
+    private final Map m_headerMap;
+    private volatile int m_activationPolicy = BundleRevisionImpl.EAGER_ACTIVATION;
+    private volatile String m_activationIncludeDir;
+    private volatile String m_activationExcludeDir;
+    private volatile boolean m_isExtension = false;
+    private volatile String m_bundleSymbolicName;
+    private volatile Version m_bundleVersion;
+    private volatile List<BundleCapability> m_capabilities;
+    private volatile List<BundleRequirement> m_requirements;
+    private volatile List<R4LibraryClause> m_libraryClauses;
+    private volatile boolean m_libraryHeadersOptional = false;
+
+    public ManifestParser(Logger logger, Map configMap, BundleRevision owner, Map headerMap)
+        throws BundleException
+    {
+        m_logger = logger;
+        m_configMap = configMap;
+        m_headerMap = headerMap;
+
+        // Verify that only manifest version 2 is specified.
+        String manifestVersion = getManifestVersion(m_headerMap);
+        if ((manifestVersion != null) && !manifestVersion.equals("2"))
+        {
+            throw new BundleException(
+                "Unknown 'Bundle-ManifestVersion' value: " + manifestVersion);
+        }
+
+        // Create lists to hold capabilities and requirements.
+        List<BundleCapabilityImpl> capList = new ArrayList();
+
+        //
+        // Parse bundle version.
+        //
+
+        m_bundleVersion = Version.emptyVersion;
+        if (headerMap.get(Constants.BUNDLE_VERSION) != null)
+        {
+            try
+            {
+                m_bundleVersion = Version.parseVersion(
+                    (String) headerMap.get(Constants.BUNDLE_VERSION));
+            }
+            catch (RuntimeException ex)
+            {
+                // R4 bundle versions must parse, R3 bundle version may not.
+                if (getManifestVersion().equals("2"))
+                {
+                    throw ex;
+                }
+                m_bundleVersion = Version.emptyVersion;
+            }
+        }
+
+        //
+        // Parse bundle symbolic name.
+        //
+
+        BundleCapabilityImpl bundleCap = parseBundleSymbolicName(owner, m_headerMap);
+        if (bundleCap != null)
+        {
+            m_bundleSymbolicName = (String)
+                bundleCap.getAttributes().get(BundleRevision.BUNDLE_NAMESPACE);
+
+            // Add a bundle capability and a host capability to all
+            // non-fragment bundles. A host capability is the same
+            // as a require capability, but with a different capability
+            // namespace. Bundle capabilities resolve required-bundle
+            // dependencies, while host capabilities resolve fragment-host
+            // dependencies.
+            if (headerMap.get(Constants.FRAGMENT_HOST) == null)
+            {
+                // All non-fragment bundles have host capabilities.
+                capList.add(bundleCap);
+                // A non-fragment bundle can choose to not have a host capability.
+                String attachment =
+                    bundleCap.getDirectives().get(Constants.FRAGMENT_ATTACHMENT_DIRECTIVE);
+                attachment = (attachment == null)
+                    ? Constants.FRAGMENT_ATTACHMENT_RESOLVETIME
+                    : attachment;
+                if (!attachment.equalsIgnoreCase(Constants.FRAGMENT_ATTACHMENT_NEVER))
+                {
+                    Map<String, Object> hostAttrs =
+                        new HashMap<String, Object>(bundleCap.getAttributes());
+                    Object value = hostAttrs.remove(BundleRevision.BUNDLE_NAMESPACE);
+                    hostAttrs.put(BundleRevision.HOST_NAMESPACE, value);
+                    capList.add(new BundleCapabilityImpl(
+                        owner, BundleRevision.HOST_NAMESPACE,
+                        bundleCap.getDirectives(),
+                        hostAttrs));
+                }
+            }
+
+            //
+            // Add the osgi.identity capability.
+            //
+            capList.add(addIdentityCapability(owner, headerMap, bundleCap));
+        }
+
+        // Verify that bundle symbolic name is specified.
+        if (getManifestVersion().equals("2") && (m_bundleSymbolicName == null))
+        {
+            throw new BundleException(
+                "R4 bundle manifests must include bundle symbolic name.");
+        }
+
+        //
+        // Parse Fragment-Host.
+        //
+
+        List<BundleRequirementImpl> hostReqs = parseFragmentHost(m_logger, owner, m_headerMap);
+
+        //
+        // Parse Require-Bundle
+        //
+
+        List<ParsedHeaderClause> rbClauses =
+            parseStandardHeader((String) headerMap.get(Constants.REQUIRE_BUNDLE));
+        rbClauses = normalizeRequireClauses(m_logger, rbClauses, getManifestVersion());
+        List<BundleRequirementImpl> rbReqs = convertRequires(rbClauses, owner);
+
+        //
+        // Parse Import-Package.
+        //
+
+        List<ParsedHeaderClause> importClauses =
+            parseStandardHeader((String) headerMap.get(Constants.IMPORT_PACKAGE));
+        importClauses = normalizeImportClauses(m_logger, importClauses, getManifestVersion());
+        List<BundleRequirement> importReqs = convertImports(importClauses, owner);
+
+        //
+        // Parse DynamicImport-Package.
+        //
+
+        List<ParsedHeaderClause> dynamicClauses =
+            parseStandardHeader((String) headerMap.get(Constants.DYNAMICIMPORT_PACKAGE));
+        dynamicClauses = normalizeDynamicImportClauses(m_logger, dynamicClauses, getManifestVersion());
+        List<BundleRequirement> dynamicReqs = convertImports(dynamicClauses, owner);
+
+        //
+        // Parse Require-Capability.
+        //
+
+        List<ParsedHeaderClause> requireClauses =
+            parseStandardHeader((String) headerMap.get(Constants.REQUIRE_CAPABILITY));
+        importClauses = normalizeRequireCapabilityClauses(
+            m_logger, requireClauses, getManifestVersion());
+        List<BundleRequirement> requireReqs = convertRequireCapabilities(importClauses, owner);
+
+        //
+        // Parse Bundle-RequiredExecutionEnvironment.
+        //
+        List<BundleRequirement> breeReqs =
+            parseBreeHeader((String) headerMap.get(Constants.BUNDLE_REQUIREDEXECUTIONENVIRONMENT), owner);
+
+        //
+        // Parse Export-Package.
+        //
+
+        List<ParsedHeaderClause> exportClauses =
+            parseStandardHeader((String) headerMap.get(Constants.EXPORT_PACKAGE));
+        exportClauses = normalizeExportClauses(logger, exportClauses,
+            getManifestVersion(), m_bundleSymbolicName, m_bundleVersion);
+        List<BundleCapability> exportCaps = convertExports(exportClauses, owner);
+
+        //
+        // Parse Provide-Capability.
+        //
+
+        List<ParsedHeaderClause> provideClauses =
+            parseStandardHeader((String) headerMap.get(Constants.PROVIDE_CAPABILITY));
+        exportClauses = normalizeProvideCapabilityClauses(
+            logger, provideClauses, getManifestVersion());
+        List<BundleCapability> provideCaps = convertProvideCapabilities(provideClauses, owner);
+
+        //
+        // Calculate implicit imports.
+        //
+
+        if (!getManifestVersion().equals("2"))
+        {
+            List<ParsedHeaderClause> implicitClauses =
+                calculateImplicitImports(exportCaps, importClauses);
+            importReqs.addAll(convertImports(implicitClauses, owner));
+
+            List<ParsedHeaderClause> allImportClauses =
+                new ArrayList<ParsedHeaderClause>(implicitClauses.size() + importClauses.size());
+            allImportClauses.addAll(importClauses);
+            allImportClauses.addAll(implicitClauses);
+
+            exportCaps = calculateImplicitUses(exportCaps, allImportClauses);
+        }
+
+        // Combine all capabilities.
+        m_capabilities = new ArrayList(
+             capList.size() + exportCaps.size() + provideCaps.size());
+        m_capabilities.addAll(capList);
+        m_capabilities.addAll(exportCaps);
+        m_capabilities.addAll(provideCaps);
+
+        // Combine all requirements.
+        m_requirements = new ArrayList(
+            hostReqs.size() + importReqs.size() + rbReqs.size()
+            + requireReqs.size() + dynamicReqs.size() + breeReqs.size());
+        m_requirements.addAll(hostReqs);
+        m_requirements.addAll(importReqs);
+        m_requirements.addAll(rbReqs);
+        m_requirements.addAll(requireReqs);
+        m_requirements.addAll(dynamicReqs);
+        m_requirements.addAll(breeReqs);
+
+        //
+        // Parse Bundle-NativeCode.
+        //
+
+        // Parse native library clauses.
+        m_libraryClauses =
+            parseLibraryStrings(
+                m_logger,
+                parseDelimitedString((String) m_headerMap.get(Constants.BUNDLE_NATIVECODE), ","));
+
+        // Check to see if there was an optional native library clause, which is
+        // represented by a null library header; if so, record it and remove it.
+        if (!m_libraryClauses.isEmpty() &&
+            (m_libraryClauses.get(m_libraryClauses.size() - 1).getLibraryEntries() == null))
+        {
+            m_libraryHeadersOptional = true;
+            m_libraryClauses.remove(m_libraryClauses.size() - 1);
+        }
+
+        //
+        // Parse activation policy.
+        //
+
+        // This sets m_activationPolicy, m_includedPolicyClasses, and
+        // m_excludedPolicyClasses.
+        parseActivationPolicy(headerMap);
+
+        m_isExtension = checkExtensionBundle(headerMap);
+    }
+
+    private static List<ParsedHeaderClause> normalizeImportClauses(
+        Logger logger, List<ParsedHeaderClause> clauses, String mv)
+        throws BundleException
+    {
+        // Verify that the values are equals if the package specifies
+        // both version and specification-version attributes.
+        Set dupeSet = new HashSet();
+        for (ParsedHeaderClause clause : clauses)
+        {
+            // Check for "version" and "specification-version" attributes
+            // and verify they are the same if both are specified.
+            Object v = clause.m_attrs.get(Constants.VERSION_ATTRIBUTE);
+            Object sv = clause.m_attrs.get(Constants.PACKAGE_SPECIFICATION_VERSION);
+            if ((v != null) && (sv != null))
+            {
+                // Verify they are equal.
+                if (!((String) v).trim().equals(((String) sv).trim()))
+                {
+                    throw new IllegalArgumentException(
+                        "Both version and specification-version are specified, but they are not equal.");
+                }
+            }
+
+            // Ensure that only the "version" attribute is used and convert
+            // it to the VersionRange type.
+            if ((v != null) || (sv != null))
+            {
+                clause.m_attrs.remove(Constants.PACKAGE_SPECIFICATION_VERSION);
+                v = (v == null) ? sv : v;
+                clause.m_attrs.put(
+                    Constants.VERSION_ATTRIBUTE,
+                    VersionRange.parse(v.toString()));
+            }
+
+            // If bundle version is specified, then convert its type to VersionRange.
+            v = clause.m_attrs.get(Constants.BUNDLE_VERSION_ATTRIBUTE);
+            if (v != null)
+            {
+                clause.m_attrs.put(
+                    Constants.BUNDLE_VERSION_ATTRIBUTE,
+                    VersionRange.parse(v.toString()));
+            }
+
+            // Verify java.* is not imported, nor any duplicate imports.
+            for (String pkgName : clause.m_paths)
+            {
+                if (!dupeSet.contains(pkgName))
+                {
+                    // Verify that java.* packages are not imported.
+                    if (pkgName.startsWith("java."))
+                    {
+                        throw new BundleException(
+                            "Importing java.* packages not allowed: " + pkgName);
+                    }
+                    // The character "." has no meaning in the OSGi spec except
+                    // when placed on the bundle class path. Some people, however,
+                    // mistakenly think it means the default package when imported
+                    // or exported. This is not correct. It is invalid.
+                    else if (pkgName.equals("."))
+                    {
+                        throw new BundleException("Imporing '.' is invalid.");
+                    }
+                    // Make sure a package name was specified.
+                    else if (pkgName.length() == 0)
+                    {
+                        throw new BundleException(
+                            "Imported package names cannot be zero length.");
+                    }
+                    dupeSet.add(pkgName);
+                }
+                else
+                {
+                    throw new BundleException("Duplicate import: " + pkgName);
+                }
+            }
+
+            if (!mv.equals("2"))
+            {
+                // R3 bundles cannot have directives on their imports.
+                if (!clause.m_dirs.isEmpty())
+                {
+                    throw new BundleException("R3 imports cannot contain directives.");
+                }
+
+                // Remove and ignore all attributes other than version.
+                // NOTE: This is checking for "version" rather than "specification-version"
+                // because the package class normalizes to "version" to avoid having
+                // future special cases. This could be changed if more strict behavior
+                // is required.
+                if (!clause.m_attrs.isEmpty())
+                {
+                    // R3 package requirements should only have version attributes.
+                    Object pkgVersion = clause.m_attrs.get(BundleCapabilityImpl.VERSION_ATTR);
+                    pkgVersion = (pkgVersion == null)
+                        ? new VersionRange(Version.emptyVersion, true, null, true)
+                        : pkgVersion;
+                    for (Entry<String, Object> entry : clause.m_attrs.entrySet())
+                    {
+                        if (!entry.getKey().equals(BundleCapabilityImpl.VERSION_ATTR))
+                        {
+                            logger.log(Logger.LOG_WARNING,
+                                "Unknown R3 import attribute: "
+                                    + entry.getKey());
+                        }
+                    }
+
+                    // Remove all other attributes except package version.
+                    clause.m_attrs.clear();
+                    clause.m_attrs.put(BundleCapabilityImpl.VERSION_ATTR, pkgVersion);
+                }
+            }
+        }
+
+        return clauses;
+    }
+
+    public static List<BundleRequirement> parseDynamicImportHeader(
+        Logger logger, BundleRevision owner, String header)
+        throws BundleException
+    {
+
+        List<ParsedHeaderClause> importClauses = parseStandardHeader(header);
+        importClauses = normalizeDynamicImportClauses(logger, importClauses, "2");
+        List<BundleRequirement> reqs = convertImports(importClauses, owner);
+        return reqs;
+    }
+
+    private static List<BundleRequirement> convertImports(
+        List<ParsedHeaderClause> clauses, BundleRevision owner)
+    {
+        // Now convert generic header clauses into requirements.
+        List reqList = new ArrayList();
+        for (ParsedHeaderClause clause : clauses)
+        {
+            for (String path : clause.m_paths)
+            {
+                // Prepend the package name to the array of attributes.
+                Map<String, Object> attrs = clause.m_attrs;
+                // Note that we use a linked hash map here to ensure the
+                // package attribute is first, which will make indexing
+                // more efficient.
+// TODO: OSGi R4.3 - This is ordering is kind of hacky.
+                // Prepend the package name to the array of attributes.
+                Map<String, Object> newAttrs = new LinkedHashMap<String, Object>(attrs.size() + 1);
+                // We want this first from an indexing perspective.
+                newAttrs.put(
+                    BundleRevision.PACKAGE_NAMESPACE,
+                    path);
+                newAttrs.putAll(attrs);
+                // But we need to put it again to make sure it wasn't overwritten.
+                newAttrs.put(
+                    BundleRevision.PACKAGE_NAMESPACE,
+                    path);
+
+                // Create filter now so we can inject filter directive.
+                SimpleFilter sf = SimpleFilter.convert(newAttrs);
+
+                // Inject filter directive.
+// TODO: OSGi R4.3 - Can we insert this on demand somehow?
+                Map<String, String> dirs = clause.m_dirs;
+                Map<String, String> newDirs = new HashMap<String, String>(dirs.size() + 1);
+                newDirs.putAll(dirs);
+                newDirs.put(
+                    Constants.FILTER_DIRECTIVE,
+                    sf.toString());
+
+                // Create package requirement and add to requirement list.
+                reqList.add(
+                    new BundleRequirementImpl(
+                        owner,
+                        BundleRevision.PACKAGE_NAMESPACE,
+                        newDirs,
+                        Collections.EMPTY_MAP,
+                        sf));
+            }
+        }
+
+        return reqList;
+    }
+
+    private static List<ParsedHeaderClause> normalizeDynamicImportClauses(
+        Logger logger, List<ParsedHeaderClause> clauses, String mv)
+        throws BundleException
+    {
+        // Verify that the values are equals if the package specifies
+        // both version and specification-version attributes.
+        for (ParsedHeaderClause clause : clauses)
+        {
+            if (!mv.equals("2"))
+            {
+                // R3 bundles cannot have directives on their imports.
+                if (!clause.m_dirs.isEmpty())
+                {
+                    throw new BundleException("R3 imports cannot contain directives.");
+                }
+            }
+
+            // Add the resolution directive to indicate that these are
+            // dynamic imports.
+            clause.m_dirs.put(Constants.RESOLUTION_DIRECTIVE,
+                FelixConstants.RESOLUTION_DYNAMIC);
+
+            // Check for "version" and "specification-version" attributes
+            // and verify they are the same if both are specified.
+            Object v = clause.m_attrs.get(Constants.VERSION_ATTRIBUTE);
+            Object sv = clause.m_attrs.get(Constants.PACKAGE_SPECIFICATION_VERSION);
+            if ((v != null) && (sv != null))
+            {
+                // Verify they are equal.
+                if (!((String) v).trim().equals(((String) sv).trim()))
+                {
+                    throw new IllegalArgumentException(
+                        "Both version and specification-version are specified, but they are not equal.");
+                }
+            }
+
+            // Ensure that only the "version" attribute is used and convert
+            // it to the VersionRange type.
+            if ((v != null) || (sv != null))
+            {
+                clause.m_attrs.remove(Constants.PACKAGE_SPECIFICATION_VERSION);
+                v = (v == null) ? sv : v;
+                clause.m_attrs.put(
+                    Constants.VERSION_ATTRIBUTE,
+                    VersionRange.parse(v.toString()));
+            }
+
+            // If bundle version is specified, then convert its type to VersionRange.
+            v = clause.m_attrs.get(Constants.BUNDLE_VERSION_ATTRIBUTE);
+            if (v != null)
+            {
+                clause.m_attrs.put(
+                    Constants.BUNDLE_VERSION_ATTRIBUTE,
+                    VersionRange.parse(v.toString()));
+            }
+
+            // Dynamic imports can have duplicates, so verify that java.*
+            // packages are not imported.
+            for (String pkgName : clause.m_paths)
+            {
+                if (pkgName.startsWith("java."))
+                {
+                    throw new BundleException(
+                        "Dynamically importing java.* packages not allowed: " + pkgName);
+                }
+                else if (!pkgName.equals("*") && pkgName.endsWith("*") && !pkgName.endsWith(".*"))
+                {
+                    throw new BundleException(
+                        "Partial package name wild carding is not allowed: " + pkgName);
+                }
+            }
+        }
+
+        return clauses;
+    }
+
+    private static List<ParsedHeaderClause> normalizeRequireCapabilityClauses(
+        Logger logger, List<ParsedHeaderClause> clauses, String mv)
+        throws BundleException
+    {
+
+        if (!mv.equals("2") && !clauses.isEmpty())
+        {
+            // Should we error here if we are not an R4 bundle?
+        }
+
+        return clauses;
+    }
+
+    private static List<BundleRequirement> convertRequireCapabilities(
+        List<ParsedHeaderClause> clauses, BundleRevision owner)
+        throws BundleException
+    {
+        // Now convert generic header clauses into requirements.
+        List reqList = new ArrayList();
+        for (ParsedHeaderClause clause : clauses)
+        {
+            try
+            {
+                String filterStr = clause.m_dirs.get(Constants.FILTER_DIRECTIVE);
+                SimpleFilter sf = (filterStr != null)
+                    ? SimpleFilter.parse(filterStr)
+                    : new SimpleFilter(null, null, SimpleFilter.MATCH_ALL);
+                for (String path : clause.m_paths)
+                {
+                    if (path.startsWith("osgi.wiring."))
+                    {
+                        throw new BundleException("Manifest cannot use Require-Capability for '"
+                            + path
+                            + "' namespace.");
+                    }
+
+                    // Create requirement and add to requirement list.
+                    reqList.add(
+                        new BundleRequirementImpl(
+                            owner,
+                            path,
+                            clause.m_dirs,
+                            clause.m_attrs,
+                            sf));
+                }
+            }
+            catch (Exception ex)
+            {
+                throw new BundleException("Error creating requirement: " + ex);
+            }
+        }
+
+        return reqList;
+    }
+
+    private static List<ParsedHeaderClause> normalizeProvideCapabilityClauses(
+        Logger logger, List<ParsedHeaderClause> clauses, String mv)
+        throws BundleException
+    {
+
+        if (!mv.equals("2") && !clauses.isEmpty())
+        {
+            // Should we error here if we are not an R4 bundle?
+        }
+
+        // Convert attributes into specified types.
+        for (ParsedHeaderClause clause : clauses)
+        {
+            for (Entry<String, String> entry : clause.m_types.entrySet())
+            {
+                String type = entry.getValue();
+                if (!type.equals("String"))
+                {
+                    if (type.equals("Double"))
+                    {
+                        clause.m_attrs.put(
+                            entry.getKey(),
+                            new Double(clause.m_attrs.get(entry.getKey()).toString().trim()));
+                    }
+                    else if (type.equals("Version"))
+                    {
+                        clause.m_attrs.put(
+                            entry.getKey(),
+                            new Version(clause.m_attrs.get(entry.getKey()).toString().trim()));
+                    }
+                    else if (type.equals("Long"))
+                    {
+                        clause.m_attrs.put(
+                            entry.getKey(),
+                            new Long(clause.m_attrs.get(entry.getKey()).toString().trim()));
+                    }
+                    else if (type.startsWith("List"))
+                    {
+                        int startIdx = type.indexOf('<');
+                        int endIdx = type.indexOf('>');
+                        if (((startIdx > 0) && (endIdx <= startIdx))
+                            || ((startIdx < 0) && (endIdx > 0)))
+                        {
+                            throw new BundleException(
+                                "Invalid Provide-Capability attribute list type for '"
+                                + entry.getKey()
+                                + "' : "
+                                + type);
+                        }
+
+                        String listType = "String";
+                        if (endIdx > startIdx)
+                        {
+                            listType = type.substring(startIdx + 1, endIdx).trim();
+                        }
+
+                        List<String> tokens = parseDelimitedString(
+                            clause.m_attrs.get(entry.getKey()).toString(), ",", false);
+                        List<Object> values = new ArrayList<Object>(tokens.size());
+                        for (String token : tokens)
+                        {
+                            if (listType.equals("String"))
+                            {
+                                values.add(token);
+                            }
+                            else if (listType.equals("Double"))
+                            {
+                                values.add(new Double(token.trim()));
+                            }
+                            else if (listType.equals("Version"))
+                            {
+                                values.add(new Version(token.trim()));
+                            }
+                            else if (listType.equals("Long"))
+                            {
+                                values.add(new Long(token.trim()));
+                            }
+                            else
+                            {
+                                throw new BundleException(
+                                    "Unknown Provide-Capability attribute list type for '"
+                                    + entry.getKey()
+                                    + "' : "
+                                    + type);
+                            }
+                        }
+                        clause.m_attrs.put(
+                            entry.getKey(),
+                            values);
+                    }
+                    else
+                    {
+                        throw new BundleException(
+                            "Unknown Provide-Capability attribute type for '"
+                            + entry.getKey()
+                            + "' : "
+                            + type);
+                    }
+                }
+            }
+        }
+
+        return clauses;
+    }
+
+    private static List<BundleCapability> convertProvideCapabilities(
+        List<ParsedHeaderClause> clauses, BundleRevision owner)
+        throws BundleException
+    {
+        List<BundleCapability> capList = new ArrayList();
+        for (ParsedHeaderClause clause : clauses)
+        {
+            for (String path : clause.m_paths)
+            {
+                if (path.startsWith("osgi.wiring."))
+                {
+                    throw new BundleException("Manifest cannot use Provide-Capability for '"
+                        + path
+                        + "' namespace.");
+                }
+
+                // Create package capability and add to capability list.
+                capList.add(
+                    new BundleCapabilityImpl(
+                        owner,
+                        path,
+                        clause.m_dirs,
+                        clause.m_attrs));
+            }
+        }
+
+        return capList;
+    }
+
+    private static List<ParsedHeaderClause> normalizeExportClauses(
+        Logger logger, List<ParsedHeaderClause> clauses,
+        String mv, String bsn, Version bv)
+        throws BundleException
+    {
+        // Verify that "java.*" packages are not exported.
+        for (ParsedHeaderClause clause : clauses)
+        {
+            // Verify that the named package has not already been declared.
+            for (String pkgName : clause.m_paths)
+            {
+                // Verify that java.* packages are not exported.
+                if (pkgName.startsWith("java."))
+                {
+                    throw new BundleException(
+                        "Exporting java.* packages not allowed: "
+                        + pkgName);
+                }
+                // The character "." has no meaning in the OSGi spec except
+                // when placed on the bundle class path. Some people, however,
+                // mistakenly think it means the default package when imported
+                // or exported. This is not correct. It is invalid.
+                else if (pkgName.equals("."))
+                {
+                    throw new BundleException("Exporing '.' is invalid.");
+                }
+                // Make sure a package name was specified.
+                else if (pkgName.length() == 0)
+                {
+                    throw new BundleException(
+                        "Exported package names cannot be zero length.");
+                }
+            }
+
+            // Check for "version" and "specification-version" attributes
+            // and verify they are the same if both are specified.
+            Object v = clause.m_attrs.get(Constants.VERSION_ATTRIBUTE);
+            Object sv = clause.m_attrs.get(Constants.PACKAGE_SPECIFICATION_VERSION);
+            if ((v != null) && (sv != null))
+            {
+                // Verify they are equal.
+                if (!((String) v).trim().equals(((String) sv).trim()))
+                {
+                    throw new IllegalArgumentException(
+                        "Both version and specification-version are specified, but they are not equal.");
+                }
+            }
+
+            // Always add the default version if not specified.
+            if ((v == null) && (sv == null))
+            {
+                v = Version.emptyVersion;
+            }
+
+            // Ensure that only the "version" attribute is used and convert
+            // it to the appropriate type.
+            if ((v != null) || (sv != null))
+            {
+                // Convert version attribute to type Version.
+                clause.m_attrs.remove(Constants.PACKAGE_SPECIFICATION_VERSION);
+                v = (v == null) ? sv : v;
+                clause.m_attrs.put(
+                    Constants.VERSION_ATTRIBUTE,
+                    Version.parseVersion(v.toString()));
+            }
+
+            // If this is an R4 bundle, then make sure it doesn't specify
+            // bundle symbolic name or bundle version attributes.
+            if (mv.equals("2"))
+            {
+                // Find symbolic name and version attribute, if present.
+                if (clause.m_attrs.containsKey(Constants.BUNDLE_VERSION_ATTRIBUTE)
+                    || clause.m_attrs.containsKey(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE))
+                {
+                    throw new BundleException(
+                        "Exports must not specify bundle symbolic name or bundle version.");
+                }
+
+                // Now that we know that there are no bundle symbolic name and version
+                // attributes, add them since the spec says they are there implicitly.
+                clause.m_attrs.put(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE, bsn);
+                clause.m_attrs.put(Constants.BUNDLE_VERSION_ATTRIBUTE, bv);
+            }
+            else if (!mv.equals("2"))
+            {
+                // R3 bundles cannot have directives on their exports.
+                if (!clause.m_dirs.isEmpty())
+                {
+                    throw new BundleException("R3 exports cannot contain directives.");
+                }
+
+                // Remove and ignore all attributes other than version.
+                // NOTE: This is checking for "version" rather than "specification-version"
+                // because the package class normalizes to "version" to avoid having
+                // future special cases. This could be changed if more strict behavior
+                // is required.
+                if (!clause.m_attrs.isEmpty())
+                {
+                    // R3 package capabilities should only have a version attribute.
+                    Object pkgVersion = clause.m_attrs.get(BundleCapabilityImpl.VERSION_ATTR);
+                    pkgVersion = (pkgVersion == null)
+                        ? Version.emptyVersion
+                        : pkgVersion;
+                    for (Entry<String, Object> entry : clause.m_attrs.entrySet())
+                    {
+                        if (!entry.getKey().equals(BundleCapabilityImpl.VERSION_ATTR))
+                        {
+                            logger.log(
+                                Logger.LOG_WARNING,
+                                "Unknown R3 export attribute: "
+                                + entry.getKey());
+                        }
+                    }
+
+                    // Remove all other attributes except package version.
+                    clause.m_attrs.clear();
+                    clause.m_attrs.put(BundleCapabilityImpl.VERSION_ATTR, pkgVersion);
+                }
+            }
+        }
+
+        return clauses;
+    }
+
+    private static List<BundleCapability> convertExports(
+        List<ParsedHeaderClause> clauses, BundleRevision owner)
+    {
+        List<BundleCapability> capList = new ArrayList();
+        for (ParsedHeaderClause clause : clauses)
+        {
+            for (String pkgName : clause.m_paths)
+            {
+                // Prepend the package name to the array of attributes.
+                Map<String, Object> attrs = clause.m_attrs;
+                Map<String, Object> newAttrs = new HashMap<String, Object>(attrs.size() + 1);
+                newAttrs.putAll(attrs);
+                newAttrs.put(
+                    BundleRevision.PACKAGE_NAMESPACE,
+                    pkgName);
+
+                // Create package capability and add to capability list.
+                capList.add(
+                    new BundleCapabilityImpl(
+                        owner,
+                        BundleRevision.PACKAGE_NAMESPACE,
+                        clause.m_dirs,
+                        newAttrs));
+            }
+        }
+
+        return capList;
+    }
+
+    public String getManifestVersion()
+    {
+        String manifestVersion = getManifestVersion(m_headerMap);
+        return (manifestVersion == null) ? "1" : manifestVersion;
+    }
+
+    private static String getManifestVersion(Map headerMap)
+    {
+        String manifestVersion = (String) headerMap.get(Constants.BUNDLE_MANIFESTVERSION);
+        return (manifestVersion == null) ? null : manifestVersion.trim();
+    }
+
+    public int getActivationPolicy()
+    {
+        return m_activationPolicy;
+    }
+
+    public String getActivationIncludeDirective()
+    {
+        return m_activationIncludeDir;
+    }
+
+    public String getActivationExcludeDirective()
+    {
+        return m_activationExcludeDir;
+    }
+
+    public boolean isExtension()
+    {
+        return m_isExtension;
+    }
+
+    public String getSymbolicName()
+    {
+        return m_bundleSymbolicName;
+    }
+
+    public Version getBundleVersion()
+    {
+        return m_bundleVersion;
+    }
+
+    public List<BundleCapability> getCapabilities()
+    {
+        return m_capabilities;
+    }
+
+    public List<BundleRequirement> getRequirements()
+    {
+        return m_requirements;
+    }
+
+    public List<R4LibraryClause> getLibraryClauses()
+    {
+        return m_libraryClauses;
+    }
+
+    /**
+     * <p>
+     * This method returns the selected native library metadata from
+     * the manifest. The information is not the raw metadata from the
+     * manifest, but is the native library clause selected according
+     * to the OSGi native library clause selection policy. The metadata
+     * returned by this method will be attached directly to a module and
+     * used for finding its native libraries at run time. To inspect the
+     * raw native library metadata refer to <tt>getLibraryClauses()</tt>.
+     * </p>
+     * <p>
+     * This method returns one of three values:
+     * </p>
+     * <ul>
+     * <li><tt>null</tt> - if the are no native libraries for this module;
+     *     this may also indicate the native libraries are optional and
+     *     did not match the current platform.</li>
+     * <li>Zero-length <tt>R4Library</tt> array - if no matching native library
+     *     clause was found; this bundle should not resolve.</li>
+     * <li>Nonzero-length <tt>R4Library</tt> array - the native libraries
+     *     associated with the matching native library clause.</li>
+     * </ul>
+     *
+     * @return <tt>null</tt> if there are no native libraries, a zero-length
+     *         array if no libraries matched, or an array of selected libraries.
+    **/
+    public List<R4Library> getLibraries()
+    {
+        ArrayList<R4Library> libs = null;
+        try
+        {
+            R4LibraryClause clause = getSelectedLibraryClause();
+            if (clause != null)
+            {
+                String[] entries = clause.getLibraryEntries();
+                libs = new ArrayList<R4Library>(entries.length);
+                int current = 0;
+                for (int i = 0; i < entries.length; i++)
+                {
+                    String name = getName(entries[i]);
+                    boolean found = false;
+                    for (int j = 0; !found && (j < current); j++)
+                    {
+                        found = getName(entries[j]).equals(name);
+                    }
+                    if (!found)
+                    {
+                        libs.add(new R4Library(
+                            clause.getLibraryEntries()[i],
+                            clause.getOSNames(), clause.getProcessors(), clause.getOSVersions(),
+                            clause.getLanguages(), clause.getSelectionFilter()));
+                    }
+                }
+                libs.trimToSize();
+            }
+        }
+        catch (Exception ex)
+        {
+            libs = new ArrayList<R4Library>(0);
+        }
+        return libs;
+    }
+
+    private String getName(String path)
+    {
+        int idx = path.lastIndexOf('/');
+        if (idx > -1)
+        {
+            return path.substring(idx);
+        }
+        return path;
+    }
+
+    private R4LibraryClause getSelectedLibraryClause() throws BundleException
+    {
+        if ((m_libraryClauses != null) && (m_libraryClauses.size() > 0))
+        {
+            List clauseList = new ArrayList();
+
+            // Search for matching native clauses.
+            for (R4LibraryClause libraryClause : m_libraryClauses)
+            {
+                if (libraryClause.match(m_configMap))
+                {
+                    clauseList.add(libraryClause);
+                }
+            }
+
+            // Select the matching native clause.
+            int selected = 0;
+            if (clauseList.isEmpty())
+            {
+                // If optional clause exists, no error thrown.
+                if (m_libraryHeadersOptional)
+                {
+                    return null;
+                }
+                else
+                {
+                    throw new BundleException("Unable to select a native library clause.");
+                }
+            }
+            else if (clauseList.size() == 1)
+            {
+                selected = 0;
+            }
+            else if (clauseList.size() > 1)
+            {
+                selected = firstSortedClause(clauseList);
+            }
+            return ((R4LibraryClause) clauseList.get(selected));
+        }
+
+        return null;
+    }
+
+    private int firstSortedClause(List<R4LibraryClause> clauseList)
+    {
+        ArrayList indexList = new ArrayList();
+        ArrayList selection = new ArrayList();
+
+        // Init index list
+        for (int i = 0; i < clauseList.size(); i++)
+        {
+            indexList.add("" + i);
+        }
+
+        // Select clause with 'osversion' range declared
+        // and get back the max floor of 'osversion' ranges.
+        Version osVersionRangeMaxFloor = new Version(0, 0, 0);
+        for (int i = 0; i < indexList.size(); i++)
+        {
+            int index = Integer.parseInt(indexList.get(i).toString());
+            String[] osversions = ((R4LibraryClause) clauseList.get(index)).getOSVersions();
+            if (osversions != null)
+            {
+                selection.add("" + indexList.get(i));
+            }
+            for (int k = 0; (osversions != null) && (k < osversions.length); k++)
+            {
+                VersionRange range = VersionRange.parse(osversions[k]);
+                if ((range.getFloor()).compareTo(osVersionRangeMaxFloor) >= 0)
+                {
+                    osVersionRangeMaxFloor = range.getFloor();
+                }
+            }
+        }
+
+        if (selection.size() == 1)
+        {
+            return Integer.parseInt(selection.get(0).toString());
+        }
+        else if (selection.size() > 1)
+        {
+            // Keep only selected clauses with an 'osversion'
+            // equal to the max floor of 'osversion' ranges.
+            indexList = selection;
+            selection = new ArrayList();
+            for (int i = 0; i < indexList.size(); i++)
+            {
+                int index = Integer.parseInt(indexList.get(i).toString());
+                String[] osversions = ((R4LibraryClause) clauseList.get(index)).getOSVersions();
+                for (int k = 0; k < osversions.length; k++)
+                {
+                    VersionRange range = VersionRange.parse(osversions[k]);
+                    if ((range.getFloor()).compareTo(osVersionRangeMaxFloor) >= 0)
+                    {
+                        selection.add("" + indexList.get(i));
+                    }
+                }
+            }
+        }
+
+        if (selection.isEmpty())
+        {
+            // Re-init index list.
+            selection.clear();
+            indexList.clear();
+            for (int i = 0; i < clauseList.size(); i++)
+            {
+                indexList.add("" + i);
+            }
+        }
+        else if (selection.size() == 1)
+        {
+            return Integer.parseInt(selection.get(0).toString());
+        }
+        else
+        {
+            indexList = selection;
+            selection.clear();
+        }
+
+        // Keep only clauses with 'language' declared.
+        for (int i = 0; i < indexList.size(); i++)
+        {
+            int index = Integer.parseInt(indexList.get(i).toString());
+            if (((R4LibraryClause) clauseList.get(index)).getLanguages() != null)
+            {
+                selection.add("" + indexList.get(i));
+            }
+        }
+
+        // Return the first sorted clause
+        if (selection.isEmpty())
+        {
+            return 0;
+        }
+        else
+        {
+            return Integer.parseInt(selection.get(0).toString());
+        }
+    }
+
+    private static List<ParsedHeaderClause> calculateImplicitImports(
+        List<BundleCapability> exports, List<ParsedHeaderClause> imports)
+        throws BundleException
+    {
+        List<ParsedHeaderClause> clauseList = new ArrayList();
+
+        // Since all R3 exports imply an import, add a corresponding
+        // requirement for each existing export capability. Do not
+        // duplicate imports.
+        Map map =  new HashMap();
+        // Add existing imports.
+        for (int impIdx = 0; impIdx < imports.size(); impIdx++)
+        {
+            for (int pathIdx = 0; pathIdx < imports.get(impIdx).m_paths.size(); pathIdx++)
+            {
+                map.put(
+                    imports.get(impIdx).m_paths.get(pathIdx),
+                    imports.get(impIdx).m_paths.get(pathIdx));
+            }
+        }
+        // Add import requirement for each export capability.
+        for (int i = 0; i < exports.size(); i++)
+        {
+            if (map.get(exports.get(i).getAttributes()
+                .get(BundleRevision.PACKAGE_NAMESPACE)) == null)
+            {
+                // Convert Version to VersionRange.
+                Map<String, Object> attrs = new HashMap<String, Object>();
+                Object version = exports.get(i).getAttributes().get(Constants.VERSION_ATTRIBUTE);
+                if (version != null)
+                {
+                    attrs.put(
+                        Constants.VERSION_ATTRIBUTE,
+                        VersionRange.parse(version.toString()));
+                }
+
+                List<String> paths = new ArrayList();
+                paths.add((String)
+                    exports.get(i).getAttributes().get(BundleRevision.PACKAGE_NAMESPACE));
+                clauseList.add(
+                    new ParsedHeaderClause(
+                        paths, Collections.EMPTY_MAP, attrs, Collections.EMPTY_MAP));
+            }
+        }
+
+        return clauseList;
+    }
+
+    private static List<BundleCapability> calculateImplicitUses(
+        List<BundleCapability> exports, List<ParsedHeaderClause> imports)
+        throws BundleException
+    {
+        // Add a "uses" directive onto each export of R3 bundles
+        // that references every other import (which will include
+        // exports, since export implies import); this is
+        // necessary since R3 bundles assumed a single class space,
+        // but R4 allows for multiple class spaces.
+        String usesValue = "";
+        for (int i = 0; i < imports.size(); i++)
+        {
+            for (int pathIdx = 0; pathIdx < imports.get(i).m_paths.size(); pathIdx++)
+            {
+                usesValue = usesValue
+                    + ((usesValue.length() > 0) ? "," : "")
+                    + imports.get(i).m_paths.get(pathIdx);
+            }
+        }
+        for (int i = 0; i < exports.size(); i++)
+        {
+            Map<String, String> dirs = new HashMap<String, String>(1);
+            dirs.put(Constants.USES_DIRECTIVE, usesValue);
+            exports.set(i, new BundleCapabilityImpl(
+                exports.get(i).getRevision(),
+                BundleRevision.PACKAGE_NAMESPACE,
+                dirs,
+                exports.get(i).getAttributes()));
+        }
+
+        return exports;
+    }
+
+    private static boolean checkExtensionBundle(Map headerMap) throws BundleException
+    {
+        Object extension = parseExtensionBundleHeader(
+            (String) headerMap.get(Constants.FRAGMENT_HOST));
+
+        if (extension != null)
+        {
+            if (!(Constants.EXTENSION_FRAMEWORK.equals(extension) ||
+                Constants.EXTENSION_BOOTCLASSPATH.equals(extension)))
+            {
+                throw new BundleException(
+                    "Extension bundle must have either 'extension:=framework' or 'extension:=bootclasspath'");
+            }
+            if (headerMap.containsKey(Constants.IMPORT_PACKAGE) ||
+                headerMap.containsKey(Constants.REQUIRE_BUNDLE) ||
+                headerMap.containsKey(Constants.BUNDLE_NATIVECODE) ||
+                headerMap.containsKey(Constants.DYNAMICIMPORT_PACKAGE) ||
+                headerMap.containsKey(Constants.BUNDLE_ACTIVATOR))
+            {
+                throw new BundleException("Invalid extension bundle manifest");
+            }
+            return true;
+        }
+        return false;
+    }
+
+    private static BundleCapabilityImpl parseBundleSymbolicName(
+        BundleRevision owner, Map headerMap)
+        throws BundleException
+    {
+        List<ParsedHeaderClause> clauses = parseStandardHeader(
+            (String) headerMap.get(Constants.BUNDLE_SYMBOLICNAME));
+        if (clauses.size() > 0)
+        {
+            if (clauses.size() > 1)
+            {
+                throw new BundleException(
+                    "Cannot have multiple symbolic names: "
+                        + headerMap.get(Constants.BUNDLE_SYMBOLICNAME));
+            }
+            else if (clauses.get(0).m_paths.size() > 1)
+            {
+                throw new BundleException(
+                    "Cannot have multiple symbolic names: "
+                        + headerMap.get(Constants.BUNDLE_SYMBOLICNAME));
+            }
+
+            // Get bundle version.
+            Version bundleVersion = Version.emptyVersion;
+            if (headerMap.get(Constants.BUNDLE_VERSION) != null)
+            {
+                try
+                {
+                    bundleVersion = Version.parseVersion(
+                        (String) headerMap.get(Constants.BUNDLE_VERSION));
+                }
+                catch (RuntimeException ex)
+                {
+                    // R4 bundle versions must parse, R3 bundle version may not.
+                    String mv = getManifestVersion(headerMap);
+                    if (mv != null)
+                    {
+                        throw ex;
+                    }
+                    bundleVersion = Version.emptyVersion;
+                }
+            }
+
+            // Create a require capability and return it.
+            String symName = (String) clauses.get(0).m_paths.get(0);
+            clauses.get(0).m_attrs.put(BundleRevision.BUNDLE_NAMESPACE, symName);
+            clauses.get(0).m_attrs.put(Constants.BUNDLE_VERSION_ATTRIBUTE, bundleVersion);
+            return new BundleCapabilityImpl(
+                owner,
+                BundleRevision.BUNDLE_NAMESPACE,
+                clauses.get(0).m_dirs,
+                clauses.get(0).m_attrs);
+        }
+
+        return null;
+    }
+
+    private static BundleCapabilityImpl addIdentityCapability(BundleRevision owner,
+        Map headerMap, BundleCapabilityImpl bundleCap)
+    {
+        Map<String, Object> attrs = new HashMap<String, Object>();
+
+        attrs.put(IdentityNamespace.IDENTITY_NAMESPACE,
+            bundleCap.getAttributes().get(BundleNamespace.BUNDLE_NAMESPACE));
+        attrs.put(IdentityNamespace.CAPABILITY_TYPE_ATTRIBUTE,
+            headerMap.get(Constants.FRAGMENT_HOST) == null
+            ? IdentityNamespace.TYPE_BUNDLE
+            : IdentityNamespace.TYPE_FRAGMENT);
+        attrs.put(IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE,
+            bundleCap.getAttributes().get(Constants.BUNDLE_VERSION_ATTRIBUTE));
+
+        if (headerMap.get(Constants.BUNDLE_COPYRIGHT) != null)
+        {
+            attrs.put(IdentityNamespace.CAPABILITY_COPYRIGHT_ATTRIBUTE,
+                headerMap.get(Constants.BUNDLE_COPYRIGHT));
+        }
+
+        if (headerMap.get(Constants.BUNDLE_DESCRIPTION) != null)
+        {
+            attrs.put(IdentityNamespace.CAPABILITY_DESCRIPTION_ATTRIBUTE,
+                headerMap.get(Constants.BUNDLE_DESCRIPTION));
+        }
+        if (headerMap.get(Constants.BUNDLE_DOCURL) != null)
+        {
+            attrs.put(IdentityNamespace.CAPABILITY_DOCUMENTATION_ATTRIBUTE,
+                headerMap.get(Constants.BUNDLE_DOCURL));
+        }
+        if (headerMap.get(BUNDLE_LICENSE_HEADER) != null)
+        {
+            attrs.put(IdentityNamespace.CAPABILITY_LICENSE_ATTRIBUTE,
+                headerMap.get(BUNDLE_LICENSE_HEADER));
+        }
+
+        Map<String, String> dirs;
+        if (bundleCap.getDirectives().get(Constants.SINGLETON_DIRECTIVE) != null)
+        {
+            dirs = Collections.singletonMap(IdentityNamespace.CAPABILITY_SINGLETON_DIRECTIVE,
+                    bundleCap.getDirectives().get(Constants.SINGLETON_DIRECTIVE));
+        }
+        else
+        {
+            dirs = Collections.emptyMap();
+        }
+        return new BundleCapabilityImpl(owner, IdentityNamespace.IDENTITY_NAMESPACE, dirs, attrs);
+    }
+
+    private static List<BundleRequirementImpl> parseFragmentHost(
+        Logger logger, BundleRevision owner, Map headerMap)
+        throws BundleException
+    {
+        List<BundleRequirementImpl> reqs = new ArrayList();
+
+        String mv = getManifestVersion(headerMap);
+        if ((mv != null) && mv.equals("2"))
+        {
+            List<ParsedHeaderClause> clauses = parseStandardHeader(
+                (String) headerMap.get(Constants.FRAGMENT_HOST));
+            if (clauses.size() > 0)
+            {
+                // Make sure that only one fragment host symbolic name is specified.
+                if (clauses.size() > 1)
+                {
+                    throw new BundleException(
+                        "Fragments cannot have multiple hosts: "
+                            + headerMap.get(Constants.FRAGMENT_HOST));
+                }
+                else if (clauses.get(0).m_paths.size() > 1)
+                {
+                    throw new BundleException(
+                        "Fragments cannot have multiple hosts: "
+                            + headerMap.get(Constants.FRAGMENT_HOST));
+                }
+
+                // If the bundle-version attribute is specified, then convert
+                // it to the proper type.
+                Object value = clauses.get(0).m_attrs.get(Constants.BUNDLE_VERSION_ATTRIBUTE);
+                value = (value == null) ? "0.0.0" : value;
+                if (value != null)
+                {
+                    clauses.get(0).m_attrs.put(
+                        Constants.BUNDLE_VERSION_ATTRIBUTE,
+                        VersionRange.parse(value.toString()));
+                }
+
+                // Note that we use a linked hash map here to ensure the
+                // host symbolic name is first, which will make indexing
+                // more efficient.
+// TODO: OSGi R4.3 - This is ordering is kind of hacky.
+                // Prepend the host symbolic name to the map of attributes.
+                Map<String, Object> attrs = clauses.get(0).m_attrs;
+                Map<String, Object> newAttrs = new LinkedHashMap<String, Object>(attrs.size() + 1);
+                // We want this first from an indexing perspective.
+                newAttrs.put(
+                    BundleRevision.HOST_NAMESPACE,
+                    clauses.get(0).m_paths.get(0));
+                newAttrs.putAll(attrs);
+                // But we need to put it again to make sure it wasn't overwritten.
+                newAttrs.put(
+                    BundleRevision.HOST_NAMESPACE,
+                    clauses.get(0).m_paths.get(0));
+
+                // Create filter now so we can inject filter directive.
+                SimpleFilter sf = SimpleFilter.convert(newAttrs);
+
+                // Inject filter directive.
+// TODO: OSGi R4.3 - Can we insert this on demand somehow?
+                Map<String, String> dirs = clauses.get(0).m_dirs;
+                Map<String, String> newDirs = new HashMap<String, String>(dirs.size() + 1);
+                newDirs.putAll(dirs);
+                newDirs.put(
+                    Constants.FILTER_DIRECTIVE,
+                    sf.toString());
+
+                reqs.add(new BundleRequirementImpl(
+                    owner, BundleRevision.HOST_NAMESPACE,
+                    newDirs,
+                    newAttrs));
+            }
+        }
+        else if (headerMap.get(Constants.FRAGMENT_HOST) != null)
+        {
+            String s = (String) headerMap.get(Constants.BUNDLE_SYMBOLICNAME);
+            s = (s == null) ? (String) headerMap.get(Constants.BUNDLE_NAME) : s;
+            s = (s == null) ? headerMap.toString() : s;
+            logger.log(
+                Logger.LOG_WARNING,
+                "Only R4 bundles can be fragments: " + s);
+        }
+
+        return reqs;
+    }
+
+    public static List<BundleCapability> parseExportHeader(
+        Logger logger, BundleRevision owner, String header, String bsn, Version bv)
+    {
+
+        List<BundleCapability> caps = null;
+        try
+        {
+            List<ParsedHeaderClause> exportClauses = parseStandardHeader(header);
+            exportClauses = normalizeExportClauses(logger, exportClauses, "2", bsn, bv);
+            caps = convertExports(exportClauses, owner);
+        }
+        catch (BundleException ex)
+        {
+            caps = null;
+        }
+        return caps;
+    }
+
+    private static List<BundleRequirement> parseBreeHeader(String header, BundleRevision owner)
+    {
+        List<String> filters = new ArrayList<String>();
+        for (String entry : parseDelimitedString(header, ","))
+        {
+            List<String> names = parseDelimitedString(entry, "/");
+            List<String> left = parseDelimitedString(names.get(0), "-");
+
+            String lName = left.get(0);
+            Version lVer;
+            try
+            {
+                lVer = Version.parseVersion(left.get(1));
+            }
+            catch (Exception ex)
+            {
+                // Version doesn't parse. Make it part of the name.
+                lName = names.get(0);
+                lVer = null;
+            }
+
+            String rName = null;
+            Version rVer = null;
+            if (names.size() > 1)
+            {
+                List<String> right = parseDelimitedString(names.get(1), "-");
+                rName = right.get(0);
+                try
+                {
+                    rVer = Version.parseVersion(right.get(1));
+                }
+                catch (Exception ex)
+                {
+                    rName = names.get(1);
+                    rVer = null;
+                }
+            }
+
+            String versionClause;
+            if (lVer != null)
+            {
+                if ((rVer != null) && (!rVer.equals(lVer)))
+                {
+                    // Both versions are defined, but different. Make each of them part of the name
+                    lName = names.get(0);
+                    rName = names.get(1);
+                    versionClause = null;
+                }
+                else
+                {
+                    versionClause = getBreeVersionClause(lVer);
+                }
+            }
+            else
+            {
+                versionClause = getBreeVersionClause(rVer);
+            }
+
+            if ("J2SE".equals(lName))
+            {
+                // J2SE is not used in the Capability variant of BREE, use JavaSE here
+                // This can only happen with the lName part...
+                lName = "JavaSE";
+            }
+
+            String nameClause;
+            if (rName != null)
+                nameClause = "(" + ExecutionEnvironmentNamespace.EXECUTION_ENVIRONMENT_NAMESPACE + "=" + lName + "/" + rName + ")";
+            else
+                nameClause = "(" + ExecutionEnvironmentNamespace.EXECUTION_ENVIRONMENT_NAMESPACE + "=" + lName + ")";
+
+            String filter;
+            if (versionClause != null)
+                filter = "(&" + nameClause + versionClause + ")";
+            else
+                filter = nameClause;
+
+            filters.add(filter);
+        }
+
+        if (filters.size() == 0)
+        {
+            return Collections.emptyList();
+        }
+        else
+        {
+            String reqFilter;
+            if (filters.size() == 1)
+            {
+                reqFilter = filters.get(0);
+            }
+            else
+            {
+                // If there are more BREE filters, we need to or them together
+                StringBuilder sb = new StringBuilder("(|");
+                for (String f : filters)
+                {
+                    sb.append(f);
+                }
+                sb.append(")");
+                reqFilter = sb.toString();
+            }
+
+            SimpleFilter sf = SimpleFilter.parse(reqFilter);
+            return Collections.<BundleRequirement>singletonList(new BundleRequirementImpl(
+                owner,
+                ExecutionEnvironmentNamespace.EXECUTION_ENVIRONMENT_NAMESPACE,
+                Collections.singletonMap(ExecutionEnvironmentNamespace.REQUIREMENT_FILTER_DIRECTIVE, reqFilter),
+                Collections.<String, Object>emptyMap(),
+                sf));
+        }
+    }
+
+    private static String getBreeVersionClause(Version ver) {
+        if (ver == null)
+            return null;
+
+        return "(" + ExecutionEnvironmentNamespace.CAPABILITY_VERSION_ATTRIBUTE + "=" + ver + ")";
+    }
+
+    private static List<ParsedHeaderClause> normalizeRequireClauses(
+        Logger logger, List<ParsedHeaderClause> clauses, String mv)
+    {
+        // R3 bundles cannot require other bundles.
+        if (!mv.equals("2"))
+        {
+            clauses.clear();
+        }
+        else
+        {
+            // Convert bundle version attribute to VersionRange type.
+            for (ParsedHeaderClause clause : clauses)
+            {
+                Object value = clause.m_attrs.get(Constants.BUNDLE_VERSION_ATTRIBUTE);
+                if (value != null)
+                {
+                    clause.m_attrs.put(
+                        Constants.BUNDLE_VERSION_ATTRIBUTE,
+                        VersionRange.parse(value.toString()));
+                }
+            }
+        }
+
+        return clauses;
+    }
+
+    private static List<BundleRequirementImpl> convertRequires(
+        List<ParsedHeaderClause> clauses, BundleRevision owner)
+    {
+        List<BundleRequirementImpl> reqList = new ArrayList();
+        for (ParsedHeaderClause clause : clauses)
+        {
+            for (String path : clause.m_paths)
+            {
+                // Prepend the bundle symbolic name to the array of attributes.
+                Map<String, Object> attrs = clause.m_attrs;
+                // Note that we use a linked hash map here to ensure the
+                // symbolic name attribute is first, which will make indexing
+                // more efficient.
+// TODO: OSGi R4.3 - This is ordering is kind of hacky.
+                // Prepend the symbolic name to the array of attributes.
+                Map<String, Object> newAttrs = new LinkedHashMap<String, Object>(attrs.size() + 1);
+                // We want this first from an indexing perspective.
+                newAttrs.put(
+                    BundleRevision.BUNDLE_NAMESPACE,
+                    path);
+                newAttrs.putAll(attrs);
+                // But we need to put it again to make sure it wasn't overwritten.
+                newAttrs.put(
+                    BundleRevision.BUNDLE_NAMESPACE,
+                    path);
+
+                // Create filter now so we can inject filter directive.
+                SimpleFilter sf = SimpleFilter.convert(newAttrs);
+
+                // Inject filter directive.
+// TODO: OSGi R4.3 - Can we insert this on demand somehow?
+                Map<String, String> dirs = clause.m_dirs;
+                Map<String, String> newDirs = new HashMap<String, String>(dirs.size() + 1);
+                newDirs.putAll(dirs);
+                newDirs.put(
+                    Constants.FILTER_DIRECTIVE,
+                    sf.toString());
+
+                // Create package requirement and add to requirement list.
+                reqList.add(
+                    new BundleRequirementImpl(
+                        owner,
+                        BundleRevision.BUNDLE_NAMESPACE,
+                        newDirs,
+                        newAttrs));
+            }
+        }
+
+        return reqList;
+    }
+
+    public static String parseExtensionBundleHeader(String header)
+        throws BundleException
+    {
+        List<ParsedHeaderClause> clauses = parseStandardHeader(header);
+
+        String result = null;
+
+        if (clauses.size() == 1)
+        {
+            for (Entry<String, String> entry : clauses.get(0).m_dirs.entrySet())
+            {
+                if (Constants.EXTENSION_DIRECTIVE.equals(entry.getKey()))
+                {
+                    result = entry.getValue();
+                }
+            }
+
+            if (FelixConstants.SYSTEM_BUNDLE_SYMBOLICNAME.equals(clauses.get(0).m_paths.get(0)) ||
+                Constants.SYSTEM_BUNDLE_SYMBOLICNAME.equals(clauses.get(0).m_paths.get(0)))
+            {
+                result = (result == null) ? Constants.EXTENSION_FRAMEWORK : result;
+            }
+            else if (result != null)
+            {
+                throw new BundleException(
+                    "Only the system bundle can have extension bundles.");
+            }
+        }
+
+        return result;
+    }
+
+    private void parseActivationPolicy(Map headerMap)
+    {
+        m_activationPolicy = BundleRevisionImpl.EAGER_ACTIVATION;
+
+        List<ParsedHeaderClause> clauses = parseStandardHeader(
+            (String) headerMap.get(Constants.BUNDLE_ACTIVATIONPOLICY));
+
+        if (clauses.size() > 0)
+        {
+            // Just look for a "path" matching the lazy policy, ignore
+            // everything else.
+            for (String path : clauses.get(0).m_paths)
+            {
+                if (path.equals(Constants.ACTIVATION_LAZY))
+                {
+                    m_activationPolicy = BundleRevisionImpl.LAZY_ACTIVATION;
+                    for (Entry<String, String> entry : clauses.get(0).m_dirs.entrySet())
+                    {
+                        if (entry.getKey().equalsIgnoreCase(Constants.INCLUDE_DIRECTIVE))
+                        {
+                            m_activationIncludeDir = entry.getValue();
+                        }
+                        else if (entry.getKey().equalsIgnoreCase(Constants.EXCLUDE_DIRECTIVE))
+                        {
+                            m_activationExcludeDir = entry.getValue();
+                        }
+                    }
+                    break;
+                }
+            }
+        }
+    }
+
+    // Like this: path; path; dir1:=dirval1; dir2:=dirval2; attr1=attrval1; attr2=attrval2,
+    //            path; path; dir1:=dirval1; dir2:=dirval2; attr1=attrval1; attr2=attrval2
+    public static void main(String[] headers)
+    {
+        String header = headers[0];
+        if (header != null)
+        {
+            if (header.length() == 0)
+            {
+                throw new IllegalArgumentException(
+                    "A header cannot be an empty string.");
+            }
+            List<ParsedHeaderClause> clauses = parseStandardHeader(header);
+
+            for (ParsedHeaderClause clause : clauses)
+            {
+                System.out.println("PATHS " + clause.m_paths);
+                System.out.println("    DIRS  " + clause.m_dirs);
+                System.out.println("    ATTRS " + clause.m_attrs);
+                System.out.println("    TYPES " + clause.m_types);
+            }
+
+        }
+    }
+
+    private static final char EOF = (char) -1;
+
+    private static char charAt(int pos, String headers, int length)
+    {
+        if (pos >= length)
+        {
+            return EOF;
+        }
+        return headers.charAt(pos);
+    }
+
+    private static final int CLAUSE_START = 0;
+    private static final int PARAMETER_START = 1;
+    private static final int KEY = 2;
+    private static final int DIRECTIVE_OR_TYPEDATTRIBUTE = 4;
+    private static final int ARGUMENT = 8;
+    private static final int VALUE = 16;
+
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    private static List<ParsedHeaderClause> parseStandardHeader(String header)
+    {
+        List<ParsedHeaderClause> clauses = new ArrayList<ParsedHeaderClause>();
+        if (header == null)
+        {
+            return clauses;
+        }
+        ParsedHeaderClause clause = null;
+        String key = null;
+        Map targetMap = null;
+        int state = CLAUSE_START;
+        int currentPosition = 0;
+        int startPosition = 0;
+        int length = header.length();
+        boolean quoted = false;
+        boolean escaped = false;
+
+        char currentChar = EOF;
+        do
+        {
+            currentChar = charAt(currentPosition, header, length);
+            switch (state)
+            {
+                case CLAUSE_START:
+                    clause = new ParsedHeaderClause(
+                            new ArrayList<String>(),
+                            new HashMap<String, String>(),
+                            new HashMap<String, Object>(),
+                            new HashMap<String, String>());
+                    clauses.add(clause);
+                    state = PARAMETER_START;
+                case PARAMETER_START:
+                    startPosition = currentPosition;
+                    state = KEY;
+                case KEY:
+                    switch (currentChar)
+                    {
+                        case ':':
+                        case '=':
+                            key = header.substring(startPosition, currentPosition).trim();
+                            startPosition = currentPosition + 1;
+                            targetMap = clause.m_attrs;
+                            state = currentChar == ':' ? DIRECTIVE_OR_TYPEDATTRIBUTE : ARGUMENT;
+                            break;
+                        case EOF:
+                        case ',':
+                        case ';':
+                            clause.m_paths.add(header.substring(startPosition, currentPosition).trim());
+                            state = currentChar == ',' ? CLAUSE_START : PARAMETER_START;
+                            break;
+                        default:
+                            break;
+                    }
+                    currentPosition++;
+                    break;
+                case DIRECTIVE_OR_TYPEDATTRIBUTE:
+                    switch(currentChar)
+                    {
+                        case '=':
+                            if (startPosition != currentPosition)
+                            {
+                                clause.m_types.put(key, header.substring(startPosition, currentPosition).trim());
+                            }
+                            else
+                            {
+                                targetMap = clause.m_dirs;
+                            }
+                            state = ARGUMENT;
+                            startPosition = currentPosition + 1;
+                            break;
+                        default:
+                            break;
+                    }
+                    currentPosition++;
+                    break;
+                case ARGUMENT:
+                    if (currentChar == '\"')
+                    {
+                        quoted = true;
+                        currentPosition++;
+                    }
+                    else
+                    {
+                        quoted = false;
+                    }
+                    if (!Character.isWhitespace(currentChar)) {
+                    	state = VALUE;
+                    }
+                    else {
+                    	currentPosition++;
+                    }
+                    break;
+                case VALUE:
+                    if (escaped)
+                    {
+                        escaped = false;
+                    }
+                    else
+                    {
+                        if (currentChar == '\\' )
+                        {
+                            escaped = true;
+                        }
+                        else if (quoted && currentChar == '\"')
+                        {
+                            quoted = false;
+                        }
+                        else if (!quoted)
+                        {
+                            String value = null;
+                            switch(currentChar)
+                            {
+                                case EOF:
+                                case ';':
+                                case ',':
+                                    value = header.substring(startPosition, currentPosition).trim();
+                                    if (value.startsWith("\"") && value.endsWith("\""))
+                                    {
+                                        value = value.substring(1, value.length() - 1);
+                                    }
+                                    if (targetMap.put(key, value) != null)
+                                    {
+                                        throw new IllegalArgumentException(
+                                                "Duplicate '" + key + "' in: " + header);
+                                    }
+                                    state = currentChar == ';' ? PARAMETER_START : CLAUSE_START;
+                                    break;
+                                default:
+                                    break;
+                            }
+                        }
+                    }
+                    currentPosition++;
+                    break;
+                default:
+                    break;
+            }
+        } while ( currentChar != EOF);
+
+        if (state > PARAMETER_START)
+        {
+            throw new IllegalArgumentException("Unable to parse header: " + header);
+        }
+        return clauses;
+    }
+
+    public static List<String> parseDelimitedString(String value, String delim)
+    {
+        return parseDelimitedString(value, delim, true);
+    }
+
+    /**
+     * Parses delimited string and returns an array containing the tokens. This
+     * parser obeys quotes, so the delimiter character will be ignored if it is
+     * inside of a quote. This method assumes that the quote character is not
+     * included in the set of delimiter characters.
+     * @param value the delimited string to parse.
+     * @param delim the characters delimiting the tokens.
+     * @return a list of string or an empty list if there are none.
+    **/
+    public static List<String> parseDelimitedString(String value, String delim, boolean trim)
+    {
+        if (value == null)
+        {
+           value = "";
+        }
+
+        List<String> list = new ArrayList();
+
+        int CHAR = 1;
+        int DELIMITER = 2;
+        int STARTQUOTE = 4;
+        int ENDQUOTE = 8;
+
+        StringBuffer sb = new StringBuffer();
+
+        int expecting = (CHAR | DELIMITER | STARTQUOTE);
+
+        boolean isEscaped = false;
+        for (int i = 0; i < value.length(); i++)
+        {
+            char c = value.charAt(i);
+
+            boolean isDelimiter = (delim.indexOf(c) >= 0);
+
+            if (!isEscaped && (c == '\\'))
+            {
+                isEscaped = true;
+                continue;
+            }
+
+            if (isEscaped)
+            {
+                sb.append(c);
+            }
+            else if (isDelimiter && ((expecting & DELIMITER) > 0))
+            {
+                if (trim)
+                {
+                    list.add(sb.toString().trim());
+                }
+                else
+                {
+                    list.add(sb.toString());
+                }
+                sb.delete(0, sb.length());
+                expecting = (CHAR | DELIMITER | STARTQUOTE);
+            }
+            else if ((c == '"') && ((expecting & STARTQUOTE) > 0))
+            {
+                sb.append(c);
+                expecting = CHAR | ENDQUOTE;
+            }
+            else if ((c == '"') && ((expecting & ENDQUOTE) > 0))
+            {
+                sb.append(c);
+                expecting = (CHAR | STARTQUOTE | DELIMITER);
+            }
+            else if ((expecting & CHAR) > 0)
+            {
+                sb.append(c);
+            }
+            else
+            {
+                throw new IllegalArgumentException("Invalid delimited string: " + value);
+            }
+
+            isEscaped = false;
+        }
+
+        if (sb.length() > 0)
+        {
+            if (trim)
+            {
+                list.add(sb.toString().trim());
+            }
+            else
+            {
+                list.add(sb.toString());
+            }
+        }
+
+        return list;
+    }
+
+    /**
+     * Parses native code manifest headers.
+     * @param libStrs an array of native library manifest header
+     *        strings from the bundle manifest.
+     * @return an array of <tt>LibraryInfo</tt> objects for the
+     *         passed in strings.
+    **/
+    private static List<R4LibraryClause> parseLibraryStrings(
+        Logger logger, List<String> libStrs)
+        throws IllegalArgumentException
+    {
+        if (libStrs == null)
+        {
+            return new ArrayList<R4LibraryClause>(0);
+        }
+
+        List<R4LibraryClause> libList = new ArrayList(libStrs.size());
+
+        for (int i = 0; i < libStrs.size(); i++)
+        {
+            R4LibraryClause clause = R4LibraryClause.parse(logger, libStrs.get(i));
+            libList.add(clause);
+        }
+
+        return libList;
+    }
+}
diff --git a/src/main/java/org/apache/felix/framework/util/manifestparser/ParsedHeaderClause.java b/src/main/java/org/apache/felix/framework/util/manifestparser/ParsedHeaderClause.java
new file mode 100644
index 0000000..7c393f2
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/util/manifestparser/ParsedHeaderClause.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.util.manifestparser;
+
+import java.util.List;
+import java.util.Map;
+
+public class ParsedHeaderClause
+{
+    public final List<String> m_paths;
+    public final Map<String, String> m_dirs;
+    public final Map<String, Object> m_attrs;
+    public final Map<String, String> m_types;
+
+    public ParsedHeaderClause(
+        List<String> paths, Map<String, String> dirs, Map<String, Object> attrs,
+        Map<String, String> types)
+    {
+        m_paths = paths;
+        m_dirs = dirs;
+        m_attrs = attrs;
+        m_types = types;
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/felix/framework/util/manifestparser/R4Library.java b/src/main/java/org/apache/felix/framework/util/manifestparser/R4Library.java
new file mode 100644
index 0000000..e82c2a1
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/util/manifestparser/R4Library.java
@@ -0,0 +1,187 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.util.manifestparser;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import org.osgi.framework.Constants;
+
+public class R4Library
+{
+    private String m_libraryFile;
+    private String[] m_osnames;
+    private String[] m_processors;
+    private String[] m_osversions;
+    private String[] m_languages;
+    private String m_selectionFilter;
+
+    public R4Library(
+        String libraryFile, String[] osnames, String[] processors, String[] osversions,
+        String[] languages, String selectionFilter) throws Exception
+    {
+        m_libraryFile = libraryFile;
+        m_osnames = osnames;
+        m_processors = processors;
+        m_osversions = osversions;
+        m_languages = languages;
+        m_selectionFilter = selectionFilter;
+    }
+
+    public String getEntryName()
+    {
+        return m_libraryFile;
+    }
+
+    public String[] getOSNames()
+    {
+        return m_osnames;
+    }
+
+    public String[] getProcessors()
+    {
+        return m_processors;
+    }
+
+    public String[] getOSVersions()
+    {
+        return m_osversions;
+    }
+
+    public String[] getLanguages()
+    {
+        return m_languages;
+    }
+
+    public String getSelectionFilter()
+    {
+        return m_selectionFilter;
+    }
+
+    /**
+     * <p>
+     * Determines if the specified native library name matches this native
+     * library definition.
+     * </p>
+     * @param name the native library name to try to match.
+     * @return <tt>true</tt> if this native library name matches this native
+     *         library definition; <tt>false</tt> otherwise.
+    **/
+    public boolean match(Map configMap, String name)
+    {
+        // First, check for an exact match.
+        boolean matched = false;
+        if (m_libraryFile.equals(name) || m_libraryFile.endsWith("/" + name))
+        {
+            matched = true;
+        }
+
+        // Then check the mapped name.
+        String libname = System.mapLibraryName(name);
+        // As well as any additional library file extensions.
+        List<String> exts = ManifestParser.parseDelimitedString(
+            (String) configMap.get(Constants.FRAMEWORK_LIBRARY_EXTENSIONS), ",");
+        if (exts == null)
+        {
+            exts = new ArrayList<String>();
+        }
+        // For Mac OSX, try dylib too.
+        if (libname.endsWith(".jnilib") && m_libraryFile.endsWith(".dylib"))
+        {
+            exts.add("dylib");
+        }
+        if (libname.endsWith(".dylib") && m_libraryFile.endsWith(".jnilib"))
+        {
+            exts.add("jnilib");
+        }
+        // Loop until we find a match or not.
+        int extIdx = -1;
+        while (!matched && (extIdx < exts.size()))
+        {
+            // Check if the current name matches.
+            if (m_libraryFile.equals(libname) || m_libraryFile.endsWith("/" + libname))
+            {
+                matched = true;
+            }
+
+            // Increment extension index.
+            extIdx++;
+
+            // If we have other native library extensions to try, then
+            // calculate the new native library name.
+            if (!matched && (extIdx < exts.size()))
+            {
+                int idx = libname.lastIndexOf(".");
+                libname = (idx < 0)
+                    ? libname + "." + exts.get(extIdx)
+                    : libname.substring(0, idx + 1) + exts.get(extIdx);
+            }
+        }
+
+        return matched;
+    }
+
+    public String toString()
+    {
+        if (m_libraryFile != null)
+        {
+            StringBuffer sb = new StringBuffer();
+            sb.append(m_libraryFile);
+            for (int i = 0; (m_osnames != null) && (i < m_osnames.length); i++)
+            {
+                sb.append(';');
+                sb.append(Constants.BUNDLE_NATIVECODE_OSNAME);
+                sb.append('=');
+                sb.append(m_osnames[i]);
+            }
+            for (int i = 0; (m_processors != null) && (i < m_processors.length); i++)
+            {
+                sb.append(';');
+                sb.append(Constants.BUNDLE_NATIVECODE_PROCESSOR);
+                sb.append('=');
+                sb.append(m_processors[i]);
+            }
+            for (int i = 0; (m_osversions != null) && (i < m_osversions.length); i++)
+            {
+                sb.append(';');
+                sb.append(Constants.BUNDLE_NATIVECODE_OSVERSION);
+                sb.append('=');
+                sb.append(m_osversions[i]);
+            }
+            for (int i = 0; (m_languages != null) && (i < m_languages.length); i++)
+            {
+                sb.append(';');
+                sb.append(Constants.BUNDLE_NATIVECODE_LANGUAGE);
+                sb.append('=');
+                sb.append(m_languages[i]);
+            }
+            if (m_selectionFilter != null)
+            {
+                sb.append(';');
+                sb.append(Constants.SELECTION_FILTER_ATTRIBUTE);
+                sb.append('=');
+                sb.append('\'');
+                sb.append(m_selectionFilter);
+            }
+
+            return sb.toString();
+        }
+        return "*";
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/felix/framework/util/manifestparser/R4LibraryClause.java b/src/main/java/org/apache/felix/framework/util/manifestparser/R4LibraryClause.java
new file mode 100644
index 0000000..0299041
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/util/manifestparser/R4LibraryClause.java
@@ -0,0 +1,529 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.util.manifestparser;
+
+import java.util.*;
+
+import org.apache.felix.framework.Logger;
+import org.apache.felix.framework.util.FelixConstants;
+import org.apache.felix.framework.util.VersionRange;
+import org.osgi.framework.*;
+
+public class R4LibraryClause
+{
+    private final String[] m_libraryEntries;
+    private final String[] m_osnames;
+    private final String[] m_processors;
+    private final String[] m_osversions;
+    private final String[] m_languages;
+    private final String m_selectionFilter;
+
+    public R4LibraryClause(String[] libraryEntries, String[] osnames,
+        String[] processors, String[] osversions, String[] languages,
+        String selectionFilter)
+    {
+        m_libraryEntries = libraryEntries;
+        m_osnames = osnames;
+        m_processors = processors;
+        m_osversions = osversions;
+        m_languages = languages;
+        m_selectionFilter = selectionFilter;
+    }
+
+    public R4LibraryClause(R4LibraryClause library)
+    {
+        m_libraryEntries = library.m_libraryEntries;
+        m_osnames = library.m_osnames;
+        m_osversions = library.m_osversions;
+        m_processors = library.m_processors;
+        m_languages = library.m_languages;
+        m_selectionFilter = library.m_selectionFilter;
+    }
+
+    public String[] getLibraryEntries()
+    {
+        return m_libraryEntries;
+    }
+
+    public String[] getOSNames()
+    {
+        return m_osnames;
+    }
+
+    public String[] getProcessors()
+    {
+        return m_processors;
+    }
+
+    public String[] getOSVersions()
+    {
+        return m_osversions;
+    }
+
+    public String[] getLanguages()
+    {
+        return m_languages;
+    }
+
+    public String getSelectionFilter()
+    {
+        return m_selectionFilter;
+    }
+
+    public boolean match(Map configMap) throws BundleException
+    {
+        String normal_osname = normalizeOSName((String) configMap.get(Constants.FRAMEWORK_OS_NAME));
+        String normal_processor = normalizeProcessor((String) configMap.get(Constants.FRAMEWORK_PROCESSOR));
+        String normal_osversion = normalizeOSVersion((String) configMap.get(Constants.FRAMEWORK_OS_VERSION));
+        String normal_language = (String) configMap.get(Constants.FRAMEWORK_LANGUAGE);
+
+        // Check library's osname.
+        if (!checkOSNames(normal_osname, getOSNames()))
+        {
+            return false;
+        }
+
+        // Check library's processor.
+        if (!checkProcessors(normal_processor, getProcessors()))
+        {
+            return false;
+        }
+
+        // Check library's osversion if specified.
+        if ((getOSVersions() != null) &&
+            (getOSVersions().length > 0) &&
+            !checkOSVersions(normal_osversion, getOSVersions()))
+        {
+            return false;
+        }
+
+        // Check library's language if specified.
+        if ((getLanguages() != null) &&
+            (getLanguages().length > 0) &&
+            !checkLanguages(normal_language, getLanguages()))
+        {
+            return false;
+        }
+
+        // Check library's selection-filter if specified.
+        if ((getSelectionFilter() != null) &&
+            (getSelectionFilter().length() >= 0) &&
+            !checkSelectionFilter(configMap, getSelectionFilter()))
+        {
+            return false;
+        }
+
+        return true;
+    }
+
+    private boolean checkOSNames(String currentOSName, String[] osnames)
+    {
+        boolean win32 = currentOSName.startsWith("win") && !currentOSName.equals("windowsce");
+
+        for (int i = 0; (osnames != null) && (i < osnames.length); i++)
+        {
+            if (osnames[i].equals(currentOSName) ||
+                ("win32".equals(osnames[i]) && win32))
+            {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean checkProcessors(String currentProcessor, String[] processors)
+    {
+        for (int i = 0; (processors != null) && (i < processors.length); i++)
+        {
+            if (processors[i].equals(currentProcessor))
+            {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean checkOSVersions(String currentOSVersion, String[] osversions)
+        throws BundleException
+    {
+        for (int i = 0; (osversions != null) && (i < osversions.length); i++)
+        {
+            try
+            {
+                VersionRange range = VersionRange.parse(osversions[i]);
+                if (range.isInRange(new Version(currentOSVersion)))
+                {
+                    return true;
+                }
+            }
+            catch (Exception ex)
+            {
+                throw new BundleException(
+                    "Error evaluating osversion: " + osversions[i], ex);
+            }
+        }
+        return false;
+    }
+
+    private boolean checkLanguages(String currentLanguage, String[] languages)
+    {
+        for (int i = 0; (languages != null) && (i < languages.length); i++)
+        {
+            if (languages[i].equals(currentLanguage))
+            {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean checkSelectionFilter(Map configMap, String expr)
+        throws BundleException
+    {
+        // Get all framework properties
+        Dictionary dict = new Hashtable();
+        for (Iterator i = configMap.keySet().iterator(); i.hasNext(); )
+        {
+            Object key = i.next();
+            dict.put(key, configMap.get(key));
+        }
+        // Compute expression
+        try
+        {
+            Filter filter = FrameworkUtil.createFilter(expr);
+            return filter.match(dict);
+        }
+        catch (Exception ex)
+        {
+            throw new BundleException(
+                "Error evaluating filter expression: " + expr, ex);
+        }
+    }
+
+    public static R4LibraryClause parse(Logger logger, String s)
+    {
+        try
+        {
+            if ((s == null) || (s.length() == 0))
+            {
+                return null;
+            }
+
+            if (s.equals(FelixConstants.BUNDLE_NATIVECODE_OPTIONAL))
+            {
+                return new R4LibraryClause(null, null, null, null, null, null);
+            }
+
+            // The tokens are separated by semicolons and may include
+            // any number of libraries along with one set of associated
+            // properties.
+            StringTokenizer st = new StringTokenizer(s, ";");
+            String[] libEntries = new String[st.countTokens()];
+            List osNameList = new ArrayList();
+            List osVersionList = new ArrayList();
+            List processorList = new ArrayList();
+            List languageList = new ArrayList();
+            String selectionFilter = null;
+            int libCount = 0;
+            while (st.hasMoreTokens())
+            {
+                String token = st.nextToken().trim();
+                if (token.indexOf('=') < 0)
+                {
+                    // Remove the slash, if necessary.
+                    libEntries[libCount] = (token.charAt(0) == '/')
+                        ? token.substring(1)
+                        : token;
+                    libCount++;
+                }
+                else
+                {
+                    // Check for valid native library properties; defined as
+                    // a property name, an equal sign, and a value.
+                    // NOTE: StringTokenizer can not be used here because
+                    // a value can contain one or more "=" too, e.g.,
+                    // selection-filter="(org.osgi.framework.windowing.system=gtk)"
+                    String property = null;
+                    String value = null;
+                    if (!(token.indexOf("=") > 1))
+                    {
+                        throw new IllegalArgumentException(
+                            "Bundle manifest native library entry malformed: " + token);
+                    }
+                    else
+                    {
+                        property = (token.substring(0, token.indexOf("=")))
+                            .trim().toLowerCase();
+                        value = (token.substring(token.indexOf("=") + 1, token
+                            .length())).trim();
+                    }
+
+                    // Values may be quoted, so remove quotes if present.
+                    if (value.charAt(0) == '"')
+                    {
+                        // This should always be true, otherwise the
+                        // value wouldn't be properly quoted, but we
+                        // will check for safety.
+                        if (value.charAt(value.length() - 1) == '"')
+                        {
+                            value = value.substring(1, value.length() - 1);
+                        }
+                        else
+                        {
+                            value = value.substring(1);
+                        }
+                    }
+                    // Add the value to its corresponding property list.
+                    if (property.equals(Constants.BUNDLE_NATIVECODE_OSNAME))
+                    {
+                        osNameList.add(normalizeOSName(value));
+                    }
+                    else if (property.equals(Constants.BUNDLE_NATIVECODE_OSVERSION))
+                    {
+                        osVersionList.add(normalizeOSVersion(value));
+                    }
+                    else if (property.equals(Constants.BUNDLE_NATIVECODE_PROCESSOR))
+                    {
+                        processorList.add(normalizeProcessor(value));
+                    }
+                    else if (property.equals(Constants.BUNDLE_NATIVECODE_LANGUAGE))
+                    {
+                        languageList.add(value);
+                    }
+                    else if (property.equals(Constants.SELECTION_FILTER_ATTRIBUTE))
+                    {
+// TODO: NATIVE - I believe we can have multiple selection filters too.
+                        selectionFilter = value;
+                    }
+                }
+            }
+
+            if (libCount == 0)
+            {
+                return null;
+            }
+
+            // Shrink lib file array.
+            String[] actualLibEntries = new String[libCount];
+            System.arraycopy(libEntries, 0, actualLibEntries, 0, libCount);
+            return new R4LibraryClause(
+                actualLibEntries,
+                (String[]) osNameList.toArray(new String[osNameList.size()]),
+                (String[]) processorList.toArray(new String[processorList.size()]),
+                (String[]) osVersionList.toArray(new String[osVersionList.size()]),
+                (String[]) languageList.toArray(new String[languageList.size()]),
+                selectionFilter);
+        }
+        catch (RuntimeException ex)
+        {
+            logger.log(Logger.LOG_ERROR,
+                "Error parsing native library header.", ex);
+            throw ex;
+        }
+    }
+
+    public static String normalizeOSName(String value)
+    {
+        value = value.toLowerCase();
+
+        if (value.startsWith("win"))
+        {
+            String os = "win";
+            if (value.indexOf("32") >= 0 || value.indexOf("*") >= 0)
+            {
+                os = "win32";
+            }
+            else if (value.indexOf("95") >= 0)
+            {
+                os = "windows95";
+            }
+            else if (value.indexOf("98") >= 0)
+            {
+                os = "windows98";
+            }
+            else if (value.indexOf("nt") >= 0)
+            {
+                os = "windowsnt";
+            }
+            else if (value.indexOf("2000") >= 0)
+            {
+                os = "windows2000";
+            }
+            else if (value.indexOf("2003") >= 0)
+            {
+                os = "windows2003";
+            }
+            else if (value.indexOf("2008") >= 0)
+            {
+                os = "windowsserver2008";
+            }
+            else if (value.indexOf("2012") >= 0)
+            {
+                os = "windowsserver2012";
+            }
+            else if (value.indexOf("xp") >= 0)
+            {
+                os = "windowsxp";
+            }
+            else if (value.indexOf("ce") >= 0)
+            {
+                os = "windowsce";
+            }
+            else if (value.indexOf("vista") >= 0)
+            {
+                os = "windowsvista";
+            }
+            else if ((value.indexOf(" 7") >= 0) || value.equals("win7"))
+            {
+                os = "windows7";
+            }
+            else if ((value.indexOf(" 8") >= 0) || value.equals("win8"))
+            {
+                os = "windows8";
+            }
+            else if ((value.indexOf(" 9") >= 0) || value.equals("win9"))
+            {
+                os = "windows9";
+            }
+            return os;
+        }
+        else if (value.startsWith("linux"))
+        {
+            return "linux";
+        }
+        else if (value.startsWith("aix"))
+        {
+            return "aix";
+        }
+        else if (value.startsWith("digitalunix"))
+        {
+            return "digitalunix";
+        }
+        else if (value.startsWith("hpux"))
+        {
+            return "hpux";
+        }
+        else if (value.startsWith("irix"))
+        {
+            return "irix";
+        }
+        else if (value.startsWith("macos") || value.startsWith("mac os"))
+        {
+            return "macos";
+        }
+        else if (value.startsWith("netware"))
+        {
+            return "netware";
+        }
+        else if (value.startsWith("openbsd"))
+        {
+            return "openbsd";
+        }
+        else if (value.startsWith("netbsd"))
+        {
+            return "netbsd";
+        }
+        else if (value.startsWith("os2") || value.startsWith("os/2"))
+        {
+            return "os2";
+        }
+        else if (value.startsWith("qnx") || value.startsWith("procnto"))
+        {
+            return "qnx";
+        }
+        else if (value.startsWith("solaris"))
+        {
+            return "solaris";
+        }
+        else if (value.startsWith("sunos"))
+        {
+            return "sunos";
+        }
+        else if (value.startsWith("vxworks"))
+        {
+            return "vxworks";
+        }
+        return value;
+    }
+
+    public static String normalizeProcessor(String value)
+    {
+        value = value.toLowerCase();
+
+        if (value.startsWith("x86-64") || value.startsWith("amd64") ||
+            value.startsWith("em64") || value.startsWith("x86_64"))
+        {
+            return "x86-64";
+        }
+        else if (value.startsWith("x86") || value.startsWith("pentium")
+            || value.startsWith("i386") || value.startsWith("i486")
+            || value.startsWith("i586") || value.startsWith("i686"))
+        {
+            return "x86";
+        }
+        else if (value.startsWith("68k"))
+        {
+            return "68k";
+        }
+        else if (value.startsWith("arm"))
+        {
+            return "arm";
+        }
+        else if (value.startsWith("alpha"))
+        {
+            return "alpha";
+        }
+        else if (value.startsWith("ignite") || value.startsWith("psc1k"))
+        {
+            return "ignite";
+        }
+        else if (value.startsWith("mips"))
+        {
+            return "mips";
+        }
+        else if (value.startsWith("parisc"))
+        {
+            return "parisc";
+        }
+        else if (value.startsWith("powerpc") || value.startsWith("power")
+            || value.startsWith("ppc"))
+        {
+            return "powerpc";
+        }
+        else if (value.startsWith("sparc"))
+        {
+            return "sparc";
+        }
+        return value;
+    }
+
+    public static String normalizeOSVersion(String value)
+    {
+        // Header: 'Bundle-NativeCode', Parameter: 'osversion'
+        // Standardized 'osversion': major.minor.micro, only digits
+        try
+        {
+            return VersionRange.parse(value).toString();
+        }
+        catch (Exception ex)
+        {
+            return Version.emptyVersion.toString();
+        }
+    }
+}
diff --git a/src/main/java/org/apache/felix/framework/wiring/BundleCapabilityImpl.java b/src/main/java/org/apache/felix/framework/wiring/BundleCapabilityImpl.java
new file mode 100644
index 0000000..0128568
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/wiring/BundleCapabilityImpl.java
@@ -0,0 +1,206 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.wiring;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.StringTokenizer;
+import org.apache.felix.framework.capabilityset.SimpleFilter;
+import org.apache.felix.framework.util.ImmutableMap;
+import org.apache.felix.framework.util.Util;
+import org.apache.felix.framework.util.manifestparser.ManifestParser;
+import org.osgi.framework.Constants;
+import org.osgi.framework.wiring.BundleCapability;
+import org.osgi.framework.wiring.BundleRevision;
+import org.osgi.resource.Resource;
+
+public class BundleCapabilityImpl implements BundleCapability
+{
+    public static final String VERSION_ATTR = "version";
+
+    private final BundleRevision m_revision;
+    private final String m_namespace;
+    private final Map<String, String> m_dirs;
+    private final Map<String, Object> m_attrs;
+    private final List<String> m_uses;
+    private final List<List<String>> m_includeFilter;
+    private final List<List<String>> m_excludeFilter;
+    private final Set<String> m_mandatory;
+
+    public BundleCapabilityImpl(BundleRevision revision, String namespace,
+        Map<String, String> dirs, Map<String, Object> attrs)
+    {
+        m_namespace = namespace;
+        m_revision = revision;
+        m_dirs = ImmutableMap.newInstance(dirs);
+        m_attrs = ImmutableMap.newInstance(attrs);
+
+        // Find all export directives: uses, mandatory, include, and exclude.
+
+        List<String> uses = Collections.EMPTY_LIST;
+        String value = m_dirs.get(Constants.USES_DIRECTIVE);
+        if (value != null)
+        {
+            // Parse these uses directive.
+            StringTokenizer tok = new StringTokenizer(value, ",");
+            uses = new ArrayList(tok.countTokens());
+            while (tok.hasMoreTokens())
+            {
+                uses.add(tok.nextToken().trim());
+            }
+        }
+        m_uses = uses;
+
+        value = m_dirs.get(Constants.INCLUDE_DIRECTIVE);
+        if (value != null)
+        {
+            List<String> filters = ManifestParser.parseDelimitedString(value, ",");
+            m_includeFilter = new ArrayList<List<String>>(filters.size());
+            for (int filterIdx = 0; filterIdx < filters.size(); filterIdx++)
+            {
+                List<String> substrings = SimpleFilter.parseSubstring(filters.get(filterIdx));
+                m_includeFilter.add(substrings);
+            }
+        }
+        else
+        {
+            m_includeFilter = null;
+        }
+
+        value = m_dirs.get(Constants.EXCLUDE_DIRECTIVE);
+        if (value != null)
+        {
+            List<String> filters = ManifestParser.parseDelimitedString(value, ",");
+            m_excludeFilter = new ArrayList<List<String>>(filters.size());
+            for (int filterIdx = 0; filterIdx < filters.size(); filterIdx++)
+            {
+                List<String> substrings = SimpleFilter.parseSubstring(filters.get(filterIdx));
+                m_excludeFilter.add(substrings);
+            }
+        }
+        else
+        {
+            m_excludeFilter = null;
+        }
+
+        Set<String> mandatory = Collections.EMPTY_SET;
+        value = m_dirs.get(Constants.MANDATORY_DIRECTIVE);
+        if (value != null)
+        {
+            List<String> names = ManifestParser.parseDelimitedString(value, ",");
+            mandatory = new HashSet<String>(names.size());
+            for (String name : names)
+            {
+                // If attribute exists, then record it as mandatory.
+                if (m_attrs.containsKey(name))
+                {
+                    mandatory.add(name);
+                }
+                // Otherwise, report an error.
+                else
+                {
+                    throw new IllegalArgumentException(
+                        "Mandatory attribute '" + name + "' does not exist.");
+                }
+            }
+        }
+        m_mandatory = mandatory;
+    }
+
+    public BundleRevision getResource()
+    {
+        return m_revision;
+    }
+
+    public BundleRevision getRevision()
+    {
+        return m_revision;
+    }
+
+    public String getNamespace()
+    {
+        return m_namespace;
+    }
+
+    public Map<String, String> getDirectives()
+    {
+        return m_dirs;
+    }
+
+    public Map<String, Object> getAttributes()
+    {
+        return m_attrs;
+    }
+
+    public boolean isAttributeMandatory(String name)
+    {
+        return !m_mandatory.isEmpty() && m_mandatory.contains(name);
+    }
+
+    public List<String> getUses()
+    {
+        return m_uses;
+    }
+
+    public boolean isIncluded(String name)
+    {
+        if ((m_includeFilter == null) && (m_excludeFilter == null))
+        {
+            return true;
+        }
+
+        // Get the class name portion of the target class.
+        String className = Util.getClassName(name);
+
+        // If there are no include filters then all classes are included
+        // by default, otherwise try to find one match.
+        boolean included = (m_includeFilter == null);
+        for (int i = 0;
+            (!included) && (m_includeFilter != null) && (i < m_includeFilter.size());
+            i++)
+        {
+            included = SimpleFilter.compareSubstring(m_includeFilter.get(i), className);
+        }
+
+        // If there are no exclude filters then no classes are excluded
+        // by default, otherwise try to find one match.
+        boolean excluded = false;
+        for (int i = 0;
+            (!excluded) && (m_excludeFilter != null) && (i < m_excludeFilter.size());
+            i++)
+        {
+            excluded = SimpleFilter.compareSubstring(m_excludeFilter.get(i), className);
+        }
+        return included && !excluded;
+    }
+
+    @Override
+    public String toString()
+    {
+        if (m_revision == null)
+        {
+            return m_attrs.toString();
+        }
+        return "[" + m_revision + "] " + m_namespace + "; " + m_attrs;
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/felix/framework/wiring/BundleRequirementImpl.java b/src/main/java/org/apache/felix/framework/wiring/BundleRequirementImpl.java
new file mode 100644
index 0000000..398272e
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/wiring/BundleRequirementImpl.java
@@ -0,0 +1,113 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.wiring;
+
+import java.util.Collections;
+import java.util.Map;
+import org.apache.felix.framework.capabilityset.CapabilitySet;
+import org.apache.felix.framework.capabilityset.SimpleFilter;
+import org.apache.felix.framework.util.ImmutableMap;
+import org.osgi.framework.Constants;
+import org.osgi.framework.wiring.BundleCapability;
+import org.osgi.framework.wiring.BundleRequirement;
+import org.osgi.framework.wiring.BundleRevision;
+import org.osgi.resource.Resource;
+
+public class BundleRequirementImpl implements BundleRequirement
+{
+    private final BundleRevision m_revision;
+    private final String m_namespace;
+    private final SimpleFilter m_filter;
+    private final boolean m_optional;
+    private final Map<String, String> m_dirs;
+    private final Map<String, Object> m_attrs;
+
+    public BundleRequirementImpl(
+        BundleRevision revision, String namespace,
+        Map<String, String> dirs, Map<String, Object> attrs, SimpleFilter filter)
+    {
+        m_revision = revision;
+        m_namespace = namespace;
+        m_dirs = ImmutableMap.newInstance(dirs);
+        m_attrs = ImmutableMap.newInstance(attrs);
+        m_filter = filter;
+
+        // Find resolution import directives.
+        boolean optional = false;
+        if (m_dirs.containsKey(Constants.RESOLUTION_DIRECTIVE)
+            && m_dirs.get(Constants.RESOLUTION_DIRECTIVE).equals(Constants.RESOLUTION_OPTIONAL))
+        {
+            optional = true;
+        }
+        m_optional = optional;
+    }
+
+    public BundleRequirementImpl(
+        BundleRevision revision, String namespace,
+        Map<String, String> dirs, Map<String, Object> attrs)
+    {
+        this(revision, namespace, dirs, Collections.EMPTY_MAP, SimpleFilter.convert(attrs));
+    }
+
+    public String getNamespace()
+    {
+        return m_namespace;
+    }
+
+    public Map<String, String> getDirectives()
+    {
+        return m_dirs;
+    }
+
+    public Map<String, Object> getAttributes()
+    {
+        return m_attrs;
+    }
+
+    public BundleRevision getResource()
+    {
+        return m_revision;
+    }
+
+    public BundleRevision getRevision()
+    {
+        return m_revision;
+    }
+
+    public boolean matches(BundleCapability cap)
+    {
+        return CapabilitySet.matches((BundleCapabilityImpl) cap, getFilter());
+    }
+
+    public boolean isOptional()
+    {
+        return m_optional;
+    }
+
+    public SimpleFilter getFilter()
+    {
+        return m_filter;
+    }
+
+    @Override
+    public String toString()
+    {
+        return "[" + m_revision + "] " + m_namespace + "; " + getFilter().toString();
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/felix/framework/wiring/BundleWireImpl.java b/src/main/java/org/apache/felix/framework/wiring/BundleWireImpl.java
new file mode 100644
index 0000000..8925943
--- /dev/null
+++ b/src/main/java/org/apache/felix/framework/wiring/BundleWireImpl.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.wiring;
+
+import org.osgi.framework.wiring.BundleCapability;
+import org.osgi.framework.wiring.BundleRequirement;
+import org.osgi.framework.wiring.BundleRevision;
+import org.osgi.framework.wiring.BundleWire;
+import org.osgi.framework.wiring.BundleWiring;
+
+public class BundleWireImpl implements BundleWire
+{
+    private final BundleRevision m_requirer;
+    private final BundleRequirement m_req;
+    private final BundleRevision m_provider;
+    private final BundleCapability m_cap;
+
+    public BundleWireImpl(BundleRevision requirer, BundleRequirement req,
+        BundleRevision provider, BundleCapability cap)
+    {
+        m_requirer = requirer;
+        m_req = req;
+        m_provider = provider;
+        m_cap = cap;
+    }
+
+    public BundleRevision getRequirer()
+    {
+        return m_requirer;
+    }
+
+    public BundleWiring getRequirerWiring()
+    {
+        return m_requirer.getWiring();
+    }
+
+    public BundleRequirement getRequirement()
+    {
+        return m_req;
+    }
+
+    public BundleRevision getProvider()
+    {
+        return m_provider;
+    }
+
+    public BundleWiring getProviderWiring()
+    {
+        return m_provider.getWiring();
+    }
+
+    public BundleCapability getCapability()
+    {
+        return m_cap;
+    }
+
+    public String toString()
+    {
+        return m_req
+            + " -> "
+            + "[" + m_provider + "]";
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/osgi/framework/AdaptPermission.java b/src/main/java/org/osgi/framework/AdaptPermission.java
new file mode 100644
index 0000000..fbe70c1
--- /dev/null
+++ b/src/main/java/org/osgi/framework/AdaptPermission.java
@@ -0,0 +1,613 @@
+/*
+ * Copyright (c) OSGi Alliance (2010, 2012). All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.framework;
+
+import java.io.IOException;
+import java.io.NotSerializableException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.ObjectStreamField;
+import java.security.AccessController;
+import java.security.BasicPermission;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A bundle's authority to adapt an object to a type.
+ * 
+ * <p>
+ * {@code AdaptPermission} has one action: {@code adapt}.
+ * 
+ * @ThreadSafe
+ * @version $Id: 3bc095bd294db2d8ea25971a3d71991de1495b1a $
+ */
+public final class AdaptPermission extends BasicPermission {
+
+	private static final long						serialVersionUID	= 1L;
+
+	/**
+	 * The action string {@code initiate}.
+	 */
+	public final static String						ADAPT				= "adapt";
+
+	private final static int						ACTION_ADAPT		= 0x00000001;
+	private final static int						ACTION_ALL			= ACTION_ADAPT;
+	final static int								ACTION_NONE			= 0;
+
+	/**
+	 * The actions mask.
+	 */
+	transient int									action_mask;
+
+	/**
+	 * The actions in canonical form.
+	 * 
+	 * @serial
+	 */
+	private volatile String							actions				= null;
+
+	/**
+	 * The bundle used by this AdaptPermission.
+	 */
+	transient final Bundle							bundle;
+
+	/**
+	 * This holds a Filter matching object used to evaluate the filter in
+	 * implies.
+	 */
+	transient Filter								filter;
+
+	/**
+	 * This map holds the properties of the permission, used to match a filter
+	 * in implies. This is not initialized until necessary, and then cached in
+	 * this object.
+	 */
+	private transient volatile Map<String, Object>	properties;
+
+	/**
+	 * Creates a new granted {@code AdaptPermission} object.
+	 * 
+	 * This constructor must only be used to create a permission that is going
+	 * to be checked.
+	 * <p>
+	 * Examples:
+	 * 
+	 * <pre>
+	 * (adaptClass=com.acme.*)
+	 * (&(signer=\*,o=ACME,c=US)(adaptClass=com.acme.*))
+	 * (signer=\*,o=ACME,c=US)
+	 * </pre>
+	 * 
+	 * <p>
+	 * When a signer key is used within the filter expression the signer value
+	 * must escape the special filter chars ('*', '(', ')').
+	 * <p>
+	 * The name is specified as a filter expression. The filter gives access to
+	 * the following attributes:
+	 * <ul>
+	 * <li>signer - A Distinguished Name chain used to sign the exporting
+	 * bundle. Wildcards in a DN are not matched according to the filter string
+	 * rules, but according to the rules defined for a DN chain.</li>
+	 * <li>location - The location of the exporting bundle.</li>
+	 * <li>id - The bundle ID of the exporting bundle.</li>
+	 * <li>name - The symbolic name of the exporting bundle.</li>
+	 * <li>adaptClass - The name of the type to which an object can be adapted.</li>
+	 * </ul>
+	 * Filter attribute names are processed in a case sensitive manner.
+	 * 
+	 * @param filter A filter expression. Filter attribute names are processed
+	 *        in a case sensitive manner. A special value of {@code "*"} can be
+	 *        used to match all adaptations.
+	 * @param actions {@code adapt}.
+	 * @throws IllegalArgumentException If the filter has an invalid syntax.
+	 */
+	public AdaptPermission(String filter, String actions) {
+		this(parseFilter(filter), parseActions(actions));
+	}
+
+	/**
+	 * Creates a new requested {@code AdaptPermission} object to be used by the
+	 * code that must perform {@code checkPermission}. {@code AdaptPermission}
+	 * objects created with this constructor cannot be added to an
+	 * {@code AdaptPermission} permission collection.
+	 * 
+	 * @param adaptClass The name of the type to which an object can be adapted.
+	 * @param adaptableBundle The bundle associated with the object being
+	 *        adapted.
+	 * @param actions {@code adapt}.
+	 */
+	public AdaptPermission(String adaptClass, Bundle adaptableBundle, String actions) {
+		super(adaptClass);
+		setTransients(null, parseActions(actions));
+		this.bundle = adaptableBundle;
+		if (adaptClass == null) {
+			throw new NullPointerException("adaptClass must not be null");
+		}
+		if (adaptableBundle == null) {
+			throw new NullPointerException("adaptableBundle must not be null");
+		}
+	}
+
+	/**
+	 * Package private constructor used by AdaptPermissionCollection.
+	 * 
+	 * @param filter name filter
+	 * @param mask action mask
+	 */
+	AdaptPermission(Filter filter, int mask) {
+		super((filter == null) ? "*" : filter.toString());
+		setTransients(filter, mask);
+		this.bundle = null;
+	}
+
+	/**
+	 * Called by constructors and when deserialized.
+	 * 
+	 * @param filter Permission's filter or {@code null} for wildcard.
+	 * @param mask action mask
+	 */
+	private void setTransients(Filter filter, int mask) {
+		this.filter = filter;
+		if ((mask == ACTION_NONE) || ((mask & ACTION_ALL) != mask)) {
+			throw new IllegalArgumentException("invalid action string");
+		}
+		this.action_mask = mask;
+	}
+
+	/**
+	 * Parse action string into action mask.
+	 * 
+	 * @param actions Action string.
+	 * @return action mask.
+	 */
+	private static int parseActions(String actions) {
+		boolean seencomma = false;
+
+		int mask = ACTION_NONE;
+
+		if (actions == null) {
+			return mask;
+		}
+
+		char[] a = actions.toCharArray();
+
+		int i = a.length - 1;
+		if (i < 0)
+			return mask;
+
+		while (i != -1) {
+			char c;
+
+			// skip whitespace
+			while ((i != -1) && ((c = a[i]) == ' ' || c == '\r' || c == '\n' || c == '\f' || c == '\t'))
+				i--;
+
+			// check for the known strings
+			int matchlen;
+
+			if (i >= 4 && (a[i - 4] == 'a' || a[i - 4] == 'A')
+					&& (a[i - 3] == 'd' || a[i - 3] == 'D')
+					&& (a[i - 2] == 'a' || a[i - 2] == 'A')
+					&& (a[i - 1] == 'p' || a[i - 1] == 'P')
+					&& (a[i] == 't' || a[i] == 'T')) {
+				matchlen = 5;
+				mask |= ACTION_ADAPT;
+
+			} else {
+				// parse error
+				throw new IllegalArgumentException("invalid actions: " + actions);
+			}
+
+			// make sure we didn't just match the tail of a word
+			// like "ackbarfadapt". Also, skip to the comma.
+			seencomma = false;
+			while (i >= matchlen && !seencomma) {
+				switch (a[i - matchlen]) {
+					case ',' :
+						seencomma = true;
+						/* FALLTHROUGH */
+					case ' ' :
+					case '\r' :
+					case '\n' :
+					case '\f' :
+					case '\t' :
+						break;
+					default :
+						throw new IllegalArgumentException("invalid permission: " + actions);
+				}
+				i--;
+			}
+
+			// point i at the location of the comma minus one (or -1).
+			i -= matchlen;
+		}
+
+		if (seencomma) {
+			throw new IllegalArgumentException("invalid actions: " + actions);
+		}
+
+		return mask;
+	}
+
+	/**
+	 * Parse filter string into a Filter object.
+	 * 
+	 * @param filterString The filter string to parse.
+	 * @return a Filter for this bundle.
+	 * @throws IllegalArgumentException If the filter syntax is invalid.
+	 */
+	private static Filter parseFilter(String filterString) {
+		filterString = filterString.trim();
+		if (filterString.equals("*")) {
+			return null;
+		}
+		try {
+			return FrameworkUtil.createFilter(filterString);
+		} catch (InvalidSyntaxException e) {
+			IllegalArgumentException iae = new IllegalArgumentException("invalid filter");
+			iae.initCause(e);
+			throw iae;
+		}
+	}
+
+	/**
+	 * Determines if the specified permission is implied by this object.
+	 * 
+	 * <p>
+	 * This method checks that the filter of the target is implied by the adapt
+	 * class name of this object. The list of {@code AdaptPermission} actions
+	 * must either match or allow for the list of the target object to imply the
+	 * target {@code AdaptPermission} action.
+	 * <p>
+	 * 
+	 * @param p The requested permission.
+	 * @return {@code true} if the specified permission is implied by this
+	 *         object; {@code false} otherwise.
+	 */
+	public boolean implies(Permission p) {
+		if (!(p instanceof AdaptPermission)) {
+			return false;
+		}
+		AdaptPermission requested = (AdaptPermission) p;
+		if (bundle != null) {
+			return false;
+		}
+		// if requested permission has a filter, then it is an invalid argument
+		if (requested.filter != null) {
+			return false;
+		}
+		return implies0(requested, ACTION_NONE);
+	}
+
+	/**
+	 * Internal implies method. Used by the implies and the permission
+	 * collection implies methods.
+	 * 
+	 * @param requested The requested AdaptPermission which has already be
+	 *        validated as a proper argument. The requested AdaptPermission must
+	 *        not have a filter expression.
+	 * @param effective The effective actions with which to start.
+	 * @return {@code true} if the specified permission is implied by this
+	 *         object; {@code false} otherwise.
+	 */
+	boolean implies0(AdaptPermission requested, int effective) {
+		/* check actions first - much faster */
+		effective |= action_mask;
+		final int desired = requested.action_mask;
+		if ((effective & desired) != desired) {
+			return false;
+		}
+		/* Get filter */
+		Filter f = filter;
+		if (f == null) {
+			// it's "*"
+			return true;
+		}
+		return f.matches(requested.getProperties());
+	}
+
+	/**
+	 * Returns the canonical string representation of the
+	 * {@code AdaptPermission} actions.
+	 * 
+	 * <p>
+	 * Always returns present {@code AdaptPermission} actions in the following
+	 * order: {@code adapt}.
+	 * 
+	 * @return Canonical string representation of the {@code AdaptPermission}
+	 *         actions.
+	 */
+	public String getActions() {
+		String result = actions;
+		if (result == null) {
+			actions = result = ADAPT;
+		}
+		return result;
+	}
+
+	/**
+	 * Returns a new {@code PermissionCollection} object suitable for storing
+	 * {@code AdaptPermission} objects.
+	 * 
+	 * @return A new {@code PermissionCollection} object.
+	 */
+	public PermissionCollection newPermissionCollection() {
+		return new AdaptPermissionCollection();
+	}
+
+	/**
+	 * Determines the equality of two {@code AdaptPermission} objects.
+	 * 
+	 * This method checks that specified permission has the same name and
+	 * {@code AdaptPermission} actions as this {@code AdaptPermission} object.
+	 * 
+	 * @param obj The object to test for equality with this
+	 *        {@code AdaptPermission} object.
+	 * @return {@code true} if {@code obj} is a {@code AdaptPermission}, and has
+	 *         the same name and actions as this {@code AdaptPermission} object;
+	 *         {@code false} otherwise.
+	 */
+	public boolean equals(Object obj) {
+		if (obj == this) {
+			return true;
+		}
+
+		if (!(obj instanceof AdaptPermission)) {
+			return false;
+		}
+
+		AdaptPermission cp = (AdaptPermission) obj;
+
+		return (action_mask == cp.action_mask) && getName().equals(cp.getName()) && ((bundle == cp.bundle) || ((bundle != null) && bundle.equals(cp.bundle)));
+	}
+
+	/**
+	 * Returns the hash code value for this object.
+	 * 
+	 * @return A hash code value for this object.
+	 */
+	public int hashCode() {
+		int h = 31 * 17 + getName().hashCode();
+		h = 31 * h + getActions().hashCode();
+		if (bundle != null) {
+			h = 31 * h + bundle.hashCode();
+		}
+		return h;
+	}
+
+	/**
+	 * WriteObject is called to save the state of this permission object to a
+	 * stream. The actions are serialized, and the superclass takes care of the
+	 * name.
+	 */
+	private synchronized void writeObject(java.io.ObjectOutputStream s) throws IOException {
+		if (bundle != null) {
+			throw new NotSerializableException("cannot serialize");
+		}
+		// Write out the actions. The superclass takes care of the name
+		// call getActions to make sure actions field is initialized
+		if (actions == null)
+			getActions();
+		s.defaultWriteObject();
+	}
+
+	/**
+	 * readObject is called to restore the state of this permission from a
+	 * stream.
+	 */
+	private synchronized void readObject(java.io.ObjectInputStream s) throws IOException, ClassNotFoundException {
+		// Read in the action, then initialize the rest
+		s.defaultReadObject();
+		setTransients(parseFilter(getName()), parseActions(actions));
+	}
+
+	/**
+	 * Called by {@code <@link AdaptPermission#implies(Permission)>}. This
+	 * method is only called on a requested permission which cannot have a
+	 * filter set.
+	 * 
+	 * @return a map of properties for this permission.
+	 */
+	private Map<String, Object> getProperties() {
+		Map<String, Object> result = properties;
+		if (result != null) {
+			return result;
+		}
+		final Map<String, Object> map = new HashMap<String, Object>(5);
+		map.put("adaptClass", getName());
+		if (bundle != null) {
+			AccessController.doPrivileged(new PrivilegedAction<Object>() {
+				public Object run() {
+					map.put("id", new Long(bundle.getBundleId()));
+					map.put("location", bundle.getLocation());
+					String name = bundle.getSymbolicName();
+					if (name != null) {
+						map.put("name", name);
+					}
+					SignerProperty signer = new SignerProperty(bundle);
+					if (signer.isBundleSigned()) {
+						map.put("signer", signer);
+					}
+					return null;
+				}
+			});
+		}
+		return properties = map;
+	}
+}
+
+/**
+ * Stores a set of {@code AdaptPermission} permissions.
+ * 
+ * @see java.security.Permission
+ * @see java.security.Permissions
+ * @see java.security.PermissionCollection
+ */
+
+final class AdaptPermissionCollection extends PermissionCollection {
+	static final long						serialVersionUID	= -3350758995234427603L;
+	/**
+	 * Collection of permissions.
+	 * 
+	 * @serial
+	 * @GuardedBy this
+	 */
+	private Map<String, AdaptPermission>	permissions;
+
+	/**
+	 * Boolean saying if "*" is in the collection.
+	 * 
+	 * @serial
+	 * @GuardedBy this
+	 */
+	private boolean							all_allowed;
+
+	/**
+	 * Create an empty AdaptPermissions object.
+	 */
+	public AdaptPermissionCollection() {
+		permissions = new HashMap<String, AdaptPermission>();
+		all_allowed = false;
+	}
+
+	/**
+	 * Adds a permission to this permission collection.
+	 * 
+	 * @param permission The {@code AdaptPermission} object to add.
+	 * @throws IllegalArgumentException If the specified permission is not a
+	 *         {@code AdaptPermission} instance or was constructed with a Bundle
+	 *         object.
+	 * @throws SecurityException If this {@code AdaptPermissionCollection}
+	 *         object has been marked read-only.
+	 */
+	public void add(final Permission permission) {
+		if (!(permission instanceof AdaptPermission)) {
+			throw new IllegalArgumentException("invalid permission: " + permission);
+		}
+		if (isReadOnly()) {
+			throw new SecurityException("attempt to add a Permission to a " + "readonly PermissionCollection");
+		}
+
+		final AdaptPermission ap = (AdaptPermission) permission;
+		if (ap.bundle != null) {
+			throw new IllegalArgumentException("cannot add to collection: " + ap);
+		}
+
+		final String name = ap.getName();
+		synchronized (this) {
+			Map<String, AdaptPermission> pc = permissions;
+			final AdaptPermission existing = pc.get(name);
+			if (existing != null) {
+				final int oldMask = existing.action_mask;
+				final int newMask = ap.action_mask;
+				if (oldMask != newMask) {
+					pc.put(name, new AdaptPermission(existing.filter, oldMask | newMask));
+
+				}
+			} else {
+				pc.put(name, ap);
+			}
+
+			if (!all_allowed) {
+				if (name.equals("*")) {
+					all_allowed = true;
+				}
+			}
+		}
+	}
+
+	/**
+	 * Determines if the specified permissions implies the permissions expressed
+	 * in {@code permission}.
+	 * 
+	 * @param permission The Permission object to compare with this
+	 *        {@code AdaptPermission} object.
+	 * @return {@code true} if {@code permission} is a proper subset of a
+	 *         permission in the set; {@code false} otherwise.
+	 */
+	public boolean implies(final Permission permission) {
+		if (!(permission instanceof AdaptPermission)) {
+			return false;
+		}
+		final AdaptPermission requested = (AdaptPermission) permission;
+		/* if requested permission has a filter, then it is an invalid argument */
+		if (requested.filter != null) {
+			return false;
+		}
+
+		int effective = AdaptPermission.ACTION_NONE;
+
+		Collection<AdaptPermission> perms;
+		synchronized (this) {
+			Map<String, AdaptPermission> pc = permissions;
+			/* short circuit if the "*" Permission was added */
+			if (all_allowed) {
+				AdaptPermission ap = pc.get("*");
+				if (ap != null) {
+					effective |= ap.action_mask;
+					final int desired = requested.action_mask;
+					if ((effective & desired) == desired) {
+						return true;
+					}
+				}
+			}
+			perms = pc.values();
+		}
+		/* iterate one by one over filteredPermissions */
+		for (AdaptPermission perm : perms) {
+			if (perm.implies0(requested, effective)) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+	/**
+	 * Returns an enumeration of all {@code AdaptPermission} objects in the
+	 * container.
+	 * 
+	 * @return Enumeration of all {@code AdaptPermission} objects.
+	 */
+	public synchronized Enumeration<Permission> elements() {
+		List<Permission> all = new ArrayList<Permission>(permissions.values());
+		return Collections.enumeration(all);
+	}
+
+	/* serialization logic */
+	private static final ObjectStreamField[]	serialPersistentFields	= {new ObjectStreamField("permissions", HashMap.class), new ObjectStreamField("all_allowed", Boolean.TYPE)};
+
+	private synchronized void writeObject(ObjectOutputStream out) throws IOException {
+		ObjectOutputStream.PutField pfields = out.putFields();
+		pfields.put("permissions", permissions);
+		pfields.put("all_allowed", all_allowed);
+		out.writeFields();
+	}
+
+	private synchronized void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
+		ObjectInputStream.GetField gfields = in.readFields();
+		permissions = (HashMap<String, AdaptPermission>) gfields.get("permissions", null);
+		all_allowed = gfields.get("all_allowed", false);
+	}
+}
diff --git a/src/main/java/org/osgi/framework/AdminPermission.java b/src/main/java/org/osgi/framework/AdminPermission.java
new file mode 100644
index 0000000..324360c
--- /dev/null
+++ b/src/main/java/org/osgi/framework/AdminPermission.java
@@ -0,0 +1,1004 @@
+/*
+ * Copyright (c) OSGi Alliance (2000, 2012). All Rights Reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.framework;
+
+import java.io.IOException;
+import java.io.NotSerializableException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.ObjectStreamField;
+import java.security.AccessController;
+import java.security.BasicPermission;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A bundle's authority to perform specific privileged administrative operations
+ * on or to get sensitive information about a bundle. The actions for this
+ * permission are:
+ * 
+ * <pre>
+ * Action             Methods
+ * class              Bundle.loadClass
+ * execute            Bundle.start
+ *                    Bundle.stop
+ *                    BundleStartLevel.setStartLevel
+ * extensionLifecycle BundleContext.installBundle for extension bundles
+ *                    Bundle.update for extension bundles
+ *                    Bundle.uninstall for extension bundles
+ * lifecycle          BundleContext.installBundle
+ *                    Bundle.update
+ *                    Bundle.uninstall
+ * listener           BundleContext.addBundleListener for SynchronousBundleListener
+ *                    BundleContext.removeBundleListener for SynchronousBundleListener
+ * metadata           Bundle.getHeaders
+ *                    Bundle.getLocation
+ * resolve            FrameworkWiring.refreshBundles
+ *                    FrameworkWiring.resolveBundles
+ * resource           Bundle.getResource
+ *                    Bundle.getResources
+ *                    Bundle.getEntry
+ *                    Bundle.getEntryPaths
+ *                    Bundle.findEntries
+ *                    Bundle resource/entry URL creation
+ * startlevel         FrameworkStartLevel.setStartLevel
+ *                    FrameworkStartLevel.setInitialBundleStartLevel
+ * context            Bundle.getBundleContext
+ * weave              WovenClass.setBytes
+ *                    WovenClass.getDynamicImports for modification
+ * </pre>
+ * 
+ * <p>
+ * The special action "*" will represent all actions. The
+ * {@code resolve} action is implied by the {@code class}, {@code execute} and
+ * {@code resource} actions.
+ * <p>
+ * The name of this permission is a filter expression. The filter gives access
+ * to the following attributes:
+ * <ul>
+ * <li>signer - A Distinguished Name chain used to sign a bundle. Wildcards in a
+ * DN are not matched according to the filter string rules, but according to the
+ * rules defined for a DN chain.</li>
+ * <li>location - The location of a bundle.</li>
+ * <li>id - The bundle ID of the designated bundle.</li>
+ * <li>name - The symbolic name of a bundle.</li>
+ * </ul>
+ * Filter attribute names are processed in a case sensitive manner.
+ * 
+ * @ThreadSafe
+ * @version $Id: cd883e81fde210ce8f0cabaebea377378d672818 $
+ */
+
+public final class AdminPermission extends BasicPermission {
+	static final long								serialVersionUID			= 307051004521261705L;
+
+	/**
+	 * The action string {@code class}. The {@code class} action implies the
+	 * {@code resolve} action.
+	 * 
+	 * @since 1.3
+	 */
+	public final static String						CLASS						= "class";
+	/**
+	 * The action string {@code execute}. The {@code execute} action implies the
+	 * {@code resolve} action.
+	 * 
+	 * @since 1.3
+	 */
+	public final static String						EXECUTE						= "execute";
+	/**
+	 * The action string {@code extensionLifecycle}.
+	 * 
+	 * @since 1.3
+	 */
+	public final static String						EXTENSIONLIFECYCLE			= "extensionLifecycle";
+	/**
+	 * The action string {@code lifecycle}.
+	 * 
+	 * @since 1.3
+	 */
+	public final static String						LIFECYCLE					= "lifecycle";
+	/**
+	 * The action string {@code listener}.
+	 * 
+	 * @since 1.3
+	 */
+	public final static String						LISTENER					= "listener";
+	/**
+	 * The action string {@code metadata}.
+	 * 
+	 * @since 1.3
+	 */
+	public final static String						METADATA					= "metadata";
+	/**
+	 * The action string {@code resolve}. The {@code resolve} action is implied
+	 * by the {@code class}, {@code execute} and {@code resource} actions.
+	 * 
+	 * @since 1.3
+	 */
+	public final static String						RESOLVE						= "resolve";
+	/**
+	 * The action string {@code resource}. The {@code resource} action implies
+	 * the {@code resolve} action.
+	 * 
+	 * @since 1.3
+	 */
+	public final static String						RESOURCE					= "resource";
+	/**
+	 * The action string {@code startlevel}.
+	 * 
+	 * @since 1.3
+	 */
+	public final static String						STARTLEVEL					= "startlevel";
+
+	/**
+	 * The action string {@code context}.
+	 * 
+	 * @since 1.4
+	 */
+	public final static String						CONTEXT						= "context";
+
+	/**
+	 * The action string {@code weave}.
+	 * 
+	 * @since 1.6
+	 */
+	public final static String						WEAVE						= "weave";
+
+	private final static int						ACTION_CLASS				= 0x00000001;
+	private final static int						ACTION_EXECUTE				= 0x00000002;
+	private final static int						ACTION_LIFECYCLE			= 0x00000004;
+	private final static int						ACTION_LISTENER				= 0x00000008;
+	private final static int						ACTION_METADATA				= 0x00000010;
+	private final static int						ACTION_RESOLVE				= 0x00000040;
+	private final static int						ACTION_RESOURCE				= 0x00000080;
+	private final static int						ACTION_STARTLEVEL			= 0x00000100;
+	private final static int						ACTION_EXTENSIONLIFECYCLE	= 0x00000200;
+	private final static int						ACTION_CONTEXT				= 0x00000400;
+	private final static int						ACTION_WEAVE				= 0x00000800;
+	private final static int						ACTION_ALL					= ACTION_CLASS | ACTION_EXECUTE | ACTION_LIFECYCLE | ACTION_LISTENER | ACTION_METADATA | ACTION_RESOLVE
+																						| ACTION_RESOURCE | ACTION_STARTLEVEL | ACTION_EXTENSIONLIFECYCLE | ACTION_CONTEXT | ACTION_WEAVE;
+	final static int								ACTION_NONE					= 0;
+
+	/**
+	 * The actions in canonical form.
+	 * 
+	 * @serial
+	 */
+	private volatile String							actions						= null;
+
+	/**
+	 * The actions mask.
+	 */
+	transient int									action_mask;
+
+	/**
+	 * If this AdminPermission was constructed with a filter, this holds a
+	 * Filter matching object used to evaluate the filter in implies.
+	 */
+	transient Filter								filter;
+
+	/**
+	 * The bundle governed by this AdminPermission - only used if filter == null
+	 */
+	transient final Bundle							bundle;
+
+	/**
+	 * This map holds the properties of the permission, used to match a filter
+	 * in implies. This is not initialized until necessary, and then cached in
+	 * this object.
+	 */
+	private transient volatile Map<String, Object>	properties;
+
+	/**
+	 * ThreadLocal used to determine if we have recursively called
+	 * getProperties.
+	 */
+	private static final ThreadLocal<Bundle>		recurse						= new ThreadLocal<Bundle>();
+
+	/**
+	 * Creates a new {@code AdminPermission} object that matches all bundles and
+	 * has all actions. Equivalent to AdminPermission("*","*");
+	 */
+	public AdminPermission() {
+		this(null, ACTION_ALL);
+	}
+
+	/**
+	 * Create a new AdminPermission.
+	 * 
+	 * This constructor must only be used to create a permission that is going
+	 * to be checked.
+	 * <p>
+	 * Examples:
+	 * 
+	 * <pre>
+	 * (signer=\*,o=ACME,c=US)   
+	 * (&(signer=\*,o=ACME,c=US)(name=com.acme.*)(location=http://www.acme.com/bundles/*))
+	 * (id>=1)
+	 * </pre>
+	 * 
+	 * <p>
+	 * When a signer key is used within the filter expression the signer value
+	 * must escape the special filter chars ('*', '(', ')').
+	 * <p>
+	 * Null arguments are equivalent to "*".
+	 * 
+	 * @param filter A filter expression that can use signer, location, id, and
+	 *        name keys. A value of "*" or {@code null} matches all
+	 *        bundle. Filter attribute names are processed in a case sensitive
+	 *        manner.
+	 * @param actions {@code class}, {@code execute}, {@code extensionLifecycle}
+	 *        , {@code lifecycle}, {@code listener}, {@code metadata},
+	 *        {@code resolve} , {@code resource}, {@code startlevel},
+	 *        {@code context} or {@code weave}. A value of "*" or {@code null}
+	 *        indicates all actions.
+	 * @throws IllegalArgumentException If the filter has an invalid syntax.
+	 */
+	public AdminPermission(String filter, String actions) {
+		// arguments will be null if called from a PermissionInfo defined with
+		// no args
+		this(parseFilter(filter), parseActions(actions));
+	}
+
+	/**
+	 * Creates a new requested {@code AdminPermission} object to be used by the
+	 * code that must perform {@code checkPermission}. {@code AdminPermission}
+	 * objects created with this constructor cannot be added to an
+	 * {@code AdminPermission} permission collection.
+	 * 
+	 * @param bundle A bundle.
+	 * @param actions {@code class}, {@code execute}, {@code extensionLifecycle}
+	 *        , {@code lifecycle}, {@code listener}, {@code metadata},
+	 *        {@code resolve} , {@code resource}, {@code startlevel},
+	 *        {@code context}, {@code weave}. A value of "*" or {@code null}
+	 *        indicates all actions.
+	 * @since 1.3
+	 */
+	public AdminPermission(Bundle bundle, String actions) {
+		super(createName(bundle));
+		setTransients(null, parseActions(actions));
+		this.bundle = bundle;
+	}
+
+	/**
+	 * Create a permission name from a Bundle
+	 * 
+	 * @param bundle Bundle to use to create permission name.
+	 * @return permission name.
+	 */
+	private static String createName(Bundle bundle) {
+		if (bundle == null) {
+			throw new IllegalArgumentException("bundle must not be null");
+		}
+		StringBuffer sb = new StringBuffer("(id=");
+		sb.append(bundle.getBundleId());
+		sb.append(")");
+		return sb.toString();
+	}
+
+	/**
+	 * Package private constructor used by AdminPermissionCollection.
+	 * 
+	 * @param filter name filter or {@code null} for wildcard.
+	 * @param mask action mask
+	 */
+	AdminPermission(Filter filter, int mask) {
+		super((filter == null) ? "*" : filter.toString());
+		setTransients(filter, mask);
+		this.bundle = null;
+	}
+
+	/**
+	 * Called by constructors and when deserialized.
+	 * 
+	 * @param filter Permission's filter or {@code null} for wildcard.
+	 * @param mask action mask
+	 */
+	private void setTransients(Filter filter, int mask) {
+		this.filter = filter;
+		if ((mask == ACTION_NONE) || ((mask & ACTION_ALL) != mask)) {
+			throw new IllegalArgumentException("invalid action string");
+		}
+		this.action_mask = mask;
+	}
+
+	/**
+	 * Parse action string into action mask.
+	 * 
+	 * @param actions Action string.
+	 * @return action mask.
+	 */
+	private static int parseActions(String actions) {
+		if ((actions == null) || actions.equals("*")) {
+			return ACTION_ALL;
+		}
+
+		boolean seencomma = false;
+
+		int mask = ACTION_NONE;
+
+		char[] a = actions.toCharArray();
+
+		int i = a.length - 1;
+		if (i < 0)
+			return mask;
+
+		while (i != -1) {
+			char c;
+
+			// skip whitespace
+			while ((i != -1) && ((c = a[i]) == ' ' || c == '\r' || c == '\n' || c == '\f' || c == '\t'))
+				i--;
+
+			// check for the known strings
+			int matchlen;
+
+			if (i >= 4 && (a[i - 4] == 'c' || a[i - 4] == 'C')
+					&& (a[i - 3] == 'l' || a[i - 3] == 'L')
+					&& (a[i - 2] == 'a' || a[i - 2] == 'A')
+					&& (a[i - 1] == 's' || a[i - 1] == 'S')
+					&& (a[i] == 's' || a[i] == 'S')) {
+				matchlen = 5;
+				mask |= ACTION_CLASS | ACTION_RESOLVE;
+
+			} else
+				if (i >= 6 && (a[i - 6] == 'e' || a[i - 6] == 'E')
+						&& (a[i - 5] == 'x' || a[i - 5] == 'X')
+						&& (a[i - 4] == 'e' || a[i - 4] == 'E')
+						&& (a[i - 3] == 'c' || a[i - 3] == 'C')
+						&& (a[i - 2] == 'u' || a[i - 2] == 'U')
+						&& (a[i - 1] == 't' || a[i - 1] == 'T')
+						&& (a[i] == 'e' || a[i] == 'E')) {
+					matchlen = 7;
+					mask |= ACTION_EXECUTE | ACTION_RESOLVE;
+
+				} else
+					if (i >= 17 && (a[i - 17] == 'e' || a[i - 17] == 'E')
+							&& (a[i - 16] == 'x' || a[i - 16] == 'X')
+							&& (a[i - 15] == 't' || a[i - 15] == 'T')
+							&& (a[i - 14] == 'e' || a[i - 14] == 'E')
+							&& (a[i - 13] == 'n' || a[i - 13] == 'N')
+							&& (a[i - 12] == 's' || a[i - 12] == 'S')
+							&& (a[i - 11] == 'i' || a[i - 11] == 'I')
+							&& (a[i - 10] == 'o' || a[i - 10] == 'O')
+							&& (a[i - 9] == 'n' || a[i - 9] == 'N')
+							&& (a[i - 8] == 'l' || a[i - 8] == 'L')
+							&& (a[i - 7] == 'i' || a[i - 7] == 'I')
+							&& (a[i - 6] == 'f' || a[i - 6] == 'F')
+							&& (a[i - 5] == 'e' || a[i - 5] == 'E')
+							&& (a[i - 4] == 'c' || a[i - 4] == 'C')
+							&& (a[i - 3] == 'y' || a[i - 3] == 'Y')
+							&& (a[i - 2] == 'c' || a[i - 2] == 'C')
+							&& (a[i - 1] == 'l' || a[i - 1] == 'L')
+							&& (a[i] == 'e' || a[i] == 'E')) {
+						matchlen = 18;
+						mask |= ACTION_EXTENSIONLIFECYCLE;
+
+					} else
+						if (i >= 8 && (a[i - 8] == 'l' || a[i - 8] == 'L')
+								&& (a[i - 7] == 'i' || a[i - 7] == 'I')
+								&& (a[i - 6] == 'f' || a[i - 6] == 'F')
+								&& (a[i - 5] == 'e' || a[i - 5] == 'E')
+								&& (a[i - 4] == 'c' || a[i - 4] == 'C')
+								&& (a[i - 3] == 'y' || a[i - 3] == 'Y')
+								&& (a[i - 2] == 'c' || a[i - 2] == 'C')
+								&& (a[i - 1] == 'l' || a[i - 1] == 'L')
+								&& (a[i] == 'e' || a[i] == 'E')) {
+							matchlen = 9;
+							mask |= ACTION_LIFECYCLE;
+
+						} else
+							if (i >= 7 && (a[i - 7] == 'l' || a[i - 7] == 'L')
+									&& (a[i - 6] == 'i' || a[i - 6] == 'I')
+									&& (a[i - 5] == 's' || a[i - 5] == 'S')
+									&& (a[i - 4] == 't' || a[i - 4] == 'T')
+									&& (a[i - 3] == 'e' || a[i - 3] == 'E')
+									&& (a[i - 2] == 'n' || a[i - 2] == 'N')
+									&& (a[i - 1] == 'e' || a[i - 1] == 'E')
+									&& (a[i] == 'r' || a[i] == 'R')) {
+								matchlen = 8;
+								mask |= ACTION_LISTENER;
+
+							} else
+								if (i >= 7
+										&& (a[i - 7] == 'm' || a[i - 7] == 'M')
+										&& (a[i - 6] == 'e' || a[i - 6] == 'E')
+										&& (a[i - 5] == 't' || a[i - 5] == 'T')
+										&& (a[i - 4] == 'a' || a[i - 4] == 'A')
+										&& (a[i - 3] == 'd' || a[i - 3] == 'D')
+										&& (a[i - 2] == 'a' || a[i - 2] == 'A')
+										&& (a[i - 1] == 't' || a[i - 1] == 'T')
+										&& (a[i] == 'a' || a[i] == 'A')) {
+									matchlen = 8;
+									mask |= ACTION_METADATA;
+
+								} else
+									if (i >= 6
+											&& (a[i - 6] == 'r' || a[i - 6] == 'R')
+											&& (a[i - 5] == 'e' || a[i - 5] == 'E')
+											&& (a[i - 4] == 's' || a[i - 4] == 'S')
+											&& (a[i - 3] == 'o' || a[i - 3] == 'O')
+											&& (a[i - 2] == 'l' || a[i - 2] == 'L')
+											&& (a[i - 1] == 'v' || a[i - 1] == 'V')
+											&& (a[i] == 'e' || a[i] == 'E')) {
+										matchlen = 7;
+										mask |= ACTION_RESOLVE;
+
+									} else
+										if (i >= 7
+												&& (a[i - 7] == 'r' || a[i - 7] == 'R')
+												&& (a[i - 6] == 'e' || a[i - 6] == 'E')
+												&& (a[i - 5] == 's' || a[i - 5] == 'S')
+												&& (a[i - 4] == 'o' || a[i - 4] == 'O')
+												&& (a[i - 3] == 'u' || a[i - 3] == 'U')
+												&& (a[i - 2] == 'r' || a[i - 2] == 'R')
+												&& (a[i - 1] == 'c' || a[i - 1] == 'C')
+												&& (a[i] == 'e' || a[i] == 'E')) {
+											matchlen = 8;
+											mask |= ACTION_RESOURCE
+													| ACTION_RESOLVE;
+
+										} else
+											if (i >= 9
+													&& (a[i - 9] == 's' || a[i - 9] == 'S')
+													&& (a[i - 8] == 't' || a[i - 8] == 'T')
+													&& (a[i - 7] == 'a' || a[i - 7] == 'A')
+													&& (a[i - 6] == 'r' || a[i - 6] == 'R')
+													&& (a[i - 5] == 't' || a[i - 5] == 'T')
+													&& (a[i - 4] == 'l' || a[i - 4] == 'L')
+													&& (a[i - 3] == 'e' || a[i - 3] == 'E')
+													&& (a[i - 2] == 'v' || a[i - 2] == 'V')
+													&& (a[i - 1] == 'e' || a[i - 1] == 'E')
+													&& (a[i] == 'l' || a[i] == 'L')) {
+												matchlen = 10;
+												mask |= ACTION_STARTLEVEL;
+
+											} else
+												if (i >= 6
+														&& (a[i - 6] == 'c' || a[i - 6] == 'C')
+														&& (a[i - 5] == 'o' || a[i - 5] == 'O')
+														&& (a[i - 4] == 'n' || a[i - 4] == 'N')
+														&& (a[i - 3] == 't' || a[i - 3] == 'T')
+														&& (a[i - 2] == 'e' || a[i - 2] == 'E')
+														&& (a[i - 1] == 'x' || a[i - 1] == 'X')
+														&& (a[i] == 't' || a[i] == 'T')) {
+													matchlen = 7;
+													mask |= ACTION_CONTEXT;
+
+												} else
+													if (i >= 4
+															&& (a[i - 4] == 'w' || a[i - 4] == 'W')
+															&& (a[i - 3] == 'e' || a[i - 3] == 'E')
+															&& (a[i - 2] == 'a' || a[i - 2] == 'A')
+															&& (a[i - 1] == 'v' || a[i - 1] == 'V')
+															&& (a[i] == 'e' || a[i] == 'E')) {
+														matchlen = 5;
+														mask |= ACTION_WEAVE;
+
+													} else
+														if (i >= 0 && (a[i] == '*')) {
+															matchlen = 1;
+															mask |= ACTION_ALL;
+
+														} else {
+															// parse error
+															throw new IllegalArgumentException("invalid permission: " + actions);
+														}
+
+			// make sure we didn't just match the tail of a word
+			// like "ackbarfstartlevel". Also, skip to the comma.
+			seencomma = false;
+			while (i >= matchlen && !seencomma) {
+				switch (a[i - matchlen]) {
+					case ',' :
+						seencomma = true;
+						/* FALLTHROUGH */
+					case ' ' :
+					case '\r' :
+					case '\n' :
+					case '\f' :
+					case '\t' :
+						break;
+					default :
+						throw new IllegalArgumentException("invalid permission: " + actions);
+				}
+				i--;
+			}
+
+			// point i at the location of the comma minus one (or -1).
+			i -= matchlen;
+		}
+
+		if (seencomma) {
+			throw new IllegalArgumentException("invalid permission: " + actions);
+		}
+
+		return mask;
+	}
+
+	/**
+	 * Parse filter string into a Filter object.
+	 * 
+	 * @param filterString The filter string to parse.
+	 * @return a Filter for this bundle. If the specified filterString is
+	 *         {@code null} or equals "*", then {@code null} is returned to
+	 *         indicate a wildcard.
+	 * @throws IllegalArgumentException If the filter syntax is invalid.
+	 */
+	private static Filter parseFilter(String filterString) {
+		if (filterString == null) {
+			return null;
+		}
+		filterString = filterString.trim();
+		if (filterString.equals("*")) {
+			return null;
+		}
+
+		try {
+			return FrameworkUtil.createFilter(filterString);
+		} catch (InvalidSyntaxException e) {
+			IllegalArgumentException iae = new IllegalArgumentException("invalid filter");
+			iae.initCause(e);
+			throw iae;
+		}
+	}
+
+	/**
+	 * Determines if the specified permission is implied by this object. This
+	 * method throws an exception if the specified permission was not
+	 * constructed with a bundle.
+	 * 
+	 * <p>
+	 * This method returns {@code true} if the specified permission is an
+	 * AdminPermission AND
+	 * <ul>
+	 * <li>this object's filter matches the specified permission's bundle ID,
+	 * bundle symbolic name, bundle location and bundle signer distinguished
+	 * name chain OR</li>
+	 * <li>this object's filter is "*"</li>
+	 * </ul>
+	 * AND this object's actions include all of the specified permission's
+	 * actions.
+	 * <p>
+	 * Special case: if the specified permission was constructed with "*"
+	 * filter, then this method returns {@code true} if this object's filter is
+	 * "*" and this object's actions include all of the specified permission's
+	 * actions
+	 * 
+	 * @param p The requested permission.
+	 * @return {@code true} if the specified permission is implied by this
+	 *         object; {@code false} otherwise.
+	 */
+	public boolean implies(Permission p) {
+		if (!(p instanceof AdminPermission)) {
+			return false;
+		}
+		AdminPermission requested = (AdminPermission) p;
+		if (bundle != null) {
+			return false;
+		}
+		// if requested permission has a filter, then it is an invalid argument
+		if (requested.filter != null) {
+			return false;
+		}
+		return implies0(requested, ACTION_NONE);
+	}
+
+	/**
+	 * Internal implies method. Used by the implies and the permission
+	 * collection implies methods.
+	 * 
+	 * @param requested The requested AdminPermision which has already be
+	 *        validated as a proper argument. The requested AdminPermission must
+	 *        not have a filter expression.
+	 * @param effective The effective actions with which to start.
+	 * @return {@code true} if the specified permission is implied by this
+	 *         object; {@code false} otherwise.
+	 */
+	boolean implies0(AdminPermission requested, int effective) {
+		/* check actions first - much faster */
+		effective |= action_mask;
+		final int desired = requested.action_mask;
+		if ((effective & desired) != desired) {
+			return false;
+		}
+
+		/* Get our filter */
+		Filter f = filter;
+		if (f == null) {
+			// it's "*"
+			return true;
+		}
+		/* is requested a wildcard filter? */
+		if (requested.bundle == null) {
+			return false;
+		}
+		Map<String, Object> requestedProperties = requested.getProperties();
+		if (requestedProperties == null) {
+			/*
+			 * If the requested properties are null, then we have detected a
+			 * recursion getting the bundle location. So we return true to
+			 * permit the bundle location request in the AdminPermission check
+			 * up the stack to succeed.
+			 */
+			return true;
+		}
+		return f.matches(requestedProperties);
+	}
+
+	/**
+	 * Returns the canonical string representation of the
+	 * {@code AdminPermission} actions.
+	 * 
+	 * <p>
+	 * Always returns present {@code AdminPermission} actions in the following
+	 * order: {@code class}, {@code execute}, {@code extensionLifecycle},
+	 * {@code lifecycle}, {@code listener}, {@code metadata}, {@code resolve},
+	 * {@code resource}, {@code startlevel}, {@code context}, {@code weave}.
+	 * 
+	 * @return Canonical string representation of the {@code AdminPermission}
+	 *         actions.
+	 */
+	public String getActions() {
+		String result = actions;
+		if (result == null) {
+			StringBuffer sb = new StringBuffer();
+
+			int mask = action_mask;
+			if ((mask & ACTION_CLASS) == ACTION_CLASS) {
+				sb.append(CLASS);
+				sb.append(',');
+			}
+
+			if ((mask & ACTION_EXECUTE) == ACTION_EXECUTE) {
+				sb.append(EXECUTE);
+				sb.append(',');
+			}
+
+			if ((mask & ACTION_EXTENSIONLIFECYCLE) == ACTION_EXTENSIONLIFECYCLE) {
+				sb.append(EXTENSIONLIFECYCLE);
+				sb.append(',');
+			}
+
+			if ((mask & ACTION_LIFECYCLE) == ACTION_LIFECYCLE) {
+				sb.append(LIFECYCLE);
+				sb.append(',');
+			}
+
+			if ((mask & ACTION_LISTENER) == ACTION_LISTENER) {
+				sb.append(LISTENER);
+				sb.append(',');
+			}
+
+			if ((mask & ACTION_METADATA) == ACTION_METADATA) {
+				sb.append(METADATA);
+				sb.append(',');
+			}
+
+			if ((mask & ACTION_RESOLVE) == ACTION_RESOLVE) {
+				sb.append(RESOLVE);
+				sb.append(',');
+			}
+
+			if ((mask & ACTION_RESOURCE) == ACTION_RESOURCE) {
+				sb.append(RESOURCE);
+				sb.append(',');
+			}
+
+			if ((mask & ACTION_STARTLEVEL) == ACTION_STARTLEVEL) {
+				sb.append(STARTLEVEL);
+				sb.append(',');
+			}
+
+			if ((mask & ACTION_CONTEXT) == ACTION_CONTEXT) {
+				sb.append(CONTEXT);
+				sb.append(',');
+			}
+
+			if ((mask & ACTION_WEAVE) == ACTION_WEAVE) {
+				sb.append(WEAVE);
+				sb.append(',');
+			}
+
+			// remove trailing comma
+			if (sb.length() > 0) {
+				sb.setLength(sb.length() - 1);
+			}
+
+			actions = result = sb.toString();
+		}
+		return result;
+	}
+
+	/**
+	 * Returns a new {@code PermissionCollection} object suitable for storing
+	 * {@code AdminPermission}s.
+	 * 
+	 * @return A new {@code PermissionCollection} object.
+	 */
+	public PermissionCollection newPermissionCollection() {
+		return new AdminPermissionCollection();
+	}
+
+	/**
+	 * Determines the equality of two {@code AdminPermission} objects.
+	 * 
+	 * @param obj The object being compared for equality with this object.
+	 * @return {@code true} if {@code obj} is equivalent to this
+	 *         {@code AdminPermission}; {@code false} otherwise.
+	 */
+	public boolean equals(Object obj) {
+		if (obj == this) {
+			return true;
+		}
+
+		if (!(obj instanceof AdminPermission)) {
+			return false;
+		}
+
+		AdminPermission ap = (AdminPermission) obj;
+
+		return (action_mask == ap.action_mask) && ((bundle == ap.bundle) || ((bundle != null) && bundle.equals(ap.bundle))) && (filter == null ? ap.filter == null : filter.equals(ap.filter));
+	}
+
+	/**
+	 * Returns the hash code value for this object.
+	 * 
+	 * @return Hash code value for this object.
+	 */
+	public int hashCode() {
+		int h = 31 * 17 + getName().hashCode();
+		h = 31 * h + getActions().hashCode();
+		if (bundle != null) {
+			h = 31 * h + bundle.hashCode();
+		}
+		return h;
+	}
+
+	/**
+	 * WriteObject is called to save the state of this permission object to a
+	 * stream. The actions are serialized, and the superclass takes care of the
+	 * name.
+	 */
+	private synchronized void writeObject(java.io.ObjectOutputStream s) throws IOException {
+		if (bundle != null) {
+			throw new NotSerializableException("cannot serialize");
+		}
+		// Write out the actions. The superclass takes care of the name
+		// call getActions to make sure actions field is initialized
+		if (actions == null)
+			getActions();
+		s.defaultWriteObject();
+	}
+
+	/**
+	 * readObject is called to restore the state of this permission from a
+	 * stream.
+	 */
+	private synchronized void readObject(java.io.ObjectInputStream s) throws IOException, ClassNotFoundException {
+		// Read in the data, then initialize the transients
+		s.defaultReadObject();
+		setTransients(parseFilter(getName()), parseActions(actions));
+	}
+
+	/**
+	 * Called by {@code implies0} on an AdminPermission which was constructed
+	 * with a Bundle. This method loads a map with the filter-matchable
+	 * properties of this bundle. The map is cached so this lookup only happens
+	 * once.
+	 * 
+	 * This method should only be called on an AdminPermission which was
+	 * constructed with a bundle
+	 * 
+	 * @return a map of properties for this bundle
+	 */
+	private Map<String, Object> getProperties() {
+		Map<String, Object> result = properties;
+		if (result != null) {
+			return result;
+		}
+		/*
+		 * We may have recursed here due to the Bundle.getLocation call in the
+		 * doPrivileged below. If this is the case, return null to allow implies
+		 * to return true.
+		 */
+		final Object mark = recurse.get();
+		if (mark == bundle) {
+			return null;
+		}
+		recurse.set(bundle);
+		try {
+			final Map<String, Object> map = new HashMap<String, Object>(4);
+			AccessController.doPrivileged(new PrivilegedAction<Object>() {
+				public Object run() {
+					map.put("id", new Long(bundle.getBundleId()));
+					map.put("location", bundle.getLocation());
+					String name = bundle.getSymbolicName();
+					if (name != null) {
+						map.put("name", name);
+					}
+					SignerProperty signer = new SignerProperty(bundle);
+					if (signer.isBundleSigned()) {
+						map.put("signer", signer);
+					}
+					return null;
+				}
+			});
+			return properties = map;
+		} finally {
+			recurse.set(null);
+		}
+	}
+}
+
+/**
+ * Stores a collection of {@code AdminPermission}s.
+ */
+final class AdminPermissionCollection extends PermissionCollection {
+	private static final long						serialVersionUID	= 3906372644575328048L;
+	/**
+	 * Collection of permissions.
+	 * 
+	 * @GuardedBy this
+	 */
+	private transient Map<String, AdminPermission>	permissions;
+
+	/**
+	 * Boolean saying if "*" is in the collection.
+	 * 
+	 * @serial
+	 * @GuardedBy this
+	 */
+	private boolean									all_allowed;
+
+	/**
+	 * Create an empty AdminPermissions object.
+	 * 
+	 */
+	public AdminPermissionCollection() {
+		permissions = new HashMap<String, AdminPermission>();
+	}
+
+	/**
+	 * Adds a permission to this permission collection.
+	 * 
+	 * @param permission The {@code AdminPermission} object to add.
+	 * @throws IllegalArgumentException If the specified permission is not an
+	 *         {@code AdminPermission} instance or was constructed with a Bundle
+	 *         object.
+	 * @throws SecurityException If this {@code AdminPermissionCollection}
+	 *         object has been marked read-only.
+	 */
+	public void add(Permission permission) {
+		if (!(permission instanceof AdminPermission)) {
+			throw new IllegalArgumentException("invalid permission: " + permission);
+		}
+		if (isReadOnly()) {
+			throw new SecurityException("attempt to add a Permission to a " + "readonly PermissionCollection");
+		}
+		final AdminPermission ap = (AdminPermission) permission;
+		if (ap.bundle != null) {
+			throw new IllegalArgumentException("cannot add to collection: " + ap);
+		}
+		final String name = ap.getName();
+		synchronized (this) {
+			Map<String, AdminPermission> pc = permissions;
+			AdminPermission existing = pc.get(name);
+			if (existing != null) {
+				int oldMask = existing.action_mask;
+				int newMask = ap.action_mask;
+
+				if (oldMask != newMask) {
+					pc.put(name, new AdminPermission(existing.filter, oldMask | newMask));
+				}
+			} else {
+				pc.put(name, ap);
+			}
+			if (!all_allowed) {
+				if (name.equals("*")) {
+					all_allowed = true;
+				}
+			}
+		}
+	}
+
+	/**
+	 * Determines if the specified permissions implies the permissions expressed
+	 * in {@code permission}.
+	 * 
+	 * @param permission The Permission object to compare with the
+	 *        {@code AdminPermission} objects in this collection.
+	 * @return {@code true} if {@code permission} is implied by an
+	 *         {@code AdminPermission} in this collection, {@code false}
+	 *         otherwise.
+	 */
+	public boolean implies(Permission permission) {
+		if (!(permission instanceof AdminPermission)) {
+			return false;
+		}
+
+		AdminPermission requested = (AdminPermission) permission;
+		// if requested permission has a filter, then it is an invalid argument
+		if (requested.filter != null) {
+			return false;
+		}
+		int effective = AdminPermission.ACTION_NONE;
+		Collection<AdminPermission> perms;
+		synchronized (this) {
+			Map<String, AdminPermission> pc = permissions;
+			// short circuit if the "*" Permission was added
+			if (all_allowed) {
+				AdminPermission ap = pc.get("*");
+				if (ap != null) {
+					effective |= ap.action_mask;
+					final int desired = requested.action_mask;
+					if ((effective & desired) == desired) {
+						return true;
+					}
+				}
+			}
+			perms = pc.values();
+		}
+
+		// just iterate one by one
+		for (AdminPermission perm : perms) {
+			if (perm.implies0(requested, effective)) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+	/**
+	 * Returns an enumeration of all {@code AdminPermission} objects in the
+	 * container.
+	 * 
+	 * @return Enumeration of all {@code AdminPermission} objects.
+	 */
+	public synchronized Enumeration<Permission> elements() {
+		List<Permission> all = new ArrayList<Permission>(permissions.values());
+		return Collections.enumeration(all);
+	}
+
+	/* serialization logic */
+	private static final ObjectStreamField[]	serialPersistentFields	= {new ObjectStreamField("permissions", Hashtable.class), new ObjectStreamField("all_allowed", Boolean.TYPE)};
+
+	private synchronized void writeObject(ObjectOutputStream out) throws IOException {
+		Hashtable<String, AdminPermission> hashtable = new Hashtable<String, AdminPermission>(permissions);
+		ObjectOutputStream.PutField pfields = out.putFields();
+		pfields.put("permissions", hashtable);
+		pfields.put("all_allowed", all_allowed);
+		out.writeFields();
+	}
+
+	private synchronized void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
+		ObjectInputStream.GetField gfields = in.readFields();
+		Hashtable<String, AdminPermission> hashtable = (Hashtable<String, AdminPermission>) gfields.get("permissions", null);
+		permissions = new HashMap<String, AdminPermission>(hashtable);
+		all_allowed = gfields.get("all_allowed", false);
+	}
+}
diff --git a/src/main/java/org/osgi/framework/AllServiceListener.java b/src/main/java/org/osgi/framework/AllServiceListener.java
new file mode 100644
index 0000000..71e27cd
--- /dev/null
+++ b/src/main/java/org/osgi/framework/AllServiceListener.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) OSGi Alliance (2005, 2012). All Rights Reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.framework;
+
+/**
+ * A {@code ServiceEvent} listener that does not filter based upon package
+ * wiring. {@code AllServiceListener} is a listener interface that may be
+ * implemented by a bundle developer. When a {@code ServiceEvent} is fired, it
+ * is synchronously delivered to an {@code AllServiceListener}. The Framework
+ * may deliver {@code ServiceEvent} objects to an {@code AllServiceListener} out
+ * of order and may concurrently call and/or reenter an
+ * {@code AllServiceListener}.
+ * <p>
+ * An {@code AllServiceListener} object is registered with the Framework using
+ * the {@code BundleContext.addServiceListener} method.
+ * {@code AllServiceListener} objects are called with a {@code ServiceEvent}
+ * object when a service is registered, modified, or is in the process of
+ * unregistering.
+ * 
+ * <p>
+ * {@code ServiceEvent} object delivery to {@code AllServiceListener} objects is
+ * filtered by the filter specified when the listener was registered. If the
+ * Java Runtime Environment supports permissions, then additional filtering is
+ * done. {@code ServiceEvent} objects are only delivered to the listener if the
+ * bundle which defines the listener object's class has the appropriate
+ * {@code ServicePermission} to get the service using at least one of the named
+ * classes under which the service was registered.
+ * 
+ * <p>
+ * Unlike normal {@code ServiceListener} objects, {@code AllServiceListener}
+ * objects receive all {@code ServiceEvent} objects regardless of whether the
+ * package source of the listening bundle is equal to the package source of the
+ * bundle that registered the service. This means that the listener may not be
+ * able to cast the service object to any of its corresponding service
+ * interfaces if the service object is retrieved.
+ * 
+ * @see ServiceEvent
+ * @see ServicePermission
+ * @ThreadSafe
+ * @since 1.3
+ * @version $Id: 7a0c82db414be7064a06e1868eb41a8c8f8d9d6c $
+ */
+
+public interface AllServiceListener extends ServiceListener {
+	// This is a marker interface
+}
diff --git a/src/main/java/org/osgi/framework/Bundle.java b/src/main/java/org/osgi/framework/Bundle.java
new file mode 100644
index 0000000..d814e9a
--- /dev/null
+++ b/src/main/java/org/osgi/framework/Bundle.java
@@ -0,0 +1,1243 @@
+/*
+ * Copyright (c) OSGi Alliance (2000, 2012). All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.framework;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.security.cert.X509Certificate;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Map;
+import org.osgi.framework.wiring.FrameworkWiring;
+
+/**
+ * An installed bundle in the Framework.
+ * 
+ * <p>
+ * A {@code Bundle} object is the access point to define the lifecycle of an
+ * installed bundle. Each bundle installed in the OSGi environment must have an
+ * associated {@code Bundle} object.
+ * 
+ * <p>
+ * A bundle must have a unique identity, a {@code long}, chosen by the
+ * Framework. This identity must not change during the lifecycle of a bundle,
+ * even when the bundle is updated. Uninstalling and then reinstalling the
+ * bundle must create a new unique identity.
+ * 
+ * <p>
+ * A bundle can be in one of six states:
+ * <ul>
+ * <li>{@link #UNINSTALLED}
+ * <li>{@link #INSTALLED}
+ * <li>{@link #RESOLVED}
+ * <li>{@link #STARTING}
+ * <li>{@link #STOPPING}
+ * <li>{@link #ACTIVE}
+ * </ul>
+ * <p>
+ * Values assigned to these states have no specified ordering; they represent
+ * bit values that may be ORed together to determine if a bundle is in one of
+ * the valid states.
+ * 
+ * <p>
+ * A bundle should only have active threads of execution when its state is one
+ * of {@code STARTING},{@code ACTIVE}, or {@code STOPPING}. An
+ * {@code UNINSTALLED} bundle can not be set to another state; it is a zombie
+ * and can only be reached because references are kept somewhere.
+ * 
+ * <p>
+ * The Framework is the only entity that is allowed to create {@code Bundle}
+ * objects, and these objects are only valid within the Framework that created
+ * them.
+ * 
+ * <p>
+ * Bundles have a natural ordering such that if two {@code Bundle}s have the
+ * same {@link #getBundleId() bundle id} they are equal. A {@code Bundle} is
+ * less than another {@code Bundle} if it has a lower {@link #getBundleId()
+ * bundle id} and is greater if it has a higher bundle id.
+ * 
+ * @ThreadSafe
+ * @noimplement
+ * @version $Id: 8a58ab72af389b1999b88348e4944203b7096510 $
+ */
+public interface Bundle extends Comparable<Bundle> {
+	/**
+	 * The bundle is uninstalled and may not be used.
+	 * 
+	 * <p>
+	 * The {@code UNINSTALLED} state is only visible after a bundle is
+	 * uninstalled; the bundle is in an unusable state but references to the
+	 * {@code Bundle} object may still be available and used for introspection.
+	 * <p>
+	 * The value of {@code UNINSTALLED} is 0x00000001.
+	 */
+	int	UNINSTALLED				= 0x00000001;
+
+	/**
+	 * The bundle is installed but not yet resolved.
+	 * 
+	 * <p>
+	 * A bundle is in the {@code INSTALLED} state when it has been installed in
+	 * the Framework but is not or cannot be resolved.
+	 * <p>
+	 * This state is visible if the bundle's code dependencies are not resolved.
+	 * The Framework may attempt to resolve an {@code INSTALLED} bundle's code
+	 * dependencies and move the bundle to the {@code RESOLVED} state.
+	 * <p>
+	 * The value of {@code INSTALLED} is 0x00000002.
+	 */
+	int	INSTALLED				= 0x00000002;
+
+	/**
+	 * The bundle is resolved and is able to be started.
+	 * 
+	 * <p>
+	 * A bundle is in the {@code RESOLVED} state when the Framework has
+	 * successfully resolved the bundle's code dependencies. These dependencies
+	 * include:
+	 * <ul>
+	 * <li>The bundle's class path from its {@link Constants#BUNDLE_CLASSPATH}
+	 * Manifest header.
+	 * <li>The bundle's package dependencies from its
+	 * {@link Constants#EXPORT_PACKAGE} and {@link Constants#IMPORT_PACKAGE}
+	 * Manifest headers.
+	 * <li>The bundle's required bundle dependencies from its
+	 * {@link Constants#REQUIRE_BUNDLE} Manifest header.
+	 * <li>A fragment bundle's host dependency from its
+	 * {@link Constants#FRAGMENT_HOST} Manifest header.
+	 * </ul>
+	 * <p>
+	 * Note that the bundle is not active yet. A bundle must be put in the
+	 * {@code RESOLVED} state before it can be started. The Framework may
+	 * attempt to resolve a bundle at any time.
+	 * <p>
+	 * The value of {@code RESOLVED} is 0x00000004.
+	 */
+	int	RESOLVED				= 0x00000004;
+
+	/**
+	 * The bundle is in the process of starting.
+	 * 
+	 * <p>
+	 * A bundle is in the {@code STARTING} state when its {@link #start(int)
+	 * start} method is active. A bundle must be in this state when the bundle's
+	 * {@link BundleActivator#start(BundleContext)} is called. If the
+	 * {@code BundleActivator.start} method completes without exception, then
+	 * the bundle has successfully started and must move to the {@code ACTIVE}
+	 * state.
+	 * <p>
+	 * If the bundle has a {@link Constants#ACTIVATION_LAZY lazy activation
+	 * policy}, then the bundle may remain in this state for some time until the
+	 * activation is triggered.
+	 * <p>
+	 * The value of {@code STARTING} is 0x00000008.
+	 */
+	int	STARTING				= 0x00000008;
+
+	/**
+	 * The bundle is in the process of stopping.
+	 * 
+	 * <p>
+	 * A bundle is in the {@code STOPPING} state when its {@link #stop(int)
+	 * stop} method is active. A bundle must be in this state when the bundle's
+	 * {@link BundleActivator#stop(BundleContext)} method is called. When the
+	 * {@code BundleActivator.stop} method completes the bundle is stopped and
+	 * must move to the {@code RESOLVED} state.
+	 * <p>
+	 * The value of {@code STOPPING} is 0x00000010.
+	 */
+	int	STOPPING				= 0x00000010;
+
+	/**
+	 * The bundle is now running.
+	 * 
+	 * <p>
+	 * A bundle is in the {@code ACTIVE} state when it has been successfully
+	 * started and activated.
+	 * <p>
+	 * The value of {@code ACTIVE} is 0x00000020.
+	 */
+	int	ACTIVE					= 0x00000020;
+
+	/**
+	 * The bundle start operation is transient and the persistent autostart
+	 * setting of the bundle is not modified.
+	 * 
+	 * <p>
+	 * This bit may be set when calling {@link #start(int)} to notify the
+	 * framework that the autostart setting of the bundle must not be modified.
+	 * If this bit is not set, then the autostart setting of the bundle is
+	 * modified.
+	 * 
+	 * @since 1.4
+	 * @see #start(int)
+	 */
+	int	START_TRANSIENT			= 0x00000001;
+
+	/**
+	 * The bundle start operation must activate the bundle according to the
+	 * bundle's declared {@link Constants#BUNDLE_ACTIVATIONPOLICY activation
+	 * policy}.
+	 * 
+	 * <p>
+	 * This bit may be set when calling {@link #start(int)} to notify the
+	 * framework that the bundle must be activated using the bundle's declared
+	 * activation policy.
+	 * 
+	 * @since 1.4
+	 * @see Constants#BUNDLE_ACTIVATIONPOLICY
+	 * @see #start(int)
+	 */
+	int	START_ACTIVATION_POLICY	= 0x00000002;
+
+	/**
+	 * The bundle stop is transient and the persistent autostart setting of the
+	 * bundle is not modified.
+	 * 
+	 * <p>
+	 * This bit may be set when calling {@link #stop(int)} to notify the
+	 * framework that the autostart setting of the bundle must not be modified.
+	 * If this bit is not set, then the autostart setting of the bundle is
+	 * modified.
+	 * 
+	 * @since 1.4
+	 * @see #stop(int)
+	 */
+	int	STOP_TRANSIENT			= 0x00000001;
+
+	/**
+	 * Request that all certificates used to sign the bundle be returned.
+	 * 
+	 * @since 1.5
+	 * @see #getSignerCertificates(int)
+	 */
+	int	SIGNERS_ALL				= 1;
+
+	/**
+	 * Request that only certificates used to sign the bundle that are trusted
+	 * by the framework be returned.
+	 * 
+	 * @since 1.5
+	 * @see #getSignerCertificates(int)
+	 */
+	int	SIGNERS_TRUSTED			= 2;
+
+	/**
+	 * Returns this bundle's current state.
+	 * 
+	 * <p>
+	 * A bundle can be in only one state at any time.
+	 * 
+	 * @return An element of {@code UNINSTALLED},{@code INSTALLED},
+	 *         {@code RESOLVED}, {@code STARTING}, {@code STOPPING},
+	 *         {@code ACTIVE}.
+	 */
+	int getState();
+
+	/**
+	 * Starts this bundle.
+	 * 
+	 * <p>
+	 * If this bundle's state is {@code UNINSTALLED} then an
+	 * {@code IllegalStateException} is thrown.
+	 * <p>
+	 * If the current start level is less than this bundle's start level:
+	 * <ul>
+	 * <li>If the {@link #START_TRANSIENT} option is set, then a
+	 * {@code BundleException} is thrown indicating this bundle cannot be
+	 * started due to the Framework's current start level.
+	 * 
+	 * <li>Otherwise, the Framework must set this bundle's persistent autostart
+	 * setting to <em>Started with declared activation</em> if the
+	 * {@link #START_ACTIVATION_POLICY} option is set or
+	 * <em>Started with eager activation</em> if not set.
+	 * </ul>
+	 * <p>
+	 * When the Framework's current start level becomes equal to or more than
+	 * this bundle's start level, this bundle will be started.
+	 * <p>
+	 * Otherwise, the following steps are required to start this bundle:
+	 * <ol>
+	 * <li>If this bundle is in the process of being activated or deactivated
+	 * then this method must wait for activation or deactivation to complete
+	 * before continuing. If this does not occur in a reasonable time, a
+	 * {@code BundleException} is thrown to indicate this bundle was unable to
+	 * be started.
+	 * 
+	 * <li>If this bundle's state is {@code ACTIVE} then this method returns
+	 * immediately.
+	 * 
+	 * <li>If the {@link #START_TRANSIENT} option is not set then set this
+	 * bundle's autostart setting to <em>Started with declared activation</em>
+	 * if the {@link #START_ACTIVATION_POLICY} option is set or
+	 * <em>Started with eager activation</em> if not set. When the Framework is
+	 * restarted and this bundle's autostart setting is not <em>Stopped</em>,
+	 * this bundle must be automatically started.
+	 * 
+	 * <li>If this bundle's state is not {@code RESOLVED}, an attempt is made to
+	 * resolve this bundle. If the Framework cannot resolve this bundle, a
+	 * {@code BundleException} is thrown.
+	 * 
+	 * <li>If the {@link #START_ACTIVATION_POLICY} option is set and this
+	 * bundle's declared activation policy is {@link Constants#ACTIVATION_LAZY
+	 * lazy} then:
+	 * <ul>
+	 * <li>If this bundle's state is {@code STARTING} then this method returns
+	 * immediately.
+	 * <li>This bundle's state is set to {@code STARTING}.
+	 * <li>A bundle event of type {@link BundleEvent#LAZY_ACTIVATION} is fired.
+	 * <li>This method returns immediately and the remaining steps will be
+	 * followed when this bundle's activation is later triggered.
+	 * </ul>
+	 * <i></i>
+	 * <li>This bundle's state is set to {@code STARTING}.
+	 * 
+	 * <li>A bundle event of type {@link BundleEvent#STARTING} is fired.
+	 * 
+	 * <li>The {@link BundleActivator#start(BundleContext)} method of this
+	 * bundle's {@code BundleActivator}, if one is specified, is called. If the
+	 * {@code BundleActivator} is invalid or throws an exception then:
+	 * <ul>
+	 * <li>This bundle's state is set to {@code STOPPING}.
+	 * <li>A bundle event of type {@link BundleEvent#STOPPING} is fired.
+	 * <li>Any services registered by this bundle must be unregistered.
+	 * <li>Any services used by this bundle must be released.
+	 * <li>Any listeners registered by this bundle must be removed.
+	 * <li>This bundle's state is set to {@code RESOLVED}.
+	 * <li>A bundle event of type {@link BundleEvent#STOPPED} is fired.
+	 * <li>A {@code BundleException} is then thrown.
+	 * </ul>
+	 * <i></i>
+	 * <li>If this bundle's state is {@code UNINSTALLED}, because this bundle
+	 * was uninstalled while the {@code BundleActivator.start} method was
+	 * running, a {@code BundleException} is thrown.
+	 * 
+	 * <li>This bundle's state is set to {@code ACTIVE}.
+	 * 
+	 * <li>A bundle event of type {@link BundleEvent#STARTED} is fired.
+	 * </ol>
+	 * 
+	 * <b>Preconditions </b>
+	 * <ul>
+	 * <li>{@code getState()} in &#x007B; {@code INSTALLED}, {@code RESOLVED}
+	 * &#x007D; or &#x007B; {@code INSTALLED}, {@code RESOLVED},
+	 * {@code STARTING} &#x007D; if this bundle has a lazy activation policy.
+	 * </ul>
+	 * <b>Postconditions, no exceptions thrown </b>
+	 * <ul>
+	 * <li>Bundle autostart setting is modified unless the
+	 * {@link #START_TRANSIENT} option was set.
+	 * <li>{@code getState()} in &#x007B; {@code ACTIVE} &#x007D; unless the
+	 * lazy activation policy was used.
+	 * <li>{@code BundleActivator.start()} has been called and did not throw an
+	 * exception unless the lazy activation policy was used.
+	 * </ul>
+	 * <b>Postconditions, when an exception is thrown </b>
+	 * <ul>
+	 * <li>Depending on when the exception occurred, bundle autostart setting is
+	 * modified unless the {@link #START_TRANSIENT} option was set.
+	 * <li>{@code getState()} not in &#x007B; {@code STARTING}, {@code ACTIVE}
+	 * &#x007D;.
+	 * </ul>
+	 * 
+	 * @param options The options for starting this bundle. See
+	 *        {@link #START_TRANSIENT} and {@link #START_ACTIVATION_POLICY}. The
+	 *        Framework must ignore unrecognized options.
+	 * @throws BundleException If this bundle could not be started.
+	 *         BundleException types thrown by this method include:
+	 *         {@link BundleException#START_TRANSIENT_ERROR},
+	 *         {@link BundleException#NATIVECODE_ERROR},
+	 *         {@link BundleException#RESOLVE_ERROR},
+	 *         {@link BundleException#STATECHANGE_ERROR}, and
+	 *         {@link BundleException#ACTIVATOR_ERROR}.
+	 * @throws IllegalStateException If this bundle has been uninstalled or this
+	 *         bundle tries to change its own state.
+	 * @throws SecurityException If the caller does not have the appropriate
+	 *         {@code AdminPermission[this,EXECUTE]}, and the Java Runtime
+	 *         Environment supports permissions.
+	 * @since 1.4
+	 */
+	void start(int options) throws BundleException;
+
+	/**
+	 * Starts this bundle with no options.
+	 * 
+	 * <p>
+	 * This method performs the same function as calling {@code start(0)}.
+	 * 
+	 * @throws BundleException If this bundle could not be started.
+	 *         BundleException types thrown by this method include:
+	 *         {@link BundleException#NATIVECODE_ERROR},
+	 *         {@link BundleException#RESOLVE_ERROR},
+	 *         {@link BundleException#STATECHANGE_ERROR}, and
+	 *         {@link BundleException#ACTIVATOR_ERROR}.
+	 * @throws IllegalStateException If this bundle has been uninstalled or this
+	 *         bundle tries to change its own state.
+	 * @throws SecurityException If the caller does not have the appropriate
+	 *         {@code AdminPermission[this,EXECUTE]}, and the Java Runtime
+	 *         Environment supports permissions.
+	 * @see #start(int)
+	 */
+	void start() throws BundleException;
+
+	/**
+	 * Stops this bundle.
+	 * 
+	 * <p>
+	 * The following steps are required to stop a bundle:
+	 * <ol>
+	 * <li>If this bundle's state is {@code UNINSTALLED} then an
+	 * {@code IllegalStateException} is thrown.
+	 * 
+	 * <li>If this bundle is in the process of being activated or deactivated
+	 * then this method must wait for activation or deactivation to complete
+	 * before continuing. If this does not occur in a reasonable time, a
+	 * {@code BundleException} is thrown to indicate this bundle was unable to
+	 * be stopped.
+	 * <li>If the {@link #STOP_TRANSIENT} option is not set then then set this
+	 * bundle's persistent autostart setting to to <em>Stopped</em>. When the
+	 * Framework is restarted and this bundle's autostart setting is
+	 * <em>Stopped</em>, this bundle must not be automatically started.
+	 * 
+	 * <li>If this bundle's state is not {@code STARTING} or {@code ACTIVE} then
+	 * this method returns immediately.
+	 * 
+	 * <li>This bundle's state is set to {@code STOPPING}.
+	 * 
+	 * <li>A bundle event of type {@link BundleEvent#STOPPING} is fired.
+	 * 
+	 * <li>If this bundle's state was {@code ACTIVE} prior to setting the state
+	 * to {@code STOPPING}, the {@link BundleActivator#stop(BundleContext)}
+	 * method of this bundle's {@code BundleActivator}, if one is specified, is
+	 * called. If that method throws an exception, this method must continue to
+	 * stop this bundle and a {@code BundleException} must be thrown after
+	 * completion of the remaining steps.
+	 * 
+	 * <li>Any services registered by this bundle must be unregistered.
+	 * <li>Any services used by this bundle must be released.
+	 * <li>Any listeners registered by this bundle must be removed.
+	 * 
+	 * <li>If this bundle's state is {@code UNINSTALLED}, because this bundle
+	 * was uninstalled while the {@code BundleActivator.stop} method was
+	 * running, a {@code BundleException} must be thrown.
+	 * 
+	 * <li>This bundle's state is set to {@code RESOLVED}.
+	 * 
+	 * <li>A bundle event of type {@link BundleEvent#STOPPED} is fired.
+	 * </ol>
+	 * 
+	 * <b>Preconditions </b>
+	 * <ul>
+	 * <li>{@code getState()} in &#x007B; {@code ACTIVE} &#x007D;.
+	 * </ul>
+	 * <b>Postconditions, no exceptions thrown </b>
+	 * <ul>
+	 * <li>Bundle autostart setting is modified unless the
+	 * {@link #STOP_TRANSIENT} option was set.
+	 * <li>{@code getState()} not in &#x007B; {@code ACTIVE}, {@code STOPPING}
+	 * &#x007D;.
+	 * <li>{@code BundleActivator.stop} has been called and did not throw an
+	 * exception.
+	 * </ul>
+	 * <b>Postconditions, when an exception is thrown </b>
+	 * <ul>
+	 * <li>Bundle autostart setting is modified unless the
+	 * {@link #STOP_TRANSIENT} option was set.
+	 * </ul>
+	 * 
+	 * @param options The options for stopping this bundle. See
+	 *        {@link #STOP_TRANSIENT}. The Framework must ignore unrecognized
+	 *        options.
+	 * @throws BundleException BundleException types thrown by this method
+	 *         include: {@link BundleException#STATECHANGE_ERROR} and
+	 *         {@link BundleException#ACTIVATOR_ERROR}.
+	 * @throws IllegalStateException If this bundle has been uninstalled or this
+	 *         bundle tries to change its own state.
+	 * @throws SecurityException If the caller does not have the appropriate
+	 *         {@code AdminPermission[this,EXECUTE]}, and the Java Runtime
+	 *         Environment supports permissions.
+	 * @since 1.4
+	 */
+	void stop(int options) throws BundleException;
+
+	/**
+	 * Stops this bundle with no options.
+	 * 
+	 * <p>
+	 * This method performs the same function as calling {@code stop(0)}.
+	 * 
+	 * @throws BundleException BundleException types thrown by this method
+	 *         include: {@link BundleException#STATECHANGE_ERROR} and
+	 *         {@link BundleException#ACTIVATOR_ERROR}.
+	 * @throws IllegalStateException If this bundle has been uninstalled or this
+	 *         bundle tries to change its own state.
+	 * @throws SecurityException If the caller does not have the appropriate
+	 *         {@code AdminPermission[this,EXECUTE]}, and the Java Runtime
+	 *         Environment supports permissions.
+	 * @see #start(int)
+	 */
+	void stop() throws BundleException;
+
+	/**
+	 * Updates this bundle from an {@code InputStream}.
+	 * 
+	 * <p>
+	 * If the specified {@code InputStream} is {@code null}, the Framework must
+	 * create the {@code InputStream} from which to read the updated bundle by
+	 * interpreting, in an implementation dependent manner, this bundle's
+	 * {@link Constants#BUNDLE_UPDATELOCATION Bundle-UpdateLocation} Manifest
+	 * header, if present, or this bundle's original location.
+	 * 
+	 * <p>
+	 * If this bundle's state is {@code ACTIVE}, it must be stopped before the
+	 * update and started after the update successfully completes.
+	 * 
+	 * <p>
+	 * If this bundle has exported any packages that are imported by another
+	 * bundle, these packages must remain exported until the
+	 * {@link FrameworkWiring#refreshBundles(java.util.Collection, FrameworkListener...)
+	 * FrameworkWiring.refreshBundles} method has been has been called or the
+	 * Framework is relaunched.
+	 * 
+	 * <p>
+	 * The following steps are required to update a bundle:
+	 * <ol>
+	 * <li>If this bundle's state is {@code UNINSTALLED} then an
+	 * {@code IllegalStateException} is thrown.
+	 * 
+	 * <li>If this bundle's state is {@code ACTIVE}, {@code STARTING} or
+	 * {@code STOPPING}, this bundle is stopped as described in the
+	 * {@code Bundle.stop} method. If {@code Bundle.stop} throws an exception,
+	 * the exception is rethrown terminating the update.
+	 * 
+	 * <li>The updated version of this bundle is read from the input stream and
+	 * installed. If the Framework is unable to install the updated version of
+	 * this bundle, the original version of this bundle must be restored and a
+	 * {@code BundleException} must be thrown after completion of the remaining
+	 * steps.
+	 * 
+	 * <li>This bundle's state is set to {@code INSTALLED}.
+	 * 
+	 * <li>If the updated version of this bundle was successfully installed, a
+	 * bundle event of type {@link BundleEvent#UPDATED} is fired.
+	 * 
+	 * <li>If this bundle's state was originally {@code ACTIVE}, the updated
+	 * bundle is started as described in the {@code Bundle.start} method. If
+	 * {@code Bundle.start} throws an exception, a Framework event of type
+	 * {@link FrameworkEvent#ERROR} is fired containing the exception.
+	 * </ol>
+	 * 
+	 * <b>Preconditions </b>
+	 * <ul>
+	 * <li>{@code getState()} not in &#x007B; {@code UNINSTALLED} &#x007D;.
+	 * </ul>
+	 * <b>Postconditions, no exceptions thrown </b>
+	 * <ul>
+	 * <li>{@code getState()} in &#x007B; {@code INSTALLED}, {@code RESOLVED},
+	 * {@code ACTIVE} &#x007D;.
+	 * <li>This bundle has been updated.
+	 * </ul>
+	 * <b>Postconditions, when an exception is thrown </b>
+	 * <ul>
+	 * <li>{@code getState()} in &#x007B; {@code INSTALLED}, {@code RESOLVED},
+	 * {@code ACTIVE} &#x007D;.
+	 * <li>Original bundle is still used; no update occurred.
+	 * </ul>
+	 * 
+	 * @param input The {@code InputStream} from which to read the new bundle or
+	 *        {@code null} to indicate the Framework must create the input
+	 *        stream from this bundle's {@link Constants#BUNDLE_UPDATELOCATION
+	 *        Bundle-UpdateLocation} Manifest header, if present, or this
+	 *        bundle's original location. The input stream must always be closed
+	 *        when this method completes, even if an exception is thrown.
+	 * @throws BundleException If this bundle could not be updated.
+	 *         BundleException types thrown by this method include:
+	 *         {@link BundleException#READ_ERROR},
+	 *         {@link BundleException#DUPLICATE_BUNDLE_ERROR},
+	 *         {@link BundleException#MANIFEST_ERROR},
+	 *         {@link BundleException#NATIVECODE_ERROR},
+	 *         {@link BundleException#RESOLVE_ERROR},
+	 *         {@link BundleException#STATECHANGE_ERROR}, and
+	 *         {@link BundleException#ACTIVATOR_ERROR}.
+	 * @throws IllegalStateException If this bundle has been uninstalled or this
+	 *         bundle tries to change its own state.
+	 * @throws SecurityException If the caller does not have the appropriate
+	 *         {@code AdminPermission[this,LIFECYCLE]} for both the current
+	 *         bundle and the updated bundle, and the Java Runtime Environment
+	 *         supports permissions.
+	 * @see #stop()
+	 * @see #start()
+	 */
+	void update(InputStream input) throws BundleException;
+
+	/**
+	 * Updates this bundle.
+	 * 
+	 * <p>
+	 * This method performs the same function as calling
+	 * {@link #update(InputStream)} with a {@code null} InputStream.
+	 * 
+	 * @throws BundleException If this bundle could not be updated.
+	 *         BundleException types thrown by this method include:
+	 *         {@link BundleException#READ_ERROR},
+	 *         {@link BundleException#DUPLICATE_BUNDLE_ERROR},
+	 *         {@link BundleException#MANIFEST_ERROR},
+	 *         {@link BundleException#NATIVECODE_ERROR},
+	 *         {@link BundleException#RESOLVE_ERROR},
+	 *         {@link BundleException#STATECHANGE_ERROR}, and
+	 *         {@link BundleException#ACTIVATOR_ERROR}.
+	 * @throws IllegalStateException If this bundle has been uninstalled or this
+	 *         bundle tries to change its own state.
+	 * @throws SecurityException If the caller does not have the appropriate
+	 *         {@code AdminPermission[this,LIFECYCLE]} for both the current
+	 *         bundle and the updated bundle, and the Java Runtime Environment
+	 *         supports permissions.
+	 * @see #update(InputStream)
+	 */
+	void update() throws BundleException;
+
+	/**
+	 * Uninstalls this bundle.
+	 * 
+	 * <p>
+	 * This method causes the Framework to notify other bundles that this bundle
+	 * is being uninstalled, and then puts this bundle into the
+	 * {@code UNINSTALLED} state. The Framework must remove any resources
+	 * related to this bundle that it is able to remove.
+	 * 
+	 * <p>
+	 * If this bundle has exported any packages, the Framework must continue to
+	 * make these packages available to their importing bundles until the
+	 * {@link FrameworkWiring#refreshBundles(java.util.Collection, FrameworkListener...)
+	 * FrameworkWiring.refreshBundles} method has been called or the Framework
+	 * is relaunched.
+	 * 
+	 * <p>
+	 * The following steps are required to uninstall a bundle:
+	 * <ol>
+	 * <li>If this bundle's state is {@code UNINSTALLED} then an
+	 * {@code IllegalStateException} is thrown.
+	 * 
+	 * <li>If this bundle's state is {@code ACTIVE}, {@code STARTING} or
+	 * {@code STOPPING}, this bundle is stopped as described in the
+	 * {@code Bundle.stop} method. If {@code Bundle.stop} throws an exception, a
+	 * Framework event of type {@link FrameworkEvent#ERROR} is fired containing
+	 * the exception.
+	 * 
+	 * <li>This bundle's state is set to {@code UNINSTALLED}.
+	 * 
+	 * <li>A bundle event of type {@link BundleEvent#UNINSTALLED} is fired.
+	 * 
+	 * <li>This bundle and any persistent storage area provided for this bundle
+	 * by the Framework are removed.
+	 * </ol>
+	 * 
+	 * <b>Preconditions </b>
+	 * <ul>
+	 * <li>{@code getState()} not in &#x007B; {@code UNINSTALLED} &#x007D;.
+	 * </ul>
+	 * <b>Postconditions, no exceptions thrown </b>
+	 * <ul>
+	 * <li>{@code getState()} in &#x007B; {@code UNINSTALLED} &#x007D;.
+	 * <li>This bundle has been uninstalled.
+	 * </ul>
+	 * <b>Postconditions, when an exception is thrown </b>
+	 * <ul>
+	 * <li>{@code getState()} not in &#x007B; {@code UNINSTALLED} &#x007D;.
+	 * <li>This Bundle has not been uninstalled.
+	 * </ul>
+	 * 
+	 * @throws BundleException If the uninstall failed. This can occur if
+	 *         another thread is attempting to change this bundle's state and
+	 *         does not complete in a timely manner. BundleException types
+	 *         thrown by this method include:
+	 *         {@link BundleException#STATECHANGE_ERROR}
+	 * @throws IllegalStateException If this bundle has been uninstalled or this
+	 *         bundle tries to change its own state.
+	 * @throws SecurityException If the caller does not have the appropriate
+	 *         {@code AdminPermission[this,LIFECYCLE]}, and the Java Runtime
+	 *         Environment supports permissions.
+	 * @see #stop()
+	 */
+	void uninstall() throws BundleException;
+
+	/**
+	 * Returns this bundle's Manifest headers and values. This method returns
+	 * all the Manifest headers and values from the main section of this
+	 * bundle's Manifest file; that is, all lines prior to the first blank line.
+	 * 
+	 * <p>
+	 * Manifest header names are case-insensitive. The methods of the returned
+	 * {@code Dictionary} object must operate on header names in a
+	 * case-insensitive manner.
+	 * 
+	 * If a Manifest header value starts with "%", it must be
+	 * localized according to the default locale. If no localization is found
+	 * for a header value, the header value without the leading "%" is
+	 * returned.
+	 * 
+	 * <p>
+	 * For example, the following Manifest headers and values are included if
+	 * they are present in the Manifest file:
+	 * 
+	 * <pre>
+	 *     Bundle-Name
+	 *     Bundle-Vendor
+	 *     Bundle-Version
+	 *     Bundle-Description
+	 *     Bundle-DocURL
+	 *     Bundle-ContactAddress
+	 * </pre>
+	 * 
+	 * <p>
+	 * This method must continue to return Manifest header information while
+	 * this bundle is in the {@code UNINSTALLED} state.
+	 * 
+	 * @return An unmodifiable {@code Dictionary} object containing this
+	 *         bundle's Manifest headers and values.
+	 * @throws SecurityException If the caller does not have the appropriate
+	 *         {@code AdminPermission[this,METADATA]}, and the Java Runtime
+	 *         Environment supports permissions.
+	 * @see Constants#BUNDLE_LOCALIZATION
+	 */
+	Dictionary<String, String> getHeaders();
+
+	/**
+	 * Returns this bundle's unique identifier. This bundle is assigned a unique
+	 * identifier by the Framework when it was installed in the OSGi
+	 * environment.
+	 * 
+	 * <p>
+	 * A bundle's unique identifier has the following attributes:
+	 * <ul>
+	 * <li>Is unique and persistent.
+	 * <li>Is a {@code long}.
+	 * <li>Its value is not reused for another bundle, even after a bundle is
+	 * uninstalled.
+	 * <li>Does not change while a bundle remains installed.
+	 * <li>Does not change when a bundle is updated.
+	 * </ul>
+	 * 
+	 * <p>
+	 * This method must continue to return this bundle's unique identifier while
+	 * this bundle is in the {@code UNINSTALLED} state.
+	 * 
+	 * @return The unique identifier of this bundle.
+	 */
+	long getBundleId();
+
+	/**
+	 * Returns this bundle's location identifier.
+	 * 
+	 * <p>
+	 * The location identifier is the location passed to
+	 * {@code BundleContext.installBundle} when a bundle is installed. The
+	 * location identifier does not change while this bundle remains installed,
+	 * even if this bundle is updated.
+	 * 
+	 * <p>
+	 * This method must continue to return this bundle's location identifier
+	 * while this bundle is in the {@code UNINSTALLED} state.
+	 * 
+	 * @return The string representation of this bundle's location identifier.
+	 * @throws SecurityException If the caller does not have the appropriate
+	 *         {@code AdminPermission[this,METADATA]}, and the Java Runtime
+	 *         Environment supports permissions.
+	 */
+	String getLocation();
+
+	/**
+	 * Returns this bundle's {@code ServiceReference} list for all services it
+	 * has registered or {@code null} if this bundle has no registered services.
+	 * 
+	 * <p>
+	 * If the Java runtime supports permissions, a {@code ServiceReference}
+	 * object to a service is included in the returned list only if the caller
+	 * has the {@code ServicePermission} to get the service using at least one
+	 * of the named classes the service was registered under.
+	 * 
+	 * <p>
+	 * The list is valid at the time of the call to this method, however, as the
+	 * Framework is a very dynamic environment, services can be modified or
+	 * unregistered at anytime.
+	 * 
+	 * @return An array of {@code ServiceReference} objects or {@code null}.
+	 * @throws IllegalStateException If this bundle has been uninstalled.
+	 * @see ServiceRegistration
+	 * @see ServiceReference
+	 * @see ServicePermission
+	 */
+	ServiceReference<?>[] getRegisteredServices();
+
+	/**
+	 * Returns this bundle's {@code ServiceReference} list for all services it
+	 * is using or returns {@code null} if this bundle is not using any
+	 * services. A bundle is considered to be using a service if its use count
+	 * for that service is greater than zero.
+	 * 
+	 * <p>
+	 * If the Java Runtime Environment supports permissions, a
+	 * {@code ServiceReference} object to a service is included in the returned
+	 * list only if the caller has the {@code ServicePermission} to get the
+	 * service using at least one of the named classes the service was
+	 * registered under.
+	 * <p>
+	 * The list is valid at the time of the call to this method, however, as the
+	 * Framework is a very dynamic environment, services can be modified or
+	 * unregistered at anytime.
+	 * 
+	 * @return An array of {@code ServiceReference} objects or {@code null}.
+	 * @throws IllegalStateException If this bundle has been uninstalled.
+	 * @see ServiceReference
+	 * @see ServicePermission
+	 */
+	ServiceReference<?>[] getServicesInUse();
+
+	/**
+	 * Determines if this bundle has the specified permissions.
+	 * 
+	 * <p>
+	 * If the Java Runtime Environment does not support permissions, this method
+	 * always returns {@code true}.
+	 * <p>
+	 * {@code permission} is of type {@code Object} to avoid referencing the
+	 * {@code java.security.Permission} class directly. This is to allow the
+	 * Framework to be implemented in Java environments which do not support
+	 * permissions.
+	 * 
+	 * <p>
+	 * If the Java Runtime Environment does support permissions, this bundle and
+	 * all its resources including embedded JAR files, belong to the same
+	 * {@code java.security.ProtectionDomain}; that is, they must share the same
+	 * set of permissions.
+	 * 
+	 * @param permission The permission to verify.
+	 * @return {@code true} if this bundle has the specified permission or the
+	 *         permissions possessed by this bundle imply the specified
+	 *         permission; {@code false} if this bundle does not have the
+	 *         specified permission or {@code permission} is not an
+	 *         {@code instanceof} {@code java.security.Permission}.
+	 * @throws IllegalStateException If this bundle has been uninstalled.
+	 */
+	boolean hasPermission(Object permission);
+
+	/**
+	 * Find the specified resource from this bundle's class loader.
+	 * 
+	 * This bundle's class loader is called to search for the specified
+	 * resource. If this bundle's state is {@code INSTALLED}, this method must
+	 * attempt to resolve this bundle before attempting to get the specified
+	 * resource. If this bundle cannot be resolved, then only this bundle must
+	 * be searched for the specified resource. Imported packages cannot be
+	 * searched when this bundle has not been resolved. If this bundle is a
+	 * fragment bundle then {@code null} is returned.
+	 * <p>
+	 * Note: Jar and zip files are not required to include directory entries.
+	 * URLs to directory entries will not be returned if the bundle contents do
+	 * not contain directory entries.
+	 * 
+	 * @param name The name of the resource. See {@code ClassLoader.getResource}
+	 *        for a description of the format of a resource name.
+	 * @return A URL to the named resource, or {@code null} if the resource
+	 *         could not be found or if this bundle is a fragment bundle or if
+	 *         the caller does not have the appropriate
+	 *         {@code AdminPermission[this,RESOURCE]}, and the Java Runtime
+	 *         Environment supports permissions.
+	 * @throws IllegalStateException If this bundle has been uninstalled.
+	 * @see #getEntry(String)
+	 * @see #findEntries(String, String, boolean)
+	 * @since 1.1
+	 */
+	URL getResource(String name);
+
+	/**
+	 * Returns this bundle's Manifest headers and values localized to the
+	 * specified locale.
+	 * 
+	 * <p>
+	 * This method performs the same function as {@code Bundle.getHeaders()}
+	 * except the manifest header values are localized to the specified locale.
+	 * 
+	 * <p>
+	 * If a Manifest header value starts with "%", it must be
+	 * localized according to the specified locale. If a locale is specified and
+	 * cannot be found, then the header values must be returned using the
+	 * default locale. Localizations are searched for in the following order:
+	 * 
+	 * <pre>
+	 *   bn + "_" + Ls + "_" + Cs + "_" + Vs
+	 *   bn + "_" + Ls + "_" + Cs
+	 *   bn + "_" + Ls
+	 *   bn + "_" + Ld + "_" + Cd + "_" + Vd
+	 *   bn + "_" + Ld + "_" + Cd
+	 *   bn + "_" + Ld
+	 *   bn
+	 * </pre>
+	 * 
+	 * Where {@code bn} is this bundle's localization basename, {@code Ls},
+	 * {@code Cs} and {@code Vs} are the specified locale (language, country,
+	 * variant) and {@code Ld}, {@code Cd} and {@code Vd} are the default locale
+	 * (language, country, variant).
+	 * 
+	 * If {@code null} is specified as the locale string, the header values must
+	 * be localized using the default locale. If the empty string ("")
+	 * is specified as the locale string, the header values must not be
+	 * localized and the raw (unlocalized) header values, including any leading
+	 * "%", must be returned. If no localization is found for a header
+	 * value, the header value without the leading "%" is returned.
+	 * 
+	 * <p>
+	 * This method must continue to return Manifest header information while
+	 * this bundle is in the {@code UNINSTALLED} state, however the header
+	 * values must only be available in the raw and default locale values.
+	 * 
+	 * @param locale The locale name into which the header values are to be
+	 *        localized. If the specified locale is {@code null} then the locale
+	 *        returned by {@code java.util.Locale.getDefault} is used. If the
+	 *        specified locale is the empty string, this method will return the
+	 *        raw (unlocalized) manifest headers including any leading
+	 *        "%".
+	 * @return An unmodifiable {@code Dictionary} object containing this
+	 *         bundle's Manifest headers and values.
+	 * @throws SecurityException If the caller does not have the appropriate
+	 *         {@code AdminPermission[this,METADATA]}, and the Java Runtime
+	 *         Environment supports permissions.
+	 * @see #getHeaders()
+	 * @see Constants#BUNDLE_LOCALIZATION
+	 * @since 1.3
+	 */
+	Dictionary<String, String> getHeaders(String locale);
+
+	/**
+	 * Returns the symbolic name of this bundle as specified by its
+	 * {@code Bundle-SymbolicName} manifest header. The bundle symbolic name
+	 * should be based on the reverse domain name naming convention like that
+	 * used for java packages.
+	 * 
+	 * <p>
+	 * This method must continue to return this bundle's symbolic name while
+	 * this bundle is in the {@code UNINSTALLED} state.
+	 * 
+	 * @return The symbolic name of this bundle or {@code null} if this bundle
+	 *         does not have a symbolic name.
+	 * @since 1.3
+	 */
+	String getSymbolicName();
+
+	/**
+	 * Loads the specified class using this bundle's class loader.
+	 * 
+	 * <p>
+	 * If this bundle is a fragment bundle then this method must throw a
+	 * {@code ClassNotFoundException}.
+	 * 
+	 * <p>
+	 * If this bundle's state is {@code INSTALLED}, this method must attempt to
+	 * resolve this bundle before attempting to load the class.
+	 * 
+	 * <p>
+	 * If this bundle cannot be resolved, a Framework event of type
+	 * {@link FrameworkEvent#ERROR} is fired containing a
+	 * {@code BundleException} with details of the reason this bundle could not
+	 * be resolved. This method must then throw a {@code ClassNotFoundException}.
+	 * 
+	 * <p>
+	 * If this bundle's state is {@code UNINSTALLED}, then an
+	 * {@code IllegalStateException} is thrown.
+	 * 
+	 * @param name The name of the class to load.
+	 * @return The Class object for the requested class.
+	 * @throws ClassNotFoundException If no such class can be found or if this
+	 *         bundle is a fragment bundle or if the caller does not have the
+	 *         appropriate {@code AdminPermission[this,CLASS]}, and the Java
+	 *         Runtime Environment supports permissions.
+	 * @throws IllegalStateException If this bundle has been uninstalled.
+	 * @since 1.3
+	 */
+	Class<?> loadClass(String name) throws ClassNotFoundException;
+
+	/**
+	 * Find the specified resources from this bundle's class loader.
+	 * 
+	 * This bundle's class loader is called to search for the specified
+	 * resources. If this bundle's state is {@code INSTALLED}, this method must
+	 * attempt to resolve this bundle before attempting to get the specified
+	 * resources. If this bundle cannot be resolved, then only this bundle must
+	 * be searched for the specified resources. Imported packages cannot be
+	 * searched when a bundle has not been resolved. If this bundle is a
+	 * fragment bundle then {@code null} is returned.
+	 * <p>
+	 * Note: Jar and zip files are not required to include directory entries.
+	 * URLs to directory entries will not be returned if the bundle contents do
+	 * not contain directory entries.
+	 * 
+	 * @param name The name of the resource. See
+	 *        {@code ClassLoader.getResources} for a description of the format
+	 *        of a resource name.
+	 * @return An enumeration of URLs to the named resources, or {@code null} if
+	 *         the resource could not be found or if this bundle is a fragment
+	 *         bundle or if the caller does not have the appropriate
+	 *         {@code AdminPermission[this,RESOURCE]}, and the Java Runtime
+	 *         Environment supports permissions.
+	 * @throws IllegalStateException If this bundle has been uninstalled.
+	 * @throws IOException If there is an I/O error.
+	 * @since 1.3
+	 */
+	Enumeration<URL> getResources(String name) throws IOException;
+
+	/**
+	 * Returns an Enumeration of all the paths ({@code String} objects) to
+	 * entries within this bundle whose longest sub-path matches the specified
+	 * path. This bundle's class loader is not used to search for entries. Only
+	 * the contents of this bundle are searched.
+	 * <p>
+	 * The specified path is always relative to the root of this bundle and may
+	 * begin with a "/". A path value of "/" indicates the
+	 * root of this bundle.
+	 * <p>
+	 * Returned paths indicating subdirectory paths end with a "/".
+	 * The returned paths are all relative to the root of this bundle and must
+	 * not begin with "/".
+	 * <p>
+	 * Note: Jar and zip files are not required to include directory entries.
+	 * Paths to directory entries will not be returned if the bundle contents do
+	 * not contain directory entries.
+	 * 
+	 * @param path The path name for which to return entry paths.
+	 * @return An Enumeration of the entry paths ({@code String} objects) or
+	 *         {@code null} if no entry could be found or if the caller does not
+	 *         have the appropriate {@code AdminPermission[this,RESOURCE]} and
+	 *         the Java Runtime Environment supports permissions.
+	 * @throws IllegalStateException If this bundle has been uninstalled.
+	 * @since 1.3
+	 */
+	Enumeration<String> getEntryPaths(String path);
+
+	/**
+	 * Returns a URL to the entry at the specified path in this bundle. This
+	 * bundle's class loader is not used to search for the entry. Only the
+	 * contents of this bundle are searched for the entry.
+	 * <p>
+	 * The specified path is always relative to the root of this bundle and may
+	 * begin with "/". A path value of "/" indicates the
+	 * root of this bundle.
+	 * <p>
+	 * Note: Jar and zip files are not required to include directory entries.
+	 * URLs to directory entries will not be returned if the bundle contents do
+	 * not contain directory entries.
+	 * 
+	 * @param path The path name of the entry.
+	 * @return A URL to the entry, or {@code null} if no entry could be found or
+	 *         if the caller does not have the appropriate
+	 *         {@code AdminPermission[this,RESOURCE]} and the Java Runtime
+	 *         Environment supports permissions.
+	 * @throws IllegalStateException If this bundle has been uninstalled.
+	 * @since 1.3
+	 */
+	URL getEntry(String path);
+
+	/**
+	 * Returns the time when this bundle was last modified. A bundle is
+	 * considered to be modified when it is installed, updated or uninstalled.
+	 * 
+	 * <p>
+	 * The time value is the number of milliseconds since January 1, 1970,
+	 * 00:00:00 UTC.
+	 * 
+	 * @return The time when this bundle was last modified.
+	 * @since 1.3
+	 */
+	long getLastModified();
+
+	/**
+	 * Returns entries in this bundle and its attached fragments. This bundle's
+	 * class loader is not used to search for entries. Only the contents of this
+	 * bundle and its attached fragments are searched for the specified entries.
+	 * 
+	 * If this bundle's state is {@code INSTALLED}, this method must attempt to
+	 * resolve this bundle before attempting to find entries.
+	 * 
+	 * <p>
+	 * This method is intended to be used to obtain configuration, setup,
+	 * localization and other information from this bundle. This method takes
+	 * into account that the "contents" of this bundle can be extended
+	 * with fragments. This "bundle space" is not a namespace with
+	 * unique members; the same entry name can be present multiple times. This
+	 * method therefore returns an enumeration of URL objects. These URLs can
+	 * come from different JARs but have the same path name. This method can
+	 * either return only entries in the specified path or recurse into
+	 * subdirectories returning entries in the directory tree beginning at the
+	 * specified path. Fragments can be attached after this bundle is resolved,
+	 * possibly changing the set of URLs returned by this method. If this bundle
+	 * is not resolved, only the entries in the JAR file of this bundle are
+	 * returned.
+	 * <p>
+	 * Examples:
+	 * 
+	 * <pre>
+	 * // List all XML files in the OSGI-INF directory and below
+	 * Enumeration e = b.findEntries("OSGI-INF", "*.xml", true);
+	 * 
+	 * // Find a specific localization file
+	 * Enumeration e = b.findEntries("OSGI-INF/l10n",
+	 *     "bundle_nl_DU.properties", false);
+	 * if (e.hasMoreElements())
+	 *     return (URL) e.nextElement();
+	 * </pre>
+	 * 
+	 * <p>
+	 * URLs for directory entries must have their path end with "/".
+	 * <p>
+	 * Note: Jar and zip files are not required to include directory entries.
+	 * URLs to directory entries will not be returned if the bundle contents do
+	 * not contain directory entries.
+	 * 
+	 * @param path The path name in which to look. The path is always relative
+	 *        to the root of this bundle and may begin with "/". A
+	 *        path value of "/" indicates the root of this bundle.
+	 * @param filePattern The file name pattern for selecting entries in the
+	 *        specified path. The pattern is only matched against the last
+	 *        element of the entry path. If the entry is a directory then the
+	 *        trailing "/" is not used for pattern matching. Substring
+	 *        matching is supported, as specified in the Filter specification,
+	 *        using the wildcard character ("*"). If null is
+	 *        specified, this is equivalent to "*" and matches all
+	 *        files.
+	 * @param recurse If {@code true}, recurse into subdirectories. Otherwise
+	 *        only return entries from the specified path.
+	 * @return An enumeration of URL objects for each matching entry, or
+	 *         {@code null} if no matching entry could be found or if the caller
+	 *         does not have the appropriate
+	 *         {@code AdminPermission[this,RESOURCE]}, and the Java Runtime
+	 *         Environment supports permissions. The URLs are sorted such that
+	 *         entries from this bundle are returned first followed by the
+	 *         entries from attached fragments in attachment order. If this
+	 *         bundle is a fragment, then only matching entries in this fragment
+	 *         are returned.
+	 * @throws IllegalStateException If this bundle has been uninstalled.
+	 * @since 1.3
+	 */
+	Enumeration<URL> findEntries(String path, String filePattern, boolean recurse);
+
+	/**
+	 * Returns this bundle's {@link BundleContext}. The returned
+	 * {@code BundleContext} can be used by the caller to act on behalf of this
+	 * bundle.
+	 * 
+	 * <p>
+	 * If this bundle is not in the {@link #STARTING}, {@link #ACTIVE}, or
+	 * {@link #STOPPING} states or this bundle is a fragment bundle, then this
+	 * bundle has no valid {@code BundleContext}. This method will return
+	 * {@code null} if this bundle has no valid {@code BundleContext}.
+	 * 
+	 * @return A {@code BundleContext} for this bundle or {@code null} if this
+	 *         bundle has no valid {@code BundleContext}.
+	 * @throws SecurityException If the caller does not have the appropriate
+	 *         {@code AdminPermission[this,CONTEXT]}, and the Java Runtime
+	 *         Environment supports permissions.
+	 * @since 1.4
+	 */
+	BundleContext getBundleContext();
+
+	/**
+	 * Return the certificates for the signers of this bundle and the
+	 * certificate chains for those signers.
+	 * 
+	 * @param signersType If {@link #SIGNERS_ALL} is specified, then information
+	 *        on all signers of this bundle is returned. If
+	 *        {@link #SIGNERS_TRUSTED} is specified, then only information on
+	 *        the signers of this bundle trusted by the framework is returned.
+	 * @return The {@code X509Certificate}s for the signers of this bundle and
+	 *         the {@code X509Certificate} chains for those signers. The keys of
+	 *         the {@code Map} are the {@code X509Certificate}s of the signers
+	 *         of this bundle. The value for a key is a {@code List} containing
+	 *         the {@code X509Certificate} chain for the signer. The first item
+	 *         in the {@code List} is the signer's {@code X509Certificate} which
+	 *         is then followed by the rest of the {@code X509Certificate}
+	 *         chain. The returned {@code Map} will be empty if there are no
+	 *         signers. The returned {@code Map} is the property of the caller
+	 *         who is free to modify it.
+	 * @throws IllegalArgumentException If the specified {@code signersType} is
+	 *         not {@link #SIGNERS_ALL} or {@link #SIGNERS_TRUSTED}.
+	 * @since 1.5
+	 */
+	Map<X509Certificate, List<X509Certificate>> getSignerCertificates(int signersType);
+
+	/**
+	 * Returns the version of this bundle as specified by its
+	 * {@code Bundle-Version} manifest header. If this bundle does not have a
+	 * specified version then {@link Version#emptyVersion} is returned.
+	 * 
+	 * <p>
+	 * This method must continue to return this bundle's version while this
+	 * bundle is in the {@code UNINSTALLED} state.
+	 * 
+	 * @return The version of this bundle.
+	 * @since 1.5
+	 */
+	Version getVersion();
+
+	/**
+	 * Adapt this bundle to the specified type.
+	 * 
+	 * <p>
+	 * Adapting this bundle to the specified type may require certain checks,
+	 * including security checks, to succeed. If a check does not succeed, then
+	 * this bundle cannot be adapted and {@code null} is returned.
+	 * 
+	 * @param <A> The type to which this bundle is to be adapted.
+	 * @param type Class object for the type to which this bundle is to be
+	 *        adapted.
+	 * @return The object, of the specified type, to which this bundle has been
+	 *         adapted or {@code null} if this bundle cannot be adapted to the
+	 *         specified type.
+	 * @throws SecurityException If the caller does not have the appropriate
+	 *         {@code AdaptPermission[type,this,ADAPT]}, and the Java Runtime
+	 *         Environment supports permissions.
+	 * @since 1.6
+	 */
+	<A> A adapt(Class<A> type);
+
+	/**
+	 * Creates a {@code File} object for a file in the persistent storage area
+	 * provided for this bundle by the Framework. This method will return
+	 * {@code null} if the platform does not have file system support or this
+	 * bundle is a fragment bundle.
+	 * 
+	 * <p>
+	 * A {@code File} object for the base directory of the persistent storage
+	 * area provided for this bundle by the Framework can be obtained by calling
+	 * this method with an empty string as {@code filename}.
+	 * 
+	 * <p>
+	 * If the Java Runtime Environment supports permissions, the Framework will
+	 * ensure that this bundle has the {@code java.io.FilePermission} with
+	 * actions {@code read},{@code write},{@code delete} for all files
+	 * (recursively) in the persistent storage area provided for this bundle.
+	 * 
+	 * @param filename A relative name to the file to be accessed.
+	 * @return A {@code File} object that represents the requested file or
+	 *         {@code null} if the platform does not have file system support or
+	 *         this bundle is a fragment bundle.
+	 * @throws IllegalStateException If this bundle has been uninstalled.
+	 * @since 1.6
+	 */
+	File getDataFile(String filename);
+}
diff --git a/src/main/java/org/osgi/framework/BundleActivator.java b/src/main/java/org/osgi/framework/BundleActivator.java
new file mode 100644
index 0000000..acdb825
--- /dev/null
+++ b/src/main/java/org/osgi/framework/BundleActivator.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) OSGi Alliance (2000, 2012). All Rights Reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.framework;
+
+/**
+ * Customizes the starting and stopping of a bundle.
+ * <p>
+ * {@code BundleActivator} is an interface that may be implemented when a bundle
+ * is started or stopped. The Framework can create instances of a bundle's
+ * {@code BundleActivator} as required. If an instance's
+ * {@code BundleActivator.start} method executes successfully, it is guaranteed
+ * that the same instance's {@code BundleActivator.stop} method will be called
+ * when the bundle is to be stopped. The Framework must not concurrently call a
+ * {@code BundleActivator} object.
+ * 
+ * <p>
+ * {@code BundleActivator} is specified through the {@code Bundle-Activator}
+ * Manifest header. A bundle can only specify a single {@code BundleActivator}
+ * in the Manifest file. Fragment bundles must not have a
+ * {@code BundleActivator}. The form of the Manifest header is:
+ * 
+ * <p>
+ * {@code Bundle-Activator: <i>class-name</i>}
+ * 
+ * <p>
+ * where {@code <i>class-name</i>} is a fully qualified Java classname.
+ * <p>
+ * The specified {@code BundleActivator} class must have a public constructor
+ * that takes no parameters so that a {@code BundleActivator} object can be
+ * created by {@code Class.newInstance()}.
+ * 
+ * @NotThreadSafe
+ * @version $Id: f5b2debe0064ab60669102d0a087feaeab13dc0e $
+ */
+
+public interface BundleActivator {
+	/**
+	 * Called when this bundle is started so the Framework can perform the
+	 * bundle-specific activities necessary to start this bundle. This method
+	 * can be used to register services or to allocate any resources that this
+	 * bundle needs.
+	 * 
+	 * <p>
+	 * This method must complete and return to its caller in a timely manner.
+	 * 
+	 * @param context The execution context of the bundle being started.
+	 * @throws Exception If this method throws an exception, this bundle is
+	 *         marked as stopped and the Framework will remove this bundle's
+	 *         listeners, unregister all services registered by this bundle, and
+	 *         release all services used by this bundle.
+	 */
+	public void start(BundleContext context) throws Exception;
+
+	/**
+	 * Called when this bundle is stopped so the Framework can perform the
+	 * bundle-specific activities necessary to stop the bundle. In general, this
+	 * method should undo the work that the {@code BundleActivator.start} method
+	 * started. There should be no active threads that were started by this
+	 * bundle when this bundle returns. A stopped bundle must not call any
+	 * Framework objects.
+	 * 
+	 * <p>
+	 * This method must complete and return to its caller in a timely manner.
+	 * 
+	 * @param context The execution context of the bundle being stopped.
+	 * @throws Exception If this method throws an exception, the bundle is still
+	 *         marked as stopped, and the Framework will remove the bundle's
+	 *         listeners, unregister all services registered by the bundle, and
+	 *         release all services used by the bundle.
+	 */
+	public void stop(BundleContext context) throws Exception;
+}
diff --git a/src/main/java/org/osgi/framework/BundleContext.java b/src/main/java/org/osgi/framework/BundleContext.java
new file mode 100644
index 0000000..71c3b92
--- /dev/null
+++ b/src/main/java/org/osgi/framework/BundleContext.java
@@ -0,0 +1,864 @@
+/*
+ * Copyright (c) OSGi Alliance (2000, 2012). All Rights Reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.framework;
+
+import java.io.File;
+import java.io.InputStream;
+import java.util.Collection;
+import java.util.Dictionary;
+
+/**
+ * A bundle's execution context within the Framework. The context is used to
+ * grant access to other methods so that this bundle can interact with the
+ * Framework.
+ * 
+ * <p>
+ * {@code BundleContext} methods allow a bundle to:
+ * <ul>
+ * <li>Subscribe to events published by the Framework.
+ * <li>Register service objects with the Framework service registry.
+ * <li>Retrieve {@code ServiceReferences} from the Framework service registry.
+ * <li>Get and release service objects for a referenced service.
+ * <li>Install new bundles in the Framework.
+ * <li>Get the list of bundles installed in the Framework.
+ * <li>Get the {@link Bundle} object for a bundle.
+ * <li>Create {@code File} objects for files in a persistent storage area
+ * provided for the bundle by the Framework.
+ * </ul>
+ * 
+ * <p>
+ * A {@code BundleContext} object will be created for a bundle when the bundle
+ * is started. The {@code Bundle} object associated with a {@code BundleContext}
+ * object is called the <em>context bundle</em>.
+ * 
+ * <p>
+ * The {@code BundleContext} object will be passed to the
+ * {@link BundleActivator#start(BundleContext)} method during activation of the
+ * context bundle. The same {@code BundleContext} object will be passed to the
+ * {@link BundleActivator#stop(BundleContext)} method when the context bundle is
+ * stopped. A {@code BundleContext} object is generally for the private use of
+ * its associated bundle and is not meant to be shared with other bundles in the
+ * OSGi environment.
+ * 
+ * <p>
+ * The {@code BundleContext} object is only valid during the execution of its
+ * context bundle; that is, during the period from when the context bundle is in
+ * the {@code STARTING}, {@code STOPPING}, and {@code ACTIVE} bundle states. If
+ * the {@code BundleContext} object is used subsequently, an
+ * {@code IllegalStateException} must be thrown. The {@code BundleContext}
+ * object must never be reused after its context bundle is stopped.
+ * 
+ * <p>
+ * Two {@code BundleContext} objects are equal if they both refer to the same
+ * execution context of a bundle. The Framework is the only entity that can
+ * create {@code BundleContext} objects and they are only valid within the
+ * Framework that created them.
+ * 
+ * <p>
+ * A {@link Bundle} can be {@link Bundle#adapt(Class) adapted} to its
+ * {@code BundleContext}. In order for this to succeed, the caller must have the
+ * appropriate {@code AdminPermission[bundle,CONTEXT]} if the Java Runtime
+ * Environment supports permissions.
+ * 
+ * @ThreadSafe
+ * @noimplement
+ * @version $Id: 4f166fd274f3965e48a7dbc239213d00e062b6d0 $
+ */
+
+public interface BundleContext extends BundleReference {
+
+	/**
+	 * Returns the value of the specified property. If the key is not found in
+	 * the Framework properties, the system properties are then searched. The
+	 * method returns {@code null} if the property is not found.
+	 * 
+	 * <p>
+	 * All bundles must have permission to read properties whose names start
+	 * with "org.osgi.".
+	 * 
+	 * @param key The name of the requested property.
+	 * @return The value of the requested property, or {@code null} if the
+	 *         property is undefined.
+	 * @throws SecurityException If the caller does not have the appropriate
+	 *         {@code PropertyPermission} to read the property, and the Java
+	 *         Runtime Environment supports permissions.
+	 */
+	String getProperty(String key);
+
+	/**
+	 * Returns the {@code Bundle} object associated with this
+	 * {@code BundleContext}. This bundle is called the context bundle.
+	 * 
+	 * @return The {@code Bundle} object associated with this
+	 *         {@code BundleContext}.
+	 * @throws IllegalStateException If this BundleContext is no longer valid.
+	 */
+	Bundle getBundle();
+
+	/**
+	 * Installs a bundle from the specified {@code InputStream} object.
+	 * 
+	 * <p>
+	 * If the specified {@code InputStream} is {@code null}, the Framework must
+	 * create the {@code InputStream} from which to read the bundle by
+	 * interpreting, in an implementation dependent manner, the specified
+	 * {@code location}.
+	 * 
+	 * <p>
+	 * The specified {@code location} identifier will be used as the identity of
+	 * the bundle. Every installed bundle is uniquely identified by its location
+	 * identifier which is typically in the form of a URL.
+	 * 
+	 * <p>
+	 * The following steps are required to install a bundle:
+	 * <ol>
+	 * <li>If a bundle containing the same location identifier is already
+	 * installed, the {@code Bundle} object for that bundle is returned.
+	 * 
+	 * <li>The bundle's content is read from the input stream. If this fails, a
+	 * {@link BundleException} is thrown.
+	 * 
+	 * <li>The bundle's associated resources are allocated. The associated
+	 * resources minimally consist of a unique identifier and a persistent
+	 * storage area if the platform has file system support. If this step fails,
+	 * a {@code BundleException} is thrown.
+	 * 
+	 * <li>The bundle's state is set to {@code INSTALLED}.
+	 * 
+	 * <li>A bundle event of type {@link BundleEvent#INSTALLED} is fired.
+	 * 
+	 * <li>The {@code Bundle} object for the newly or previously installed
+	 * bundle is returned.
+	 * </ol>
+	 * 
+	 * <b>Postconditions, no exceptions thrown </b>
+	 * <ul>
+	 * <li>{@code getState()} in &#x007B; {@code INSTALLED}, {@code RESOLVED}
+	 * &#x007D;.
+	 * <li>Bundle has a unique ID.
+	 * </ul>
+	 * <b>Postconditions, when an exception is thrown </b>
+	 * <ul>
+	 * <li>Bundle is not installed. If there was an existing bundle for the
+	 * specified location, then that bundle must still be in the state it was
+	 * prior to calling this method.</li>
+	 * </ul>
+	 * 
+	 * @param location The location identifier of the bundle to install.
+	 * @param input The {@code InputStream} object from which this bundle will
+	 *        be read or {@code null} to indicate the Framework must create the
+	 *        input stream from the specified location identifier. The input
+	 *        stream must always be closed when this method completes, even if
+	 *        an exception is thrown.
+	 * @return The {@code Bundle} object of the installed bundle.
+	 * @throws BundleException If the installation failed. BundleException types
+	 *         thrown by this method include: {@link BundleException#READ_ERROR}
+	 *         , {@link BundleException#DUPLICATE_BUNDLE_ERROR},
+	 *         {@link BundleException#MANIFEST_ERROR}, and
+	 *         {@link BundleException#REJECTED_BY_HOOK}.
+	 * @throws SecurityException If the caller does not have the appropriate
+	 *         {@code AdminPermission[installed bundle,LIFECYCLE]}, and the Java
+	 *         Runtime Environment supports permissions.
+	 * @throws IllegalStateException If this BundleContext is no longer valid.
+	 */
+	Bundle installBundle(String location, InputStream input) throws BundleException;
+
+	/**
+	 * Installs a bundle from the specified {@code location} identifier.
+	 * 
+	 * <p>
+	 * This method performs the same function as calling
+	 * {@link #installBundle(String,InputStream)} with the specified
+	 * {@code location} identifier and a {@code null} InputStream.
+	 * 
+	 * @param location The location identifier of the bundle to install.
+	 * @return The {@code Bundle} object of the installed bundle.
+	 * @throws BundleException If the installation failed. BundleException types
+	 *         thrown by this method include: {@link BundleException#READ_ERROR}
+	 *         , {@link BundleException#DUPLICATE_BUNDLE_ERROR},
+	 *         {@link BundleException#MANIFEST_ERROR}, and
+	 *         {@link BundleException#REJECTED_BY_HOOK}.
+	 * @throws SecurityException If the caller does not have the appropriate
+	 *         {@code AdminPermission[installed bundle,LIFECYCLE]}, and the Java
+	 *         Runtime Environment supports permissions.
+	 * @throws IllegalStateException If this BundleContext is no longer valid.
+	 * @see #installBundle(String, InputStream)
+	 */
+	Bundle installBundle(String location) throws BundleException;
+
+	/**
+	 * Returns the bundle with the specified identifier.
+	 * 
+	 * @param id The identifier of the bundle to retrieve.
+	 * @return A {@code Bundle} object or {@code null} if the identifier does
+	 *         not match any installed bundle.
+	 */
+	Bundle getBundle(long id);
+
+	/**
+	 * Returns a list of all installed bundles.
+	 * <p>
+	 * This method returns a list of all bundles installed in the OSGi
+	 * environment at the time of the call to this method. However, since the
+	 * Framework is a very dynamic environment, bundles can be installed or
+	 * uninstalled at anytime.
+	 * 
+	 * @return An array of {@code Bundle} objects, one object per installed
+	 *         bundle.
+	 */
+	Bundle[] getBundles();
+
+	/**
+	 * Adds the specified {@code ServiceListener} object with the specified
+	 * {@code filter} to the context bundle's list of listeners. See
+	 * {@link Filter} for a description of the filter syntax.
+	 * {@code ServiceListener} objects are notified when a service has a
+	 * lifecycle state change.
+	 * 
+	 * <p>
+	 * If the context bundle's list of listeners already contains a listener
+	 * {@code l} such that {@code (l==listener)}, then this method replaces that
+	 * listener's filter (which may be {@code null}) with the specified one
+	 * (which may be {@code null}).
+	 * 
+	 * <p>
+	 * The listener is called if the filter criteria is met. To filter based
+	 * upon the class of the service, the filter should reference the
+	 * {@link Constants#OBJECTCLASS} property. If {@code filter} is {@code null}
+	 * , all services are considered to match the filter.
+	 * 
+	 * <p>
+	 * When using a {@code filter}, it is possible that the {@code ServiceEvent}
+	 * s for the complete lifecycle of a service will not be delivered to the
+	 * listener. For example, if the {@code filter} only matches when the
+	 * property {@code x} has the value {@code 1}, the listener will not be
+	 * called if the service is registered with the property {@code x} not set
+	 * to the value {@code 1}. Subsequently, when the service is modified
+	 * setting property {@code x} to the value {@code 1}, the filter will match
+	 * and the listener will be called with a {@code ServiceEvent} of type
+	 * {@code MODIFIED}. Thus, the listener will not be called with a
+	 * {@code ServiceEvent} of type {@code REGISTERED}.
+	 * 
+	 * <p>
+	 * If the Java Runtime Environment supports permissions, the
+	 * {@code ServiceListener} object will be notified of a service event only
+	 * if the bundle that is registering it has the {@code ServicePermission} to
+	 * get the service using at least one of the named classes the service was
+	 * registered under.
+	 * 
+	 * @param listener The {@code ServiceListener} object to be added.
+	 * @param filter The filter criteria.
+	 * @throws InvalidSyntaxException If {@code filter} contains an invalid
+	 *         filter string that cannot be parsed.
+	 * @throws IllegalStateException If this BundleContext is no longer valid.
+	 * @see ServiceEvent
+	 * @see ServiceListener
+	 * @see ServicePermission
+	 */
+	void addServiceListener(ServiceListener listener, String filter) throws InvalidSyntaxException;
+
+	/**
+	 * Adds the specified {@code ServiceListener} object to the context bundle's
+	 * list of listeners.
+	 * 
+	 * <p>
+	 * This method is the same as calling
+	 * {@code BundleContext.addServiceListener(ServiceListener listener,
+	 * String filter)} with {@code filter} set to {@code null}.
+	 * 
+	 * @param listener The {@code ServiceListener} object to be added.
+	 * @throws IllegalStateException If this BundleContext is no longer valid.
+	 * @see #addServiceListener(ServiceListener, String)
+	 */
+	void addServiceListener(ServiceListener listener);
+
+	/**
+	 * Removes the specified {@code ServiceListener} object from the context
+	 * bundle's list of listeners.
+	 * 
+	 * <p>
+	 * If {@code listener} is not contained in this context bundle's list of
+	 * listeners, this method does nothing.
+	 * 
+	 * @param listener The {@code ServiceListener} to be removed.
+	 * @throws IllegalStateException If this BundleContext is no longer valid.
+	 */
+	void removeServiceListener(ServiceListener listener);
+
+	/**
+	 * Adds the specified {@code BundleListener} object to the context bundle's
+	 * list of listeners if not already present. BundleListener objects are
+	 * notified when a bundle has a lifecycle state change.
+	 * 
+	 * <p>
+	 * If the context bundle's list of listeners already contains a listener
+	 * {@code l} such that {@code (l==listener)}, this method does nothing.
+	 * 
+	 * @param listener The {@code BundleListener} to be added.
+	 * @throws IllegalStateException If this BundleContext is no longer valid.
+	 * @throws SecurityException If listener is a
+	 *         {@code SynchronousBundleListener} and the caller does not have
+	 *         the appropriate {@code AdminPermission[context bundle,LISTENER]},
+	 *         and the Java Runtime Environment supports permissions.
+	 * @see BundleEvent
+	 * @see BundleListener
+	 */
+	void addBundleListener(BundleListener listener);
+
+	/**
+	 * Removes the specified {@code BundleListener} object from the context
+	 * bundle's list of listeners.
+	 * 
+	 * <p>
+	 * If {@code listener} is not contained in the context bundle's list of
+	 * listeners, this method does nothing.
+	 * 
+	 * @param listener The {@code BundleListener} object to be removed.
+	 * @throws IllegalStateException If this BundleContext is no longer valid.
+	 * @throws SecurityException If listener is a
+	 *         {@code SynchronousBundleListener} and the caller does not have
+	 *         the appropriate {@code AdminPermission[context bundle,LISTENER]},
+	 *         and the Java Runtime Environment supports permissions.
+	 */
+	void removeBundleListener(BundleListener listener);
+
+	/**
+	 * Adds the specified {@code FrameworkListener} object to the context
+	 * bundle's list of listeners if not already present. FrameworkListeners are
+	 * notified of general Framework events.
+	 * 
+	 * <p>
+	 * If the context bundle's list of listeners already contains a listener
+	 * {@code l} such that {@code (l==listener)}, this method does nothing.
+	 * 
+	 * @param listener The {@code FrameworkListener} object to be added.
+	 * @throws IllegalStateException If this BundleContext is no longer valid.
+	 * @see FrameworkEvent
+	 * @see FrameworkListener
+	 */
+	void addFrameworkListener(FrameworkListener listener);
+
+	/**
+	 * Removes the specified {@code FrameworkListener} object from the context
+	 * bundle's list of listeners.
+	 * 
+	 * <p>
+	 * If {@code listener} is not contained in the context bundle's list of
+	 * listeners, this method does nothing.
+	 * 
+	 * @param listener The {@code FrameworkListener} object to be removed.
+	 * @throws IllegalStateException If this BundleContext is no longer valid.
+	 */
+	void removeFrameworkListener(FrameworkListener listener);
+
+	/**
+	 * Registers the specified service object with the specified properties
+	 * under the specified class names into the Framework. A
+	 * {@code ServiceRegistration} object is returned. The
+	 * {@code ServiceRegistration} object is for the private use of the bundle
+	 * registering the service and should not be shared with other bundles. The
+	 * registering bundle is defined to be the context bundle. Other bundles can
+	 * locate the service by using one of the
+	 * {@link #getServiceReferences(Class, String)},
+	 * {@link #getServiceReferences(String, String)},
+	 * {@link #getServiceReference(Class)} or
+	 * {@link #getServiceReference(String)} methods.
+	 * 
+	 * <p>
+	 * A bundle can register a service object that implements the
+	 * {@link ServiceFactory} interface to have more flexibility in providing
+	 * service objects to other bundles.
+	 * 
+	 * <p>
+	 * The following steps are required to register a service:
+	 * <ol>
+	 * <li>If {@code service} is not a {@code ServiceFactory}, an
+	 * {@code IllegalArgumentException} is thrown if {@code service} is not an
+	 * {@code instanceof} all the specified class names.
+	 * <li>The Framework adds the following service properties to the service
+	 * properties from the specified {@code Dictionary} (which may be
+	 * {@code null}): <br/>
+	 * A property named {@link Constants#SERVICE_ID} identifying the
+	 * registration number of the service <br/>
+	 * A property named {@link Constants#OBJECTCLASS} containing all the
+	 * specified classes. <br/>
+	 * Properties with these names in the specified {@code Dictionary} will be
+	 * ignored.
+	 * <li>The service is added to the Framework service registry and may now be
+	 * used by other bundles.
+	 * <li>A service event of type {@link ServiceEvent#REGISTERED} is fired.
+	 * <li>A {@code ServiceRegistration} object for this registration is
+	 * returned.
+	 * </ol>
+	 * 
+	 * @param clazzes The class names under which the service can be located.
+	 *        The class names in this array will be stored in the service's
+	 *        properties under the key {@link Constants#OBJECTCLASS}.
+	 * @param service The service object or a {@code ServiceFactory} object.
+	 * @param properties The properties for this service. The keys in the
+	 *        properties object must all be {@code String} objects. See
+	 *        {@link Constants} for a list of standard service property keys.
+	 *        Changes should not be made to this object after calling this
+	 *        method. To update the service's properties the
+	 *        {@link ServiceRegistration#setProperties(Dictionary)} method must
+	 *        be called. The set of properties may be {@code null} if the
+	 *        service has no properties.
+	 * @return A {@code ServiceRegistration} object for use by the bundle
+	 *         registering the service to update the service's properties or to
+	 *         unregister the service.
+	 * @throws IllegalArgumentException If one of the following is true:
+	 *         <ul>
+	 *         <li>{@code service} is {@code null}. <li>{@code service} is not a
+	 *         {@code ServiceFactory} object and is not an instance of all the
+	 *         named classes in {@code clazzes}. <li> {@code properties}
+	 *         contains case variants of the same key name.
+	 *         </ul>
+	 * @throws SecurityException If the caller does not have the
+	 *         {@code ServicePermission} to register the service for all the
+	 *         named classes and the Java Runtime Environment supports
+	 *         permissions.
+	 * @throws IllegalStateException If this BundleContext is no longer valid.
+	 * @see ServiceRegistration
+	 * @see ServiceFactory
+	 */
+	ServiceRegistration<?> registerService(String[] clazzes, Object service, Dictionary<String, ?> properties);
+
+	/**
+	 * Registers the specified service object with the specified properties
+	 * under the specified class name with the Framework.
+	 * 
+	 * <p>
+	 * This method is otherwise identical to
+	 * {@link #registerService(String[], Object, Dictionary)} and is provided as
+	 * a convenience when {@code service} will only be registered under a single
+	 * class name. Note that even in this case the value of the service's
+	 * {@link Constants#OBJECTCLASS} property will be an array of string, rather
+	 * than just a single string.
+	 * 
+	 * @param clazz The class name under which the service can be located.
+	 * @param service The service object or a {@code ServiceFactory} object.
+	 * @param properties The properties for this service.
+	 * @return A {@code ServiceRegistration} object for use by the bundle
+	 *         registering the service to update the service's properties or to
+	 *         unregister the service.
+	 * @throws IllegalStateException If this BundleContext is no longer valid.
+	 * @see #registerService(String[], Object, Dictionary)
+	 */
+	ServiceRegistration<?> registerService(String clazz, Object service, Dictionary<String, ?> properties);
+
+	/**
+	 * Registers the specified service object with the specified properties
+	 * under the name of the specified class with the Framework.
+	 * 
+	 * <p>
+	 * This method is otherwise identical to
+	 * {@link #registerService(String, Object, Dictionary)} and is provided to
+	 * return a type safe {@code ServiceRegistration}.
+	 * 
+	 * @param <S> Type of Service.
+	 * @param clazz The class under whose name the service can be located.
+	 * @param service The service object or a {@code ServiceFactory} object.
+	 * @param properties The properties for this service.
+	 * @return A {@code ServiceRegistration} object for use by the bundle
+	 *         registering the service to update the service's properties or to
+	 *         unregister the service.
+	 * @throws IllegalStateException If this BundleContext is no longer valid.
+	 * @see #registerService(String, Object, Dictionary)
+	 * @since 1.6
+	 */
+	<S> ServiceRegistration<S> registerService(Class<S> clazz, S service, Dictionary<String, ?> properties);
+
+	/**
+	 * Returns an array of {@code ServiceReference} objects. The returned array
+	 * of {@code ServiceReference} objects contains services that were
+	 * registered under the specified class, match the specified filter
+	 * expression, and the packages for the class names under which the services
+	 * were registered match the context bundle's packages as defined in
+	 * {@link ServiceReference#isAssignableTo(Bundle, String)}.
+	 * 
+	 * <p>
+	 * The list is valid at the time of the call to this method. However since
+	 * the Framework is a very dynamic environment, services can be modified or
+	 * unregistered at any time.
+	 * 
+	 * <p>
+	 * The specified {@code filter} expression is used to select the registered
+	 * services whose service properties contain keys and values which satisfy
+	 * the filter expression. See {@link Filter} for a description of the filter
+	 * syntax. If the specified {@code filter} is {@code null}, all registered
+	 * services are considered to match the filter. If the specified
+	 * {@code filter} expression cannot be parsed, an
+	 * {@link InvalidSyntaxException} will be thrown with a human readable
+	 * message where the filter became unparsable.
+	 * 
+	 * <p>
+	 * The result is an array of {@code ServiceReference} objects for all
+	 * services that meet all of the following conditions:
+	 * <ul>
+	 * <li>If the specified class name, {@code clazz}, is not {@code null}, the
+	 * service must have been registered with the specified class name. The
+	 * complete list of class names with which a service was registered is
+	 * available from the service's {@link Constants#OBJECTCLASS objectClass}
+	 * property.
+	 * <li>If the specified {@code filter} is not {@code null}, the filter
+	 * expression must match the service.
+	 * <li>If the Java Runtime Environment supports permissions, the caller must
+	 * have {@code ServicePermission} with the {@code GET} action for at least
+	 * one of the class names under which the service was registered.
+	 * <li>For each class name with which the service was registered, calling
+	 * {@link ServiceReference#isAssignableTo(Bundle, String)} with the context
+	 * bundle and the class name on the service's {@code ServiceReference}
+	 * object must return {@code true}
+	 * </ul>
+	 * 
+	 * @param clazz The class name with which the service was registered or
+	 *        {@code null} for all services.
+	 * @param filter The filter expression or {@code null} for all services.
+	 * @return An array of {@code ServiceReference} objects or {@code null} if
+	 *         no services are registered which satisfy the search.
+	 * @throws InvalidSyntaxException If the specified {@code filter} contains
+	 *         an invalid filter expression that cannot be parsed.
+	 * @throws IllegalStateException If this BundleContext is no longer valid.
+	 */
+	ServiceReference<?>[] getServiceReferences(String clazz, String filter) throws InvalidSyntaxException;
+
+	/**
+	 * Returns an array of {@code ServiceReference} objects. The returned array
+	 * of {@code ServiceReference} objects contains services that were
+	 * registered under the specified class and match the specified filter
+	 * expression.
+	 * 
+	 * <p>
+	 * The list is valid at the time of the call to this method. However since
+	 * the Framework is a very dynamic environment, services can be modified or
+	 * unregistered at any time.
+	 * 
+	 * <p>
+	 * The specified {@code filter} expression is used to select the registered
+	 * services whose service properties contain keys and values which satisfy
+	 * the filter expression. See {@link Filter} for a description of the filter
+	 * syntax. If the specified {@code filter} is {@code null}, all registered
+	 * services are considered to match the filter. If the specified
+	 * {@code filter} expression cannot be parsed, an
+	 * {@link InvalidSyntaxException} will be thrown with a human readable
+	 * message where the filter became unparsable.
+	 * 
+	 * <p>
+	 * The result is an array of {@code ServiceReference} objects for all
+	 * services that meet all of the following conditions:
+	 * <ul>
+	 * <li>If the specified class name, {@code clazz}, is not {@code null}, the
+	 * service must have been registered with the specified class name. The
+	 * complete list of class names with which a service was registered is
+	 * available from the service's {@link Constants#OBJECTCLASS objectClass}
+	 * property.
+	 * <li>If the specified {@code filter} is not {@code null}, the filter
+	 * expression must match the service.
+	 * <li>If the Java Runtime Environment supports permissions, the caller must
+	 * have {@code ServicePermission} with the {@code GET} action for at least
+	 * one of the class names under which the service was registered.
+	 * </ul>
+	 * 
+	 * @param clazz The class name with which the service was registered or
+	 *        {@code null} for all services.
+	 * @param filter The filter expression or {@code null} for all services.
+	 * @return An array of {@code ServiceReference} objects or {@code null} if
+	 *         no services are registered which satisfy the search.
+	 * @throws InvalidSyntaxException If the specified {@code filter} contains
+	 *         an invalid filter expression that cannot be parsed.
+	 * @throws IllegalStateException If this BundleContext is no longer valid.
+	 * @since 1.3
+	 */
+	ServiceReference<?>[] getAllServiceReferences(String clazz, String filter) throws InvalidSyntaxException;
+
+	/**
+	 * Returns a {@code ServiceReference} object for a service that implements
+	 * and was registered under the specified class.
+	 * 
+	 * <p>
+	 * The returned {@code ServiceReference} object is valid at the time of the
+	 * call to this method. However as the Framework is a very dynamic
+	 * environment, services can be modified or unregistered at any time.
+	 * 
+	 * <p>
+	 * This method is the same as calling
+	 * {@link #getServiceReferences(String, String)} with a {@code null} filter
+	 * expression and then finding the reference with the highest priority. It
+	 * is provided as a convenience for when the caller is interested in any
+	 * service that implements the specified class.
+	 * <p>
+	 * If multiple such services exist, the service with the highest priority is
+	 * selected. This priority is defined as the service reference with the
+	 * highest ranking (as specified in its {@link Constants#SERVICE_RANKING}
+	 * property) is returned.
+	 * <p>
+	 * If there is a tie in ranking, the service with the lowest service ID (as
+	 * specified in its {@link Constants#SERVICE_ID} property); that is, the
+	 * service that was registered first is returned.
+	 * 
+	 * @param clazz The class name with which the service was registered.
+	 * @return A {@code ServiceReference} object, or {@code null} if no services
+	 *         are registered which implement the named class.
+	 * @throws IllegalStateException If this BundleContext is no longer valid.
+	 * @see #getServiceReferences(String, String)
+	 */
+	ServiceReference<?> getServiceReference(String clazz);
+
+	/**
+	 * Returns a {@code ServiceReference} object for a service that implements
+	 * and was registered under the name of the specified class.
+	 * 
+	 * <p>
+	 * The returned {@code ServiceReference} object is valid at the time of the
+	 * call to this method. However as the Framework is a very dynamic
+	 * environment, services can be modified or unregistered at any time.
+	 * 
+	 * <p>
+	 * This method is the same as calling
+	 * {@link #getServiceReferences(Class, String)} with a {@code null} filter
+	 * expression. It is provided as a convenience for when the caller is
+	 * interested in any service that implements the specified class.
+	 * <p>
+	 * If multiple such services exist, the service with the highest ranking (as
+	 * specified in its {@link Constants#SERVICE_RANKING} property) is returned.
+	 * <p>
+	 * If there is a tie in ranking, the service with the lowest service ID (as
+	 * specified in its {@link Constants#SERVICE_ID} property); that is, the
+	 * service that was registered first is returned.
+	 * 
+	 * @param <S> Type of Service.
+	 * @param clazz The class under whose name the service was registered. Must
+	 *        not be {@code null}.
+	 * @return A {@code ServiceReference} object, or {@code null} if no services
+	 *         are registered which implement the specified class.
+	 * @throws IllegalStateException If this BundleContext is no longer valid.
+	 * @see #getServiceReferences(Class, String)
+	 * @since 1.6
+	 */
+	<S> ServiceReference<S> getServiceReference(Class<S> clazz);
+
+	/**
+	 * Returns a collection of {@code ServiceReference} objects. The returned
+	 * collection of {@code ServiceReference} objects contains services that
+	 * were registered under the name of the specified class, match the
+	 * specified filter expression, and the packages for the class names under
+	 * which the services were registered match the context bundle's packages as
+	 * defined in {@link ServiceReference#isAssignableTo(Bundle, String)}.
+	 * 
+	 * <p>
+	 * The collection is valid at the time of the call to this method. However
+	 * since the Framework is a very dynamic environment, services can be
+	 * modified or unregistered at any time.
+	 * 
+	 * <p>
+	 * The specified {@code filter} expression is used to select the registered
+	 * services whose service properties contain keys and values which satisfy
+	 * the filter expression. See {@link Filter} for a description of the filter
+	 * syntax. If the specified {@code filter} is {@code null}, all registered
+	 * services are considered to match the filter. If the specified
+	 * {@code filter} expression cannot be parsed, an
+	 * {@link InvalidSyntaxException} will be thrown with a human readable
+	 * message where the filter became unparsable.
+	 * 
+	 * <p>
+	 * The result is a collection of {@code ServiceReference} objects for all
+	 * services that meet all of the following conditions:
+	 * <ul>
+	 * <li>The service must have been registered with the name of the specified
+	 * class. The complete list of class names with which a service was
+	 * registered is available from the service's {@link Constants#OBJECTCLASS
+	 * objectClass} property.
+	 * <li>If the specified {@code filter} is not {@code null}, the filter
+	 * expression must match the service.
+	 * <li>If the Java Runtime Environment supports permissions, the caller must
+	 * have {@code ServicePermission} with the {@code GET} action for at least
+	 * one of the class names under which the service was registered.
+	 * <li>For each class name with which the service was registered, calling
+	 * {@link ServiceReference#isAssignableTo(Bundle, String)} with the context
+	 * bundle and the class name on the service's {@code ServiceReference}
+	 * object must return {@code true}
+	 * </ul>
+	 * 
+	 * @param <S> Type of Service
+	 * @param clazz The class under whose name the service was registered. Must
+	 *        not be {@code null}.
+	 * @param filter The filter expression or {@code null} for all services.
+	 * @return A collection of {@code ServiceReference} objects. May be empty if
+	 *         no services are registered which satisfy the search.
+	 * @throws InvalidSyntaxException If the specified {@code filter} contains
+	 *         an invalid filter expression that cannot be parsed.
+	 * @throws IllegalStateException If this BundleContext is no longer valid.
+	 * @since 1.6
+	 */
+	<S> Collection<ServiceReference<S>> getServiceReferences(Class<S> clazz, String filter) throws InvalidSyntaxException;
+
+	/**
+	 * Returns the service object referenced by the specified
+	 * {@code ServiceReference} object.
+	 * <p>
+	 * A bundle's use of a service is tracked by the bundle's use count of that
+	 * service. Each time a service's service object is returned by
+	 * {@link #getService(ServiceReference)} the context bundle's use count for
+	 * that service is incremented by one. Each time the service is released by
+	 * {@link #ungetService(ServiceReference)} the context bundle's use count
+	 * for that service is decremented by one.
+	 * <p>
+	 * When a bundle's use count for a service drops to zero, the bundle should
+	 * no longer use that service.
+	 * 
+	 * <p>
+	 * This method will always return {@code null} when the service associated
+	 * with this {@code reference} has been unregistered.
+	 * 
+	 * <p>
+	 * The following steps are required to get the service object:
+	 * <ol>
+	 * <li>If the service has been unregistered, {@code null} is returned.
+	 * <li>If the context bundle's use count for the service is currently zero
+	 * and the service was registered with an object implementing the
+	 * {@code ServiceFactory} interface, the
+	 * {@link ServiceFactory#getService(Bundle, ServiceRegistration)} method is
+	 * called to create a service object for the context bundle. If the service
+	 * object returned by the {@code ServiceFactory} object is {@code null}, not
+	 * an {@code instanceof} all the classes named when the service was
+	 * registered or the {@code ServiceFactory} object throws an exception or
+	 * will be recursively called for the context bundle, {@code null} is
+	 * returned and a Framework event of type {@link FrameworkEvent#ERROR}
+	 * containing a {@link ServiceException} describing the error is fired. <br>
+	 * This service object is cached by the Framework. While the context
+	 * bundle's use count for the service is greater than zero, subsequent calls
+	 * to get the services's service object for the context bundle will return
+	 * the cached service object.
+	 * <li>The context bundle's use count for this service is incremented by
+	 * one.
+	 * <li>The service object for the service is returned.
+	 * </ol>
+	 * 
+	 * @param <S> Type of Service.
+	 * @param reference A reference to the service.
+	 * @return A service object for the service associated with
+	 *         {@code reference} or {@code null} if the service is not
+	 *         registered, the service object returned by a
+	 *         {@code ServiceFactory} does not implement the classes under which
+	 *         it was registered or the {@code ServiceFactory} threw an
+	 *         exception.
+	 * @throws SecurityException If the caller does not have the
+	 *         {@code ServicePermission} to get the service using at least one
+	 *         of the named classes the service was registered under and the
+	 *         Java Runtime Environment supports permissions.
+	 * @throws IllegalStateException If this BundleContext is no longer valid.
+	 * @throws IllegalArgumentException If the specified
+	 *         {@code ServiceReference} was not created by the same framework
+	 *         instance as this {@code BundleContext}.
+	 * @see #ungetService(ServiceReference)
+	 * @see ServiceFactory
+	 */
+	<S> S getService(ServiceReference<S> reference);
+
+	/**
+	 * Releases the service object referenced by the specified
+	 * {@code ServiceReference} object. If the context bundle's use count for
+	 * the service is zero, this method returns {@code false}. Otherwise, the
+	 * context bundle's use count for the service is decremented by one.
+	 * 
+	 * <p>
+	 * The service's service object should no longer be used and all references
+	 * to it should be destroyed when a bundle's use count for the service drops
+	 * to zero.
+	 * 
+	 * <p>
+	 * The following steps are required to unget the service object:
+	 * <ol>
+	 * <li>If the context bundle's use count for the service is zero or the
+	 * service has been unregistered, {@code false} is returned.
+	 * <li>The context bundle's use count for this service is decremented by
+	 * one.
+	 * <li>If the context bundle's use count for the service is currently zero
+	 * and the service was registered with a {@code ServiceFactory} object, the
+	 * {@link ServiceFactory#ungetService(Bundle, ServiceRegistration, Object)}
+	 * method is called to release the service object for the context bundle.
+	 * <li>{@code true} is returned.
+	 * </ol>
+	 * 
+	 * @param reference A reference to the service to be released.
+	 * @return {@code false} if the context bundle's use count for the service
+	 *         is zero or if the service has been unregistered; {@code true}
+	 *         otherwise.
+	 * @throws IllegalStateException If this BundleContext is no longer valid.
+	 * @throws IllegalArgumentException If the specified
+	 *         {@code ServiceReference} was not created by the same framework
+	 *         instance as this {@code BundleContext}.
+	 * @see #getService(ServiceReference)
+	 * @see ServiceFactory
+	 */
+	boolean ungetService(ServiceReference<?> reference);
+
+	/**
+	 * Creates a {@code File} object for a file in the persistent storage area
+	 * provided for the bundle by the Framework. This method will return
+	 * {@code null} if the platform does not have file system support.
+	 * 
+	 * <p>
+	 * A {@code File} object for the base directory of the persistent storage
+	 * area provided for the context bundle by the Framework can be obtained by
+	 * calling this method with an empty string as {@code filename}.
+	 * 
+	 * <p>
+	 * If the Java Runtime Environment supports permissions, the Framework will
+	 * ensure that the bundle has the {@code java.io.FilePermission} with
+	 * actions {@code read},{@code write},{@code delete} for all files
+	 * (recursively) in the persistent storage area provided for the context
+	 * bundle.
+	 * 
+	 * @param filename A relative name to the file to be accessed.
+	 * @return A {@code File} object that represents the requested file or
+	 *         {@code null} if the platform does not have file system support.
+	 * @throws IllegalStateException If this BundleContext is no longer valid.
+	 */
+	File getDataFile(String filename);
+
+	/**
+	 * Creates a {@code Filter} object. This {@code Filter} object may be used
+	 * to match a {@code ServiceReference} object or a {@code Dictionary}
+	 * object.
+	 * 
+	 * <p>
+	 * If the filter cannot be parsed, an {@link InvalidSyntaxException} will be
+	 * thrown with a human readable message where the filter became unparsable.
+	 * 
+	 * @param filter The filter string.
+	 * @return A {@code Filter} object encapsulating the filter string.
+	 * @throws InvalidSyntaxException If {@code filter} contains an invalid
+	 *         filter string that cannot be parsed.
+	 * @throws NullPointerException If {@code filter} is null.
+	 * @throws IllegalStateException If this BundleContext is no longer valid.
+	 * @see "Framework specification for a description of the filter string syntax."
+	 * @see FrameworkUtil#createFilter(String)
+	 * @since 1.1
+	 */
+	Filter createFilter(String filter) throws InvalidSyntaxException;
+
+	/**
+	 * Returns the bundle with the specified location.
+	 * 
+	 * @param location The location of the bundle to retrieve.
+	 * @return A {@code Bundle} object or {@code null} if the location does not
+	 *         match any installed bundle.
+	 * @since 1.6
+	 */
+	Bundle getBundle(String location);
+}
diff --git a/src/main/java/org/osgi/framework/BundleEvent.java b/src/main/java/org/osgi/framework/BundleEvent.java
new file mode 100644
index 0000000..04a34a3
--- /dev/null
+++ b/src/main/java/org/osgi/framework/BundleEvent.java
@@ -0,0 +1,235 @@
+/*
+ * Copyright (c) OSGi Alliance (2000, 2012). All Rights Reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.framework;
+
+import java.util.EventObject;
+
+/**
+ * An event from the Framework describing a bundle lifecycle change.
+ * <p>
+ * {@code BundleEvent} objects are delivered to
+ * {@code SynchronousBundleListener}s and {@code BundleListener}s when a change
+ * occurs in a bundle's lifecycle. A type code is used to identify the event
+ * type for future extendability.
+ * 
+ * <p>
+ * OSGi Alliance reserves the right to extend the set of types.
+ * 
+ * @Immutable
+ * @see BundleListener
+ * @see SynchronousBundleListener
+ * @version $Id: 9e2102212eb526b5f11fdde4b0fc5c171a0b39c8 $
+ */
+
+public class BundleEvent extends EventObject {
+	static final long		serialVersionUID	= 4080640865971756012L;
+	/**
+	 * Bundle that had a change occur in its lifecycle.
+	 */
+	private final Bundle	bundle;
+
+	/**
+	 * Type of bundle lifecycle change.
+	 */
+	private final int		type;
+
+	/**
+	 * The bundle has been installed.
+	 * 
+	 * @see BundleContext#installBundle(String)
+	 */
+	public final static int	INSTALLED			= 0x00000001;
+
+	/**
+	 * The bundle has been started.
+	 * <p>
+	 * The bundle's {@link BundleActivator#start(BundleContext) BundleActivator
+	 * start} method has been executed if the bundle has a bundle activator
+	 * class.
+	 * 
+	 * @see Bundle#start()
+	 */
+	public final static int	STARTED				= 0x00000002;
+
+	/**
+	 * The bundle has been stopped.
+	 * <p>
+	 * The bundle's {@link BundleActivator#stop(BundleContext) BundleActivator
+	 * stop} method has been executed if the bundle has a bundle activator
+	 * class.
+	 * 
+	 * @see Bundle#stop()
+	 */
+	public final static int	STOPPED				= 0x00000004;
+
+	/**
+	 * The bundle has been updated.
+	 * 
+	 * @see Bundle#update()
+	 */
+	public final static int	UPDATED				= 0x00000008;
+
+	/**
+	 * The bundle has been uninstalled.
+	 * 
+	 * @see Bundle#uninstall()
+	 */
+	public final static int	UNINSTALLED			= 0x00000010;
+
+	/**
+	 * The bundle has been resolved.
+	 * 
+	 * @see Bundle#RESOLVED
+	 * @since 1.3
+	 */
+	public final static int	RESOLVED			= 0x00000020;
+
+	/**
+	 * The bundle has been unresolved.
+	 * 
+	 * @see Bundle#INSTALLED
+	 * @since 1.3
+	 */
+	public final static int	UNRESOLVED			= 0x00000040;
+
+	/**
+	 * The bundle is about to be activated.
+	 * <p>
+	 * The bundle's {@link BundleActivator#start(BundleContext) BundleActivator
+	 * start} method is about to be called if the bundle has a bundle activator
+	 * class. This event is only delivered to {@link SynchronousBundleListener}
+	 * s. It is not delivered to {@code BundleListener}s.
+	 * 
+	 * @see Bundle#start()
+	 * @since 1.3
+	 */
+	public final static int	STARTING			= 0x00000080;
+
+	/**
+	 * The bundle is about to deactivated.
+	 * <p>
+	 * The bundle's {@link BundleActivator#stop(BundleContext) BundleActivator
+	 * stop} method is about to be called if the bundle has a bundle activator
+	 * class. This event is only delivered to {@link SynchronousBundleListener}
+	 * s. It is not delivered to {@code BundleListener}s.
+	 * 
+	 * @see Bundle#stop()
+	 * @since 1.3
+	 */
+	public final static int	STOPPING			= 0x00000100;
+
+	/**
+	 * The bundle will be lazily activated.
+	 * <p>
+	 * The bundle has a {@link Constants#ACTIVATION_LAZY lazy activation policy}
+	 * and is waiting to be activated. It is now in the {@link Bundle#STARTING
+	 * STARTING} state and has a valid {@code BundleContext}. This event is only
+	 * delivered to {@link SynchronousBundleListener}s. It is not delivered to
+	 * {@code BundleListener}s.
+	 * 
+	 * @since 1.4
+	 */
+	public final static int	LAZY_ACTIVATION		= 0x00000200;
+
+	/**
+	 * Bundle that was the origin of the event. For install event type, this is
+	 * the bundle whose context was used to install the bundle. Otherwise it is
+	 * the bundle itself.
+	 * 
+	 * @since 1.6
+	 */
+	private final Bundle	origin;
+
+	/**
+	 * Creates a bundle event of the specified type.
+	 * 
+	 * @param type The event type.
+	 * @param bundle The bundle which had a lifecycle change.
+	 * @param origin The bundle which is the origin of the event. For the event
+	 *        type {@link #INSTALLED}, this is the bundle whose context was used
+	 *        to install the bundle. Otherwise it is the bundle itself.
+	 * @since 1.6
+	 */
+	public BundleEvent(int type, Bundle bundle, Bundle origin) {
+		super(bundle);
+		if (origin == null) {
+			throw new IllegalArgumentException("null origin");
+		}
+		this.bundle = bundle;
+		this.type = type;
+		this.origin = origin;
+	}
+
+	/**
+	 * Creates a bundle event of the specified type.
+	 * 
+	 * @param type The event type.
+	 * @param bundle The bundle which had a lifecycle change. This bundle is
+	 *        used as the origin of the event.
+	 */
+	public BundleEvent(int type, Bundle bundle) {
+		super(bundle);
+		this.bundle = bundle;
+		this.type = type;
+		this.origin = bundle;
+	}
+
+	/**
+	 * Returns the bundle which had a lifecycle change. This bundle is the
+	 * source of the event.
+	 * 
+	 * @return The bundle that had a change occur in its lifecycle.
+	 */
+	public Bundle getBundle() {
+		return bundle;
+	}
+
+	/**
+	 * Returns the type of lifecyle event. The type values are:
+	 * <ul>
+	 * <li>{@link #INSTALLED}
+	 * <li>{@link #RESOLVED}
+	 * <li>{@link #LAZY_ACTIVATION}
+	 * <li>{@link #STARTING}
+	 * <li>{@link #STARTED}
+	 * <li>{@link #STOPPING}
+	 * <li>{@link #STOPPED}
+	 * <li>{@link #UPDATED}
+	 * <li>{@link #UNRESOLVED}
+	 * <li>{@link #UNINSTALLED}
+	 * </ul>
+	 * 
+	 * @return The type of lifecycle event.
+	 */
+	public int getType() {
+		return type;
+	}
+
+	/**
+	 * Returns the bundle that was the origin of the event.
+	 * 
+	 * <p>
+	 * For the event type {@link #INSTALLED}, this is the bundle whose context
+	 * was used to install the bundle. Otherwise it is the bundle itself.
+	 * 
+	 * @return The bundle that was the origin of the event.
+	 * @since 1.6
+	 */
+	public Bundle getOrigin() {
+		return origin;
+	}
+}
diff --git a/src/main/java/org/osgi/framework/BundleException.java b/src/main/java/org/osgi/framework/BundleException.java
new file mode 100644
index 0000000..c47eb77
--- /dev/null
+++ b/src/main/java/org/osgi/framework/BundleException.java
@@ -0,0 +1,235 @@
+/*
+ * Copyright (c) OSGi Alliance (2000, 2012). All Rights Reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.framework;
+
+/**
+ * A Framework exception used to indicate that a bundle lifecycle problem
+ * occurred.
+ * 
+ * <p>
+ * A {@code BundleException} object is created by the Framework to denote an
+ * exception condition in the lifecycle of a bundle. {@code BundleException}s
+ * should not be created by bundle developers. A type code is used to identify
+ * the exception type for future extendability.
+ * 
+ * <p>
+ * OSGi Alliance reserves the right to extend the set of types.
+ * 
+ * <p>
+ * This exception conforms to the general purpose exception chaining mechanism.
+ * 
+ * @version $Id: 0c97ed2696b4576d61440020922b1a97545beb1e $
+ */
+
+public class BundleException extends Exception {
+	static final long		serialVersionUID		= 3571095144220455665L;
+	/**
+	 * Type of bundle exception.
+	 * 
+	 * @since 1.5
+	 */
+	private final int		type;
+
+	/**
+	 * No exception type is specified.
+	 * 
+	 * @since 1.5
+	 */
+	public static final int	UNSPECIFIED				= 0;
+	/**
+	 * The operation was unsupported. This type can be used anywhere a
+	 * BundleException can be thrown.
+	 * 
+	 * @since 1.5
+	 */
+	public static final int	UNSUPPORTED_OPERATION	= 1;
+	/**
+	 * The operation was invalid.
+	 * 
+	 * @since 1.5
+	 */
+	public static final int	INVALID_OPERATION		= 2;
+	/**
+	 * The bundle manifest was in error.
+	 * 
+	 * @since 1.5
+	 */
+	public static final int	MANIFEST_ERROR			= 3;
+	/**
+	 * The bundle was not resolved.
+	 * 
+	 * @since 1.5
+	 */
+	public static final int	RESOLVE_ERROR			= 4;
+	/**
+	 * The bundle activator was in error.
+	 * 
+	 * @since 1.5
+	 */
+	public static final int	ACTIVATOR_ERROR			= 5;
+	/**
+	 * The operation failed due to insufficient permissions.
+	 * 
+	 * @since 1.5
+	 */
+	public static final int	SECURITY_ERROR			= 6;
+	/**
+	 * The operation failed to complete the requested lifecycle state change.
+	 * 
+	 * @since 1.5
+	 */
+	public static final int	STATECHANGE_ERROR		= 7;
+
+	/**
+	 * The bundle could not be resolved due to an error with the
+	 * Bundle-NativeCode header.
+	 * 
+	 * @since 1.5
+	 */
+	public static final int	NATIVECODE_ERROR		= 8;
+
+	/**
+	 * The install or update operation failed because another already installed
+	 * bundle has the same symbolic name and version. This exception type will
+	 * only occur if the framework is configured to only allow a single bundle
+	 * to be installed for a given symbolic name and version.
+	 * 
+	 * @see Constants#FRAMEWORK_BSNVERSION
+	 * @since 1.5
+	 */
+	public static final int	DUPLICATE_BUNDLE_ERROR	= 9;
+
+	/**
+	 * The start transient operation failed because the start level of the
+	 * bundle is greater than the current framework start level
+	 * 
+	 * @since 1.5
+	 */
+	public static final int	START_TRANSIENT_ERROR	= 10;
+
+	/**
+	 * The framework received an error while reading the input stream for a
+	 * bundle.
+	 * 
+	 * @since 1.6
+	 */
+	public static final int	READ_ERROR				= 11;
+
+	/**
+	 * A framework hook rejected the operation.
+	 * 
+	 * @since 1.6
+	 */
+	public static final int	REJECTED_BY_HOOK		= 12;
+
+	/**
+	 * Creates a {@code BundleException} with the specified message and
+	 * exception cause.
+	 * 
+	 * @param msg The associated message.
+	 * @param cause The cause of this exception.
+	 */
+	public BundleException(String msg, Throwable cause) {
+		this(msg, UNSPECIFIED, cause);
+	}
+
+	/**
+	 * Creates a {@code BundleException} with the specified message.
+	 * 
+	 * @param msg The message.
+	 */
+	public BundleException(String msg) {
+		this(msg, UNSPECIFIED);
+	}
+
+	/**
+	 * Creates a {@code BundleException} with the specified message, type and
+	 * exception cause.
+	 * 
+	 * @param msg The associated message.
+	 * @param type The type for this exception.
+	 * @param cause The cause of this exception.
+	 * @since 1.5
+	 */
+	public BundleException(String msg, int type, Throwable cause) {
+		super(msg, cause);
+		this.type = type;
+	}
+
+	/**
+	 * Creates a {@code BundleException} with the specified message and type.
+	 * 
+	 * @param msg The message.
+	 * @param type The type for this exception.
+	 * @since 1.5
+	 */
+	public BundleException(String msg, int type) {
+		super(msg);
+		this.type = type;
+	}
+
+	/**
+	 * Returns the cause of this exception or {@code null} if no cause was
+	 * specified when this exception was created.
+	 * 
+	 * <p>
+	 * This method predates the general purpose exception chaining mechanism.
+	 * The {@code getCause()} method is now the preferred means of obtaining
+	 * this information.
+	 * 
+	 * @return The result of calling {@code getCause()}.
+	 */
+	public Throwable getNestedException() {
+		return getCause();
+	}
+
+	/**
+	 * Returns the cause of this exception or {@code null} if no cause was set.
+	 * 
+	 * @return The cause of this exception or {@code null} if no cause was set.
+	 * @since 1.3
+	 */
+	public Throwable getCause() {
+		return super.getCause();
+	}
+
+	/**
+	 * Initializes the cause of this exception to the specified value.
+	 * 
+	 * @param cause The cause of this exception.
+	 * @return This exception.
+	 * @throws IllegalArgumentException If the specified cause is this
+	 *         exception.
+	 * @throws IllegalStateException If the cause of this exception has already
+	 *         been set.
+	 * @since 1.3
+	 */
+	public Throwable initCause(Throwable cause) {
+		return super.initCause(cause);
+	}
+
+	/**
+	 * Returns the type for this exception or {@code UNSPECIFIED} if the type
+	 * was unspecified or unknown.
+	 * 
+	 * @return The type of this exception.
+	 * @since 1.5
+	 */
+	public int getType() {
+		return type;
+	}
+}
diff --git a/src/main/java/org/osgi/framework/BundleListener.java b/src/main/java/org/osgi/framework/BundleListener.java
new file mode 100644
index 0000000..8d86d30
--- /dev/null
+++ b/src/main/java/org/osgi/framework/BundleListener.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) OSGi Alliance (2000, 2011). All Rights Reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.framework;
+
+import java.util.EventListener;
+
+/**
+ * A {@code BundleEvent} listener. {@code BundleListener} is a listener
+ * interface that may be implemented by a bundle developer. When a
+ * {@code BundleEvent} is fired, it is asynchronously delivered to a
+ * {@code BundleListener}. The Framework delivers {@code BundleEvent} objects to
+ * a {@code BundleListener} in order and must not concurrently call a
+ * {@code BundleListener}.
+ * <p>
+ * A {@code BundleListener} object is registered with the Framework using the
+ * {@link BundleContext#addBundleListener(BundleListener)} method.
+ * {@code BundleListener}s are called with a {@code BundleEvent} object when a
+ * bundle has been installed, resolved, started, stopped, updated, unresolved,
+ * or uninstalled.
+ * 
+ * @see BundleEvent
+ * @NotThreadSafe
+ * @version $Id: d48b4a8a59c839466a3d749dde23980d236f58c6 $
+ */
+
+public interface BundleListener extends EventListener {
+	/**
+	 * Receives notification that a bundle has had a lifecycle change.
+	 * 
+	 * @param event The {@code BundleEvent}.
+	 */
+	public void bundleChanged(BundleEvent event);
+}
diff --git a/src/main/java/org/osgi/framework/BundlePermission.java b/src/main/java/org/osgi/framework/BundlePermission.java
new file mode 100644
index 0000000..f9e64ae
--- /dev/null
+++ b/src/main/java/org/osgi/framework/BundlePermission.java
@@ -0,0 +1,584 @@
+/*
+ * Copyright (c) OSGi Alliance (2004, 2012). All Rights Reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.framework;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.ObjectStreamField;
+import java.security.BasicPermission;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A bundle's authority to require or provide a bundle or to receive or attach
+ * fragments.
+ * 
+ * <p>
+ * A bundle symbolic name defines a unique fully qualified name. Wildcards may
+ * be used.
+ * 
+ * <pre>
+ * name ::= <symbolic name> | <symbolic name ending in ".*"> | *
+ * </pre>
+ * 
+ * Examples:
+ * 
+ * <pre>
+ * org.osgi.example.bundle
+ * org.osgi.example.*
+ * *
+ * </pre>
+ * 
+ * <p>
+ * {@code BundlePermission} has four actions: {@code provide}, {@code require},
+ * {@code host}, and {@code fragment}. The {@code provide} action implies the
+ * {@code require} action.
+ * 
+ * @since 1.3
+ * @ThreadSafe
+ * @version $Id: ccba905e3373800dfdb080118e97145abf778da2 $
+ */
+
+public final class BundlePermission extends BasicPermission {
+
+	private static final long	serialVersionUID	= 3257846601685873716L;
+
+	/**
+	 * The action string {@code provide}. The {@code provide} action implies the
+	 * {@code require} action.
+	 */
+	public final static String	PROVIDE				= "provide";
+
+	/**
+	 * The action string {@code require}. The {@code require} action is implied
+	 * by the {@code provide} action.
+	 */
+	public final static String	REQUIRE				= "require";
+
+	/**
+	 * The action string {@code host}.
+	 */
+	public final static String	HOST				= "host";
+
+	/**
+	 * The action string {@code fragment}.
+	 */
+	public final static String	FRAGMENT			= "fragment";
+
+	private final static int	ACTION_PROVIDE		= 0x00000001;
+	private final static int	ACTION_REQUIRE		= 0x00000002;
+	private final static int	ACTION_HOST			= 0x00000004;
+	private final static int	ACTION_FRAGMENT		= 0x00000008;
+	private final static int	ACTION_ALL			= ACTION_PROVIDE | ACTION_REQUIRE | ACTION_HOST | ACTION_FRAGMENT;
+	final static int			ACTION_NONE			= 0;
+	/**
+	 * The actions mask.
+	 */
+	private transient int		action_mask;
+
+	/**
+	 * The actions in canonical form.
+	 * 
+	 * @serial
+	 */
+	private volatile String		actions				= null;
+
+	/**
+	 * Defines the authority to provide and/or require and or specify a host
+	 * fragment symbolic name within the OSGi environment.
+	 * <p>
+	 * Bundle Permissions are granted over all possible versions of a bundle.
+	 * 
+	 * A bundle that needs to provide a bundle must have the appropriate
+	 * {@code BundlePermission} for the symbolic name; a bundle that requires a
+	 * bundle must have the appropriate {@code BundlePermssion} for that
+	 * symbolic name; a bundle that specifies a fragment host must have the
+	 * appropriate {@code BundlePermission} for that symbolic name.
+	 * 
+	 * @param symbolicName The bundle symbolic name.
+	 * @param actions {@code provide},{@code require}, {@code host},
+	 *        {@code fragment} (canonical order).
+	 */
+	public BundlePermission(String symbolicName, String actions) {
+		this(symbolicName, parseActions(actions));
+	}
+
+	/**
+	 * Package private constructor used by BundlePermissionCollection.
+	 * 
+	 * @param symbolicName the bundle symbolic name
+	 * @param mask the action mask
+	 */
+	BundlePermission(String symbolicName, int mask) {
+		super(symbolicName);
+		setTransients(mask);
+	}
+
+	/**
+	 * Called by constructors and when deserialized.
+	 * 
+	 * @param mask
+	 */
+	private synchronized void setTransients(int mask) {
+		if ((mask == ACTION_NONE) || ((mask & ACTION_ALL) != mask)) {
+			throw new IllegalArgumentException("invalid action string");
+		}
+
+		action_mask = mask;
+	}
+
+	/**
+	 * Returns the current action mask.
+	 * <p>
+	 * Used by the BundlePermissionCollection class.
+	 * 
+	 * @return Current action mask.
+	 */
+	synchronized int getActionsMask() {
+		return action_mask;
+	}
+
+	/**
+	 * Parse action string into action mask.
+	 * 
+	 * @param actions Action string.
+	 * @return action mask.
+	 */
+	private static int parseActions(String actions) {
+		boolean seencomma = false;
+
+		int mask = ACTION_NONE;
+
+		if (actions == null) {
+			return mask;
+		}
+
+		char[] a = actions.toCharArray();
+
+		int i = a.length - 1;
+		if (i < 0)
+			return mask;
+
+		while (i != -1) {
+			char c;
+
+			// skip whitespace
+			while ((i != -1) && ((c = a[i]) == ' ' || c == '\r' || c == '\n' || c == '\f' || c == '\t'))
+				i--;
+
+			// check for the known strings
+			int matchlen;
+
+			if (i >= 6 && (a[i - 6] == 'p' || a[i - 6] == 'P')
+					&& (a[i - 5] == 'r' || a[i - 5] == 'R')
+					&& (a[i - 4] == 'o' || a[i - 4] == 'O')
+					&& (a[i - 3] == 'v' || a[i - 3] == 'V')
+					&& (a[i - 2] == 'i' || a[i - 2] == 'I')
+					&& (a[i - 1] == 'd' || a[i - 1] == 'D')
+					&& (a[i] == 'e' || a[i] == 'E')) {
+				matchlen = 7;
+				mask |= ACTION_PROVIDE | ACTION_REQUIRE;
+			} else
+				if (i >= 6 && (a[i - 6] == 'r' || a[i - 6] == 'R')
+						&& (a[i - 5] == 'e' || a[i - 5] == 'E')
+						&& (a[i - 4] == 'q' || a[i - 4] == 'Q')
+						&& (a[i - 3] == 'u' || a[i - 3] == 'U')
+						&& (a[i - 2] == 'i' || a[i - 2] == 'I')
+						&& (a[i - 1] == 'r' || a[i - 1] == 'R')
+						&& (a[i] == 'e' || a[i] == 'E')) {
+					matchlen = 7;
+					mask |= ACTION_REQUIRE;
+				} else
+					if (i >= 3 && (a[i - 3] == 'h' || a[i - 3] == 'H')
+							&& (a[i - 2] == 'o' || a[i - 2] == 'O')
+							&& (a[i - 1] == 's' || a[i - 1] == 'S')
+							&& (a[i] == 't' || a[i] == 'T')) {
+						matchlen = 4;
+						mask |= ACTION_HOST;
+					} else
+						if (i >= 7 && (a[i - 7] == 'f' || a[i - 7] == 'F')
+								&& (a[i - 6] == 'r' || a[i - 6] == 'R')
+								&& (a[i - 5] == 'a' || a[i - 5] == 'A')
+								&& (a[i - 4] == 'g' || a[i - 4] == 'G')
+								&& (a[i - 3] == 'm' || a[i - 3] == 'M')
+								&& (a[i - 2] == 'e' || a[i - 2] == 'E')
+								&& (a[i - 1] == 'n' || a[i - 1] == 'N')
+								&& (a[i] == 't' || a[i] == 'T')) {
+							matchlen = 8;
+							mask |= ACTION_FRAGMENT;
+						} else {
+							// parse error
+							throw new IllegalArgumentException("invalid permission: " + actions);
+						}
+
+			// make sure we didn't just match the tail of a word
+			// like "ackbarfrequire". Also, skip to the comma.
+			seencomma = false;
+			while (i >= matchlen && !seencomma) {
+				switch (a[i - matchlen]) {
+					case ',' :
+						seencomma = true;
+						/* FALLTHROUGH */
+					case ' ' :
+					case '\r' :
+					case '\n' :
+					case '\f' :
+					case '\t' :
+						break;
+					default :
+						throw new IllegalArgumentException("invalid permission: " + actions);
+				}
+				i--;
+			}
+
+			// point i at the location of the comma minus one (or -1).
+			i -= matchlen;
+		}
+
+		if (seencomma) {
+			throw new IllegalArgumentException("invalid permission: " + actions);
+		}
+
+		return mask;
+	}
+
+	/**
+	 * Determines if the specified permission is implied by this object.
+	 * 
+	 * <p>
+	 * This method checks that the symbolic name of the target is implied by the
+	 * symbolic name of this object. The list of {@code BundlePermission}
+	 * actions must either match or allow for the list of the target object to
+	 * imply the target {@code BundlePermission} action.
+	 * <p>
+	 * The permission to provide a bundle implies the permission to require the
+	 * named symbolic name.
+	 * 
+	 * <pre>
+	 *       x.y.*,"provide" -> x.y.z,"provide" is true
+	 *       *,"require" -> x.y, "require"      is true
+	 *       *,"provide" -> x.y, "require"      is true
+	 *       x.y,"provide" -> x.y.z, "provide"  is false
+	 * </pre>
+	 * 
+	 * @param p The requested permission.
+	 * @return {@code true} if the specified {@code BundlePermission} action is
+	 *         implied by this object; {@code false} otherwise.
+	 */
+	public boolean implies(Permission p) {
+		if (!(p instanceof BundlePermission)) {
+			return false;
+		}
+		BundlePermission requested = (BundlePermission) p;
+
+		final int effective = getActionsMask();
+		final int desired = requested.getActionsMask();
+		return ((effective & desired) == desired) && super.implies(requested);
+	}
+
+	/**
+	 * Returns the canonical string representation of the
+	 * {@code BundlePermission} actions.
+	 * 
+	 * <p>
+	 * Always returns present {@code BundlePermission} actions in the following
+	 * order: {@code provide}, {@code require}, {@code host}, {@code fragment}.
+	 * 
+	 * @return Canonical string representation of the {@code BundlePermission
+	 *         } actions.
+	 */
+	public String getActions() {
+		String result = actions;
+		if (result == null) {
+			StringBuffer sb = new StringBuffer();
+			boolean comma = false;
+
+			if ((action_mask & ACTION_PROVIDE) == ACTION_PROVIDE) {
+				sb.append(PROVIDE);
+				comma = true;
+			}
+
+			if ((action_mask & ACTION_REQUIRE) == ACTION_REQUIRE) {
+				if (comma)
+					sb.append(',');
+				sb.append(REQUIRE);
+				comma = true;
+			}
+
+			if ((action_mask & ACTION_HOST) == ACTION_HOST) {
+				if (comma)
+					sb.append(',');
+				sb.append(HOST);
+				comma = true;
+			}
+
+			if ((action_mask & ACTION_FRAGMENT) == ACTION_FRAGMENT) {
+				if (comma)
+					sb.append(',');
+				sb.append(FRAGMENT);
+			}
+
+			actions = result = sb.toString();
+		}
+		return result;
+	}
+
+	/**
+	 * Returns a new {@code PermissionCollection} object suitable for storing
+	 * {@code BundlePermission} objects.
+	 * 
+	 * @return A new {@code PermissionCollection} object.
+	 */
+	public PermissionCollection newPermissionCollection() {
+		return new BundlePermissionCollection();
+	}
+
+	/**
+	 * Determines the equality of two {@code BundlePermission} objects.
+	 * 
+	 * This method checks that specified bundle has the same bundle symbolic
+	 * name and {@code BundlePermission} actions as this
+	 * {@code BundlePermission} object.
+	 * 
+	 * @param obj The object to test for equality with this
+	 *        {@code BundlePermission} object.
+	 * @return {@code true} if {@code obj} is a {@code BundlePermission}, and
+	 *         has the same bundle symbolic name and actions as this
+	 *         {@code BundlePermission} object; {@code false} otherwise.
+	 */
+	public boolean equals(Object obj) {
+		if (obj == this) {
+			return true;
+		}
+
+		if (!(obj instanceof BundlePermission)) {
+			return false;
+		}
+
+		BundlePermission bp = (BundlePermission) obj;
+
+		return (getActionsMask() == bp.getActionsMask()) && getName().equals(bp.getName());
+	}
+
+	/**
+	 * Returns the hash code value for this object.
+	 * 
+	 * @return A hash code value for this object.
+	 */
+	public int hashCode() {
+		int h = 31 * 17 + getName().hashCode();
+		h = 31 * h + getActions().hashCode();
+		return h;
+	}
+
+	/**
+	 * WriteObject is called to save the state of the {@code BundlePermission}
+	 * object to a stream. The actions are serialized, and the superclass takes
+	 * care of the name.
+	 */
+	private synchronized void writeObject(java.io.ObjectOutputStream s) throws IOException {
+		// Write out the actions. The superclass takes care of the name
+		// call getActions to make sure actions field is initialized
+		if (actions == null)
+			getActions();
+		s.defaultWriteObject();
+	}
+
+	/**
+	 * readObject is called to restore the state of the BundlePermission from a
+	 * stream.
+	 */
+	private synchronized void readObject(java.io.ObjectInputStream s) throws IOException, ClassNotFoundException {
+		// Read in the action, then initialize the rest
+		s.defaultReadObject();
+		setTransients(parseActions(actions));
+	}
+}
+
+/**
+ * Stores a set of {@code BundlePermission} permissions.
+ * 
+ * @see java.security.Permission
+ * @see java.security.Permissions
+ * @see java.security.PermissionCollection
+ */
+
+final class BundlePermissionCollection extends PermissionCollection {
+	private static final long						serialVersionUID	= 3258407326846433079L;
+
+	/**
+	 * Table of permissions.
+	 * 
+	 * @GuardedBy this
+	 */
+	private transient Map<String, BundlePermission>	permissions;
+
+	/**
+	 * Boolean saying if "*" is in the collection.
+	 * 
+	 * @serial
+	 * @GuardedBy this
+	 */
+	private boolean									all_allowed;
+
+	/**
+	 * Create an empty BundlePermissions object.
+	 * 
+	 */
+	public BundlePermissionCollection() {
+		permissions = new HashMap<String, BundlePermission>();
+		all_allowed = false;
+	}
+
+	/**
+	 * Add a permission to this permission collection.
+	 * 
+	 * @param permission The {@code BundlePermission} object to add.
+	 * @throws IllegalArgumentException If the permission is not a
+	 *         {@code BundlePermission} instance.
+	 * @throws SecurityException If this {@code BundlePermissionCollection}
+	 *         object has been marked read-only.
+	 */
+	public void add(final Permission permission) {
+		if (!(permission instanceof BundlePermission)) {
+			throw new IllegalArgumentException("invalid permission: " + permission);
+		}
+		if (isReadOnly()) {
+			throw new SecurityException("attempt to add a Permission to a " + "readonly PermissionCollection");
+		}
+		final BundlePermission bp = (BundlePermission) permission;
+		final String name = bp.getName();
+		synchronized (this) {
+			Map<String, BundlePermission> pc = permissions;
+			BundlePermission existing = pc.get(name);
+			if (existing != null) {
+				final int oldMask = existing.getActionsMask();
+				final int newMask = bp.getActionsMask();
+				if (oldMask != newMask) {
+					pc.put(name, new BundlePermission(name, oldMask | newMask));
+
+				}
+			} else {
+				pc.put(name, bp);
+			}
+
+			if (!all_allowed) {
+				if (name.equals("*"))
+					all_allowed = true;
+			}
+		}
+	}
+
+	/**
+	 * Determines if the specified permissions implies the permissions expressed
+	 * in {@code permission}.
+	 * 
+	 * @param permission The Permission object to compare with this
+	 *        {@code BundlePermission} object.
+	 * @return {@code true} if {@code permission} is a proper subset of a
+	 *         permission in the set; {@code false} otherwise.
+	 */
+	public boolean implies(final Permission permission) {
+		if (!(permission instanceof BundlePermission)) {
+			return false;
+		}
+		BundlePermission requested = (BundlePermission) permission;
+		String requestedName = requested.getName();
+		final int desired = requested.getActionsMask();
+		int effective = BundlePermission.ACTION_NONE;
+		BundlePermission bp;
+
+		synchronized (this) {
+			Map<String, BundlePermission> pc = permissions;
+			/* short circuit if the "*" Permission was added */
+			if (all_allowed) {
+				bp = pc.get("*");
+				if (bp != null) {
+					effective |= bp.getActionsMask();
+					if ((effective & desired) == desired) {
+						return true;
+					}
+				}
+			}
+			bp = pc.get(requestedName);
+			// strategy:
+			// Check for full match first. Then work our way up the
+			// name looking for matches on a.b.*
+			if (bp != null) {
+				// we have a direct hit!
+				effective |= bp.getActionsMask();
+				if ((effective & desired) == desired) {
+					return true;
+				}
+			}
+			// work our way up the tree...
+			int last;
+			int offset = requestedName.length() - 1;
+			while ((last = requestedName.lastIndexOf(".", offset)) != -1) {
+				requestedName = requestedName.substring(0, last + 1) + "*";
+				bp = pc.get(requestedName);
+				if (bp != null) {
+					effective |= bp.getActionsMask();
+					if ((effective & desired) == desired) {
+						return true;
+					}
+				}
+				offset = last - 1;
+			}
+			// we don't have to check for "*" as it was already checked
+			// at the top (all_allowed), so we just return false
+			return false;
+		}
+	}
+
+	/**
+	 * Returns an enumeration of all {@code BundlePermission} objects in the
+	 * container.
+	 * 
+	 * @return Enumeration of all {@code BundlePermission} objects.
+	 */
+	public synchronized Enumeration<Permission> elements() {
+		List<Permission> all = new ArrayList<Permission>(permissions.values());
+		return Collections.enumeration(all);
+	}
+
+	/* serialization logic */
+	private static final ObjectStreamField[]	serialPersistentFields	= {new ObjectStreamField("permissions", Hashtable.class), new ObjectStreamField("all_allowed", Boolean.TYPE)};
+
+	private synchronized void writeObject(ObjectOutputStream out) throws IOException {
+		Hashtable<String, BundlePermission> hashtable = new Hashtable<String, BundlePermission>(permissions);
+		ObjectOutputStream.PutField pfields = out.putFields();
+		pfields.put("permissions", hashtable);
+		pfields.put("all_allowed", all_allowed);
+		out.writeFields();
+	}
+
+	private synchronized void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
+		ObjectInputStream.GetField gfields = in.readFields();
+		Hashtable<String, BundlePermission> hashtable = (Hashtable<String, BundlePermission>) gfields.get("permissions", null);
+		permissions = new HashMap<String, BundlePermission>(hashtable);
+		all_allowed = gfields.get("all_allowed", false);
+	}
+}
diff --git a/src/main/java/org/osgi/framework/BundleReference.java b/src/main/java/org/osgi/framework/BundleReference.java
new file mode 100644
index 0000000..97340f7
--- /dev/null
+++ b/src/main/java/org/osgi/framework/BundleReference.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) OSGi Alliance (2009, 2010). All Rights Reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.framework;
+
+/**
+ * A reference to a Bundle.
+ * 
+ * @since 1.5
+ * @ThreadSafe
+ * @noimplement
+ * @version $Id: e61bd3e020264b04022a430fe09a85ee3aabf1a3 $
+ */
+public interface BundleReference {
+	/**
+	 * Returns the {@code Bundle} object associated with this
+	 * {@code BundleReference}.
+	 * 
+	 * @return The {@code Bundle} object associated with this
+	 *         {@code BundleReference}.
+	 */
+	public Bundle getBundle();
+}
diff --git a/src/main/java/org/osgi/framework/CapabilityPermission.java b/src/main/java/org/osgi/framework/CapabilityPermission.java
new file mode 100644
index 0000000..c27e1ac
--- /dev/null
+++ b/src/main/java/org/osgi/framework/CapabilityPermission.java
@@ -0,0 +1,775 @@
+/*
+ * Copyright (c) OSGi Alliance (2000, 2012). All Rights Reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.framework;
+
+import java.io.IOException;
+import java.io.NotSerializableException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.ObjectStreamField;
+import java.security.AccessController;
+import java.security.BasicPermission;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.PrivilegedAction;
+import java.util.AbstractMap;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * A bundle's authority to provide or require a capability.
+ * <ul>
+ * <li>The {@code provide} action allows a bundle to provide a capability
+ * matching the specified filter.
+ * <li>The {@code require} action allows a bundle to require a capability
+ * matching the specified filter.
+ * </ul>
+ * 
+ * @ThreadSafe
+ * @version $Id: b17bcaec959f67c3eae4c4c80c39a0a8716b22dc $
+ * @since 1.6
+ */
+
+public final class CapabilityPermission extends BasicPermission {
+	static final long								serialVersionUID	= -7662148639076511574L;
+	/**
+	 * The action string {@code require}.
+	 */
+	public final static String						REQUIRE				= "require";
+	/**
+	 * The action string {@code provide}.
+	 */
+	public final static String						PROVIDE				= "provide";
+
+	private final static int						ACTION_REQUIRE		= 0x00000001;
+	private final static int						ACTION_PROVIDE		= 0x00000002;
+	private final static int						ACTION_ALL			= ACTION_REQUIRE | ACTION_PROVIDE;
+	final static int								ACTION_NONE			= 0;
+
+	/**
+	 * The actions mask.
+	 */
+	transient int									action_mask;
+
+	/**
+	 * The actions in canonical form.
+	 * 
+	 * @serial
+	 */
+	private volatile String							actions				= null;
+
+	/**
+	 * The attributes of the requested capability. Must be null if not
+	 * constructed with attributes.
+	 */
+	transient final Map<String, Object>				attributes;
+
+	/**
+	 * The bundle of the requested capability. Must be null if not constructed
+	 * with bundle.
+	 */
+	transient final Bundle							bundle;
+
+	/**
+	 * If this CapabilityPermission was constructed with a filter, this holds a
+	 * Filter matching object used to evaluate the filter in implies.
+	 */
+	transient Filter								filter;
+
+	/**
+	 * This map holds the properties of the permission, used to match a filter
+	 * in implies. This is not initialized until necessary, and then cached in
+	 * this object.
+	 */
+	private transient volatile Map<String, Object>	properties;
+
+	/**
+	 * Create a new CapabilityPermission.
+	 * 
+	 * <p>
+	 * The name is specified as a dot-separated string. Wildcards may be used.
+	 * 
+	 * <pre>
+	 * name ::= <namespace> | <namespace ending in ".*"> | *
+	 * </pre>
+	 * 
+	 * Examples:
+	 * 
+	 * <pre>
+	 * com.acme.capability.*
+	 * org.foo.capability
+	 * *
+	 * </pre>
+	 * 
+	 * For the {@code require} action, the name can also be a filter expression.
+	 * The filter gives access to the capability attributes as well as the
+	 * following attributes:
+	 * <ul>
+	 * <li>signer - A Distinguished Name chain used to sign the bundle providing
+	 * the capability. Wildcards in a DN are not matched according to the filter
+	 * string rules, but according to the rules defined for a DN chain.</li>
+	 * <li>location - The location of the bundle providing the capability.</li>
+	 * <li>id - The bundle ID of the bundle providing the capability.</li>
+	 * <li>name - The symbolic name of the bundle providing the capability.</li>
+	 * <li>capability.namespace - The namespace of the required capability.</li>
+	 * </ul>
+	 * Since the above attribute names may conflict with attribute names of a
+	 * capability, you can prefix an attribute name with '@' in the filter
+	 * expression to match against the capability attributes and not one of the
+	 * above attributes. Filter attribute names are processed in a case
+	 * sensitive manner.
+	 * 
+	 * <p>
+	 * There are two possible actions: {@code require} and {@code provide}. The
+	 * {@code require} permission allows the owner of this permission to require
+	 * a capability matching the attributes. The {@code provide} permission
+	 * allows the bundle to provide a capability in the specified capability
+	 * namespace.
+	 * 
+	 * @param name The capability namespace or a filter over the attributes.
+	 * @param actions {@code require},{@code provide} (canonical order)
+	 * @throws IllegalArgumentException If the specified name is a filter
+	 *         expression and either the specified action is not {@code require}
+	 *         or the filter has an invalid syntax.
+	 */
+	public CapabilityPermission(String name, String actions) {
+		this(name, parseActions(actions));
+		if ((this.filter != null) && ((action_mask & ACTION_ALL) != ACTION_REQUIRE)) {
+			throw new IllegalArgumentException("invalid action string for filter expression");
+		}
+	}
+
+	/**
+	 * Creates a new requested {@code CapabilityPermission} object to be used by
+	 * code that must perform {@code checkPermission} for the {@code require}
+	 * action. {@code CapabilityPermission} objects created with this
+	 * constructor cannot be added to a {@code CapabilityPermission} permission
+	 * collection.
+	 * 
+	 * @param namespace The requested capability namespace.
+	 * @param attributes The requested capability attributes.
+	 * @param providingBundle The bundle providing the requested capability.
+	 * @param actions The action {@code require}.
+	 * @throws IllegalArgumentException If the specified action is not
+	 *         {@code require} or attributes or providingBundle are {@code null}
+	 *         .
+	 */
+	public CapabilityPermission(String namespace, Map<String, ?> attributes, Bundle providingBundle, String actions) {
+		super(namespace);
+		setTransients(namespace, parseActions(actions));
+		if (attributes == null) {
+			throw new IllegalArgumentException("attributes must not be null");
+		}
+		if (providingBundle == null) {
+			throw new IllegalArgumentException("bundle must not be null");
+		}
+		this.attributes = new HashMap<String, Object>(attributes);
+		this.bundle = providingBundle;
+		if ((action_mask & ACTION_ALL) != ACTION_REQUIRE) {
+			throw new IllegalArgumentException("invalid action string");
+		}
+	}
+
+	/**
+	 * Package private constructor used by CapabilityPermissionCollection.
+	 * 
+	 * @param name class name
+	 * @param mask action mask
+	 */
+	CapabilityPermission(String name, int mask) {
+		super(name);
+		setTransients(name, mask);
+		this.attributes = null;
+		this.bundle = null;
+	}
+
+	/**
+	 * Called by constructors and when deserialized.
+	 * 
+	 * @param mask action mask
+	 */
+	private void setTransients(String name, int mask) {
+		if ((mask == ACTION_NONE) || ((mask & ACTION_ALL) != mask)) {
+			throw new IllegalArgumentException("invalid action string");
+		}
+		action_mask = mask;
+		filter = parseFilter(name);
+	}
+
+	/**
+	 * Parse action string into action mask.
+	 * 
+	 * @param actions Action string.
+	 * @return action mask.
+	 */
+	private static int parseActions(String actions) {
+		boolean seencomma = false;
+
+		int mask = ACTION_NONE;
+
+		if (actions == null) {
+			return mask;
+		}
+
+		char[] a = actions.toCharArray();
+
+		int i = a.length - 1;
+		if (i < 0)
+			return mask;
+
+		while (i != -1) {
+			char c;
+
+			// skip whitespace
+			while ((i != -1) && ((c = a[i]) == ' ' || c == '\r' || c == '\n' || c == '\f' || c == '\t'))
+				i--;
+
+			// check for the known strings
+			int matchlen;
+
+			if (i >= 6 && (a[i - 6] == 'r' || a[i - 6] == 'R')
+					&& (a[i - 5] == 'e' || a[i - 5] == 'E')
+					&& (a[i - 4] == 'q' || a[i - 4] == 'Q')
+					&& (a[i - 3] == 'u' || a[i - 3] == 'U')
+					&& (a[i - 2] == 'i' || a[i - 2] == 'I')
+					&& (a[i - 1] == 'r' || a[i - 1] == 'R')
+					&& (a[i] == 'e' || a[i] == 'E')) {
+				matchlen = 7;
+				mask |= ACTION_REQUIRE;
+			} else
+				if (i >= 6 && (a[i - 6] == 'p' || a[i - 6] == 'P')
+						&& (a[i - 5] == 'r' || a[i - 5] == 'R')
+						&& (a[i - 4] == 'o' || a[i - 4] == 'O')
+						&& (a[i - 3] == 'v' || a[i - 3] == 'V')
+						&& (a[i - 2] == 'i' || a[i - 2] == 'I')
+						&& (a[i - 1] == 'd' || a[i - 1] == 'D')
+						&& (a[i] == 'e' || a[i] == 'E')) {
+					matchlen = 7;
+					mask |= ACTION_PROVIDE;
+				} else {
+					// parse error
+					throw new IllegalArgumentException("invalid permission: " + actions);
+				}
+
+			// make sure we didn't just match the tail of a word
+			// like "ackbarfprovide". Also, skip to the comma.
+			seencomma = false;
+			while (i >= matchlen && !seencomma) {
+				switch (a[i - matchlen]) {
+					case ',' :
+						seencomma = true;
+						/* FALLTHROUGH */
+					case ' ' :
+					case '\r' :
+					case '\n' :
+					case '\f' :
+					case '\t' :
+						break;
+					default :
+						throw new IllegalArgumentException("invalid permission: " + actions);
+				}
+				i--;
+			}
+
+			// point i at the location of the comma minus one (or -1).
+			i -= matchlen;
+		}
+
+		if (seencomma) {
+			throw new IllegalArgumentException("invalid permission: " + actions);
+		}
+
+		return mask;
+	}
+
+	/**
+	 * Parse filter string into a Filter object.
+	 * 
+	 * @param filterString The filter string to parse.
+	 * @return a Filter for this bundle. If the specified filterString is not a
+	 *         filter expression, then {@code null} is returned.
+	 * @throws IllegalArgumentException If the filter syntax is invalid.
+	 */
+	private static Filter parseFilter(String filterString) {
+		filterString = filterString.trim();
+		if (filterString.charAt(0) != '(') {
+			return null;
+		}
+
+		try {
+			return FrameworkUtil.createFilter(filterString);
+		} catch (InvalidSyntaxException e) {
+			IllegalArgumentException iae = new IllegalArgumentException("invalid filter");
+			iae.initCause(e);
+			throw iae;
+		}
+	}
+
+	/**
+	 * Determines if a {@code CapabilityPermission} object "implies" the
+	 * specified permission.
+	 * 
+	 * @param p The target permission to check.
+	 * @return {@code true} if the specified permission is implied by this
+	 *         object; {@code false} otherwise.
+	 */
+	public boolean implies(Permission p) {
+		if (!(p instanceof CapabilityPermission)) {
+			return false;
+		}
+		CapabilityPermission requested = (CapabilityPermission) p;
+		if (bundle != null) {
+			return false;
+		}
+		// if requested permission has a filter, then it is an invalid argument
+		if (requested.filter != null) {
+			return false;
+		}
+		return implies0(requested, ACTION_NONE);
+	}
+
+	/**
+	 * Internal implies method. Used by the implies and the permission
+	 * collection implies methods.
+	 * 
+	 * @param requested The requested CapabilityPermission which has already be
+	 *        validated as a proper argument. The requested CapabilityPermission
+	 *        must not have a filter expression.
+	 * @param effective The effective actions with which to start.
+	 * @return {@code true} if the specified permission is implied by this
+	 *         object; {@code false} otherwise.
+	 */
+	boolean implies0(CapabilityPermission requested, int effective) {
+		/* check actions first - much faster */
+		effective |= action_mask;
+		final int desired = requested.action_mask;
+		if ((effective & desired) != desired) {
+			return false;
+		}
+		/* Get filter if any */
+		Filter f = filter;
+		if (f == null) {
+			return super.implies(requested);
+		}
+		return f.matches(requested.getProperties());
+	}
+
+	/**
+	 * Returns the canonical string representation of the actions. Always
+	 * returns present actions in the following order: {@code require},
+	 * {@code provide}.
+	 * 
+	 * @return The canonical string representation of the actions.
+	 */
+	public String getActions() {
+		String result = actions;
+		if (result == null) {
+			StringBuffer sb = new StringBuffer();
+			boolean comma = false;
+
+			int mask = action_mask;
+			if ((mask & ACTION_REQUIRE) == ACTION_REQUIRE) {
+				sb.append(REQUIRE);
+				comma = true;
+			}
+
+			if ((mask & ACTION_PROVIDE) == ACTION_PROVIDE) {
+				if (comma)
+					sb.append(',');
+				sb.append(PROVIDE);
+			}
+
+			actions = result = sb.toString();
+		}
+
+		return result;
+	}
+
+	/**
+	 * Returns a new {@code PermissionCollection} object for storing
+	 * {@code CapabilityPermission} objects.
+	 * 
+	 * @return A new {@code PermissionCollection} object suitable for storing
+	 *         {@code CapabilityPermission} objects.
+	 */
+	public PermissionCollection newPermissionCollection() {
+		return new CapabilityPermissionCollection();
+	}
+
+	/**
+	 * Determines the equality of two CapabilityPermission objects.
+	 * 
+	 * Checks that specified object has the same name and action as this
+	 * {@code CapabilityPermission}.
+	 * 
+	 * @param obj The object to test for equality.
+	 * @return true if obj is a {@code CapabilityPermission}, and has the same
+	 *         name and actions as this {@code CapabilityPermission} object;
+	 *         {@code false} otherwise.
+	 */
+	public boolean equals(Object obj) {
+		if (obj == this) {
+			return true;
+		}
+
+		if (!(obj instanceof CapabilityPermission)) {
+			return false;
+		}
+
+		CapabilityPermission cp = (CapabilityPermission) obj;
+
+		return (action_mask == cp.action_mask) && getName().equals(cp.getName()) && ((attributes == cp.attributes) || ((attributes != null) && (attributes.equals(cp.attributes))))
+				&& ((bundle == cp.bundle) || ((bundle != null) && bundle.equals(cp.bundle)));
+	}
+
+	/**
+	 * Returns the hash code value for this object.
+	 * 
+	 * @return Hash code value for this object.
+	 */
+	public int hashCode() {
+		int h = 31 * 17 + getName().hashCode();
+		h = 31 * h + getActions().hashCode();
+		if (attributes != null) {
+			h = 31 * h + attributes.hashCode();
+		}
+		if (bundle != null) {
+			h = 31 * h + bundle.hashCode();
+		}
+		return h;
+	}
+
+	/**
+	 * WriteObject is called to save the state of this permission to a stream.
+	 * The actions are serialized, and the superclass takes care of the name.
+	 */
+	private synchronized void writeObject(java.io.ObjectOutputStream s) throws IOException {
+		if (bundle != null) {
+			throw new NotSerializableException("cannot serialize");
+		}
+		// Write out the actions. The superclass takes care of the name
+		// call getActions to make sure actions field is initialized
+		if (actions == null)
+			getActions();
+		s.defaultWriteObject();
+	}
+
+	/**
+	 * readObject is called to restore the state of this permission from a
+	 * stream.
+	 */
+	private synchronized void readObject(java.io.ObjectInputStream s) throws IOException, ClassNotFoundException {
+		// Read in the action, then initialize the rest
+		s.defaultReadObject();
+		setTransients(getName(), parseActions(actions));
+	}
+
+	/**
+	 * Called by {@code <@link CapabilityPermission#implies(Permission)>}. This
+	 * method is only called on a requested permission which cannot have a
+	 * filter set.
+	 * 
+	 * @return a map of properties for this permission.
+	 */
+	private Map<String, Object> getProperties() {
+		Map<String, Object> result = properties;
+		if (result != null) {
+			return result;
+		}
+		final Map<String, Object> props = new HashMap<String, Object>(5);
+		props.put("capability.namespace", getName());
+		if (bundle == null) {
+			return properties = props;
+		}
+		AccessController.doPrivileged(new PrivilegedAction<Object>() {
+			public Object run() {
+				props.put("id", new Long(bundle.getBundleId()));
+				props.put("location", bundle.getLocation());
+				String name = bundle.getSymbolicName();
+				if (name != null) {
+					props.put("name", name);
+				}
+				SignerProperty signer = new SignerProperty(bundle);
+				if (signer.isBundleSigned()) {
+					props.put("signer", signer);
+				}
+				return null;
+			}
+		});
+		return properties = new Properties(props, attributes);
+	}
+
+	static private final class Properties extends AbstractMap<String, Object> {
+		private final Map<String, Object>							properties;
+		private final Map<String, Object>							attributes;
+		private transient volatile Set<Map.Entry<String, Object>>	entries;
+
+		Properties(Map<String, Object> properties, Map<String, Object> attributes) {
+			this.properties = properties;
+			this.attributes = attributes;
+			entries = null;
+		}
+
+		public Object get(Object k) {
+			if (!(k instanceof String)) {
+				return null;
+			}
+			String key = (String) k;
+			if (key.charAt(0) == '@') {
+				return attributes.get(key.substring(1));
+			}
+			Object value = properties.get(key);
+			if (value != null) { // fall back to service properties
+				return value;
+			}
+			return attributes.get(key);
+		}
+
+		public Set<Map.Entry<String, Object>> entrySet() {
+			if (entries != null) {
+				return entries;
+			}
+			Set<Map.Entry<String, Object>> all = new HashSet<Map.Entry<String, Object>>(attributes.size() + properties.size());
+			all.addAll(attributes.entrySet());
+			all.addAll(properties.entrySet());
+			return entries = Collections.unmodifiableSet(all);
+		}
+	}
+}
+
+/**
+ * Stores a set of CapabilityPermission permissions.
+ * 
+ * @see java.security.Permission
+ * @see java.security.Permissions
+ * @see java.security.PermissionCollection
+ */
+final class CapabilityPermissionCollection extends PermissionCollection {
+	static final long							serialVersionUID	= -615322242639008920L;
+
+	/**
+	 * Table of permissions.
+	 * 
+	 * @serial
+	 * @GuardedBy this
+	 */
+	private Map<String, CapabilityPermission>	permissions;
+
+	/**
+	 * Boolean saying if "*" is in the collection.
+	 * 
+	 * @serial
+	 * @GuardedBy this
+	 */
+	private boolean								all_allowed;
+
+	/**
+	 * Table of permissions with filter expressions.
+	 * 
+	 * @serial
+	 * @GuardedBy this
+	 */
+	private Map<String, CapabilityPermission>	filterPermissions;
+
+	/**
+	 * Creates an empty CapabilityPermissionCollection object.
+	 */
+	public CapabilityPermissionCollection() {
+		permissions = new HashMap<String, CapabilityPermission>();
+		all_allowed = false;
+	}
+
+	/**
+	 * Adds a permission to this permission collection.
+	 * 
+	 * @param permission The Permission object to add.
+	 * @throws IllegalArgumentException If the specified permission is not a
+	 *         CapabilityPermission object.
+	 * @throws SecurityException If this {@code CapabilityPermissionCollection}
+	 *         object has been marked read-only.
+	 */
+	public void add(final Permission permission) {
+		if (!(permission instanceof CapabilityPermission)) {
+			throw new IllegalArgumentException("invalid permission: " + permission);
+		}
+		if (isReadOnly()) {
+			throw new SecurityException("attempt to add a Permission to a " + "readonly PermissionCollection");
+		}
+
+		final CapabilityPermission cp = (CapabilityPermission) permission;
+		if (cp.bundle != null) {
+			throw new IllegalArgumentException("cannot add to collection: " + cp);
+		}
+
+		final String name = cp.getName();
+		final Filter f = cp.filter;
+		synchronized (this) {
+			/* select the bucket for the permission */
+			Map<String, CapabilityPermission> pc;
+			if (f != null) {
+				pc = filterPermissions;
+				if (pc == null) {
+					filterPermissions = pc = new HashMap<String, CapabilityPermission>();
+				}
+			} else {
+				pc = permissions;
+			}
+			final CapabilityPermission existing = pc.get(name);
+
+			if (existing != null) {
+				final int oldMask = existing.action_mask;
+				final int newMask = cp.action_mask;
+				if (oldMask != newMask) {
+					pc.put(name, new CapabilityPermission(name, oldMask | newMask));
+				}
+			} else {
+				pc.put(name, cp);
+			}
+
+			if (!all_allowed) {
+				if (name.equals("*")) {
+					all_allowed = true;
+				}
+			}
+		}
+	}
+
+	/**
+	 * Determines if a set of permissions implies the permissions expressed in
+	 * {@code permission}.
+	 * 
+	 * @param permission The Permission object to compare.
+	 * @return {@code true} if {@code permission} is a proper subset of a
+	 *         permission in the set; {@code false} otherwise.
+	 */
+	public boolean implies(final Permission permission) {
+		if (!(permission instanceof CapabilityPermission)) {
+			return false;
+		}
+		final CapabilityPermission requested = (CapabilityPermission) permission;
+		/* if requested permission has a filter, then it is an invalid argument */
+		if (requested.filter != null) {
+			return false;
+		}
+
+		String requestedName = requested.getName();
+		final int desired = requested.action_mask;
+		int effective = CapabilityPermission.ACTION_NONE;
+
+		Collection<CapabilityPermission> perms;
+		synchronized (this) {
+			Map<String, CapabilityPermission> pc = permissions;
+			CapabilityPermission cp;
+			/* short circuit if the "*" Permission was added */
+			if (all_allowed) {
+				cp = pc.get("*");
+				if (cp != null) {
+					effective |= cp.action_mask;
+					if ((effective & desired) == desired) {
+						return true;
+					}
+				}
+			}
+
+			/*
+			 * strategy: Check for full match first. Then work our way up the
+			 * name looking for matches on a.b.*
+			 */
+			cp = pc.get(requestedName);
+			if (cp != null) {
+				/* we have a direct hit! */
+				effective |= cp.action_mask;
+				if ((effective & desired) == desired) {
+					return true;
+				}
+			}
+			/* work our way up the tree... */
+			int last;
+			int offset = requestedName.length() - 1;
+			while ((last = requestedName.lastIndexOf(".", offset)) != -1) {
+				requestedName = requestedName.substring(0, last + 1) + "*";
+				cp = pc.get(requestedName);
+				if (cp != null) {
+					effective |= cp.action_mask;
+					if ((effective & desired) == desired) {
+						return true;
+					}
+				}
+				offset = last - 1;
+			}
+			/*
+			 * we don't have to check for "*" as it was already checked before
+			 * we were called.
+			 */
+			pc = filterPermissions;
+			if (pc == null) {
+				return false;
+			}
+			perms = pc.values();
+		}
+		/* iterate one by one over filteredPermissions */
+		for (CapabilityPermission perm : perms) {
+			if (perm.implies0(requested, effective)) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+	/**
+	 * Returns an enumeration of all the {@code CapabilityPermission} objects in
+	 * the container.
+	 * 
+	 * @return Enumeration of all the CapabilityPermission objects.
+	 */
+	public synchronized Enumeration<Permission> elements() {
+		List<Permission> all = new ArrayList<Permission>(permissions.values());
+		Map<String, CapabilityPermission> pc = filterPermissions;
+		if (pc != null) {
+			all.addAll(pc.values());
+		}
+		return Collections.enumeration(all);
+	}
+
+	/* serialization logic */
+	private static final ObjectStreamField[]	serialPersistentFields	= {new ObjectStreamField("permissions", HashMap.class), new ObjectStreamField("all_allowed", Boolean.TYPE),
+			new ObjectStreamField("filterPermissions", HashMap.class)	};
+
+	private synchronized void writeObject(ObjectOutputStream out) throws IOException {
+		ObjectOutputStream.PutField pfields = out.putFields();
+		pfields.put("permissions", permissions);
+		pfields.put("all_allowed", all_allowed);
+		pfields.put("filterPermissions", filterPermissions);
+		out.writeFields();
+	}
+
+	private synchronized void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
+		ObjectInputStream.GetField gfields = in.readFields();
+		HashMap<String, CapabilityPermission> p = (HashMap<String, CapabilityPermission>) gfields.get("permissions", null);
+		permissions = p;
+		all_allowed = gfields.get("all_allowed", false);
+		HashMap<String, CapabilityPermission> fp = (HashMap<String, CapabilityPermission>) gfields.get("filterPermissions", null);
+		filterPermissions = fp;
+	}
+}
diff --git a/src/main/java/org/osgi/framework/Configurable.java b/src/main/java/org/osgi/framework/Configurable.java
new file mode 100644
index 0000000..5fa08c4
--- /dev/null
+++ b/src/main/java/org/osgi/framework/Configurable.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) OSGi Alliance (2000, 2012). All Rights Reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.framework;
+
+/**
+ * Supports a configuration object.
+ * 
+ * <p>
+ * {@code Configurable} is an interface that should be used by a bundle
+ * developer in support of a configurable service. Bundles that need to
+ * configure a service may test to determine if the service object is an
+ * {@code instanceof Configurable}.
+ * 
+ * @deprecated As of 1.2. Please use Configuration Admin service.
+ * @version $Id: 1018601ae90d2d16ec34136db4b04dca3ccf8e65 $
+ */
+public interface Configurable {
+	/**
+	 * Returns this service's configuration object.
+	 * 
+	 * <p>
+	 * Services implementing {@code Configurable} should take care when
+	 * returning a service configuration object since this object is probably
+	 * sensitive.
+	 * <p>
+	 * If the Java Runtime Environment supports permissions, it is recommended
+	 * that the caller is checked for some appropriate permission before
+	 * returning the configuration object.
+	 * 
+	 * @return The configuration object for this service.
+	 * @throws SecurityException If the caller does not have an appropriate
+	 *         permission and the Java Runtime Environment supports permissions.
+	 * @deprecated As of 1.2. Please use Configuration Admin service.
+	 */
+	public Object getConfigurationObject();
+}
diff --git a/src/main/java/org/osgi/framework/Constants.java b/src/main/java/org/osgi/framework/Constants.java
new file mode 100644
index 0000000..10618a1
--- /dev/null
+++ b/src/main/java/org/osgi/framework/Constants.java
@@ -0,0 +1,1703 @@
+/*
+ * Copyright (c) OSGi Alliance (2000, 2012). All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.framework;
+
+import org.osgi.framework.hooks.bundle.CollisionHook;
+import org.osgi.framework.launch.Framework;
+
+/**
+ * Defines standard names for the OSGi environment system properties, service
+ * properties, and Manifest header attribute keys.
+ * 
+ * <p>
+ * The values associated with these keys are of type {@code String}, unless
+ * otherwise indicated.
+ * 
+ * @since 1.1
+ * @noimplement
+ * @version $Id: 6d07a4c3e29a5cd93b3daf0f9fcdab5472b357f6 $
+ */
+
+public interface Constants {
+	/**
+	 * Location identifier of the OSGi <i>system bundle </i>, which is defined
+	 * to be "System Bundle".
+	 */
+	String	SYSTEM_BUNDLE_LOCATION					= "System Bundle";
+
+	/**
+	 * Alias for the symbolic name of the OSGi <i>system bundle </i>. It is
+	 * defined to be "system.bundle".
+	 * 
+	 * @since 1.3
+	 */
+	String	SYSTEM_BUNDLE_SYMBOLICNAME				= "system.bundle";
+
+	/**
+	 * Manifest header identifying the bundle's category.
+	 * <p>
+	 * The header value may be retrieved from the {@code Dictionary} object
+	 * returned by the {@code Bundle.getHeaders} method.
+	 */
+	String	BUNDLE_CATEGORY							= "Bundle-Category";
+
+	/**
+	 * Manifest header identifying a list of directories and embedded JAR files,
+	 * which are bundle resources used to extend the bundle's classpath.
+	 * 
+	 * <p>
+	 * The header value may be retrieved from the {@code Dictionary} object
+	 * returned by the {@code Bundle.getHeaders} method.
+	 */
+	String	BUNDLE_CLASSPATH						= "Bundle-ClassPath";
+
+	/**
+	 * Manifest header identifying the bundle's copyright information.
+	 * <p>
+	 * The header value may be retrieved from the {@code Dictionary} object
+	 * returned by the {@code Bundle.getHeaders} method.
+	 */
+	String	BUNDLE_COPYRIGHT						= "Bundle-Copyright";
+
+	/**
+	 * Manifest header containing a brief description of the bundle's
+	 * functionality.
+	 * <p>
+	 * The header value may be retrieved from the {@code Dictionary} object
+	 * returned by the {@code Bundle.getHeaders} method.
+	 */
+	String	BUNDLE_DESCRIPTION						= "Bundle-Description";
+
+	/**
+	 * Manifest header identifying the bundle's name.
+	 * <p>
+	 * The header value may be retrieved from the {@code Dictionary} object
+	 * returned by the {@code Bundle.getHeaders} method.
+	 */
+	String	BUNDLE_NAME								= "Bundle-Name";
+
+	/**
+	 * Manifest header identifying a number of hardware environments and the
+	 * native language code libraries that the bundle is carrying for each of
+	 * these environments.
+	 * 
+	 * <p>
+	 * The header value may be retrieved from the {@code Dictionary} object
+	 * returned by the {@code Bundle.getHeaders} method.
+	 */
+	String	BUNDLE_NATIVECODE						= "Bundle-NativeCode";
+
+	/**
+	 * Manifest header identifying the packages that the bundle offers to the
+	 * Framework for export.
+	 * 
+	 * <p>
+	 * The header value may be retrieved from the {@code Dictionary} object
+	 * returned by the {@code Bundle.getHeaders} method.
+	 */
+	String	EXPORT_PACKAGE							= "Export-Package";
+
+	/**
+	 * Manifest header identifying the fully qualified class names of the
+	 * services that the bundle may register (used for informational purposes
+	 * only).
+	 * 
+	 * <p>
+	 * The header value may be retrieved from the {@code Dictionary} object
+	 * returned by the {@code Bundle.getHeaders} method.
+	 * 
+	 * @deprecated As of 1.2.
+	 */
+	String	EXPORT_SERVICE							= "Export-Service";
+
+	/**
+	 * Manifest header identifying the packages on which the bundle depends.
+	 * 
+	 * <p>
+	 * The header value may be retrieved from the {@code Dictionary} object
+	 * returned by the {@code Bundle.getHeaders} method.
+	 */
+	String	IMPORT_PACKAGE							= "Import-Package";
+
+	/**
+	 * Manifest header identifying the packages that the bundle may dynamically
+	 * import during execution.
+	 * 
+	 * <p>
+	 * The header value may be retrieved from the {@code Dictionary} object
+	 * returned by the {@code Bundle.getHeaders} method.
+	 * 
+	 * @since 1.2
+	 */
+	String	DYNAMICIMPORT_PACKAGE					= "DynamicImport-Package";
+
+	/**
+	 * Manifest header identifying the fully qualified class names of the
+	 * services that the bundle requires (used for informational purposes only).
+	 * 
+	 * <p>
+	 * The header value may be retrieved from the {@code Dictionary} object
+	 * returned by the {@code Bundle.getHeaders} method.
+	 * 
+	 * @deprecated As of 1.2.
+	 */
+	String	IMPORT_SERVICE							= "Import-Service";
+
+	/**
+	 * Manifest header identifying the bundle's vendor.
+	 * 
+	 * <p>
+	 * The header value may be retrieved from the {@code Dictionary} object
+	 * returned by the {@code Bundle.getHeaders} method.
+	 */
+	String	BUNDLE_VENDOR							= "Bundle-Vendor";
+
+	/**
+	 * Manifest header identifying the bundle's version.
+	 * 
+	 * <p>
+	 * The header value may be retrieved from the {@code Dictionary} object
+	 * returned by the {@code Bundle.getHeaders} method.
+	 */
+	String	BUNDLE_VERSION							= "Bundle-Version";
+
+	/**
+	 * Manifest header identifying the bundle's documentation URL, from which
+	 * further information about the bundle may be obtained.
+	 * 
+	 * <p>
+	 * The header value may be retrieved from the {@code Dictionary} object
+	 * returned by the {@code Bundle.getHeaders} method.
+	 */
+	String	BUNDLE_DOCURL							= "Bundle-DocURL";
+
+	/**
+	 * Manifest header identifying the contact address where problems with the
+	 * bundle may be reported; for example, an email address.
+	 * 
+	 * <p>
+	 * The header value may be retrieved from the {@code Dictionary} object
+	 * returned by the {@code Bundle.getHeaders} method.
+	 */
+	String	BUNDLE_CONTACTADDRESS					= "Bundle-ContactAddress";
+
+	/**
+	 * Manifest header attribute identifying the bundle's activator class.
+	 * 
+	 * <p>
+	 * If present, this header specifies the name of the bundle resource class
+	 * that implements the {@code BundleActivator} interface and whose
+	 * {@code start} and {@code stop} methods are called by the Framework when
+	 * the bundle is started and stopped, respectively.
+	 * 
+	 * <p>
+	 * The header value may be retrieved from the {@code Dictionary} object
+	 * returned by the {@code Bundle.getHeaders} method.
+	 */
+	String	BUNDLE_ACTIVATOR						= "Bundle-Activator";
+
+	/**
+	 * Manifest header identifying the location from which a new bundle version
+	 * is obtained during a bundle update operation.
+	 * 
+	 * <p>
+	 * The header value may be retrieved from the {@code Dictionary} object
+	 * returned by the {@code Bundle.getHeaders} method.
+	 */
+	String	BUNDLE_UPDATELOCATION					= "Bundle-UpdateLocation";
+
+	/**
+	 * Manifest header attribute identifying the version of a package specified
+	 * in the Export-Package or Import-Package manifest header.
+	 * 
+	 * @deprecated As of 1.3. This has been replaced by
+	 *             {@link #VERSION_ATTRIBUTE}.
+	 */
+	String	PACKAGE_SPECIFICATION_VERSION			= "specification-version";
+
+	/**
+	 * Manifest header attribute identifying the processor required to run
+	 * native bundle code specified in the Bundle-NativeCode manifest header).
+	 * 
+	 * <p>
+	 * The attribute value is encoded in the Bundle-NativeCode manifest header
+	 * like:
+	 * 
+	 * <pre>
+	 *     Bundle-NativeCode: http.so ; processor=x86 ...
+	 * </pre>
+	 * 
+	 * @see #BUNDLE_NATIVECODE
+	 */
+	String	BUNDLE_NATIVECODE_PROCESSOR				= "processor";
+
+	/**
+	 * Manifest header attribute identifying the operating system required to
+	 * run native bundle code specified in the Bundle-NativeCode manifest
+	 * header).
+	 * <p>
+	 * The attribute value is encoded in the Bundle-NativeCode manifest header
+	 * like:
+	 * 
+	 * <pre>
+	 *     Bundle-NativeCode: http.so ; osname=Linux ...
+	 * </pre>
+	 * 
+	 * @see #BUNDLE_NATIVECODE
+	 */
+	String	BUNDLE_NATIVECODE_OSNAME				= "osname";
+
+	/**
+	 * Manifest header attribute identifying the operating system version
+	 * required to run native bundle code specified in the Bundle-NativeCode
+	 * manifest header).
+	 * <p>
+	 * The attribute value is encoded in the Bundle-NativeCode manifest header
+	 * like:
+	 * 
+	 * <pre>
+	 *     Bundle-NativeCode: http.so ; osversion="2.34" ...
+	 * </pre>
+	 * 
+	 * @see #BUNDLE_NATIVECODE
+	 */
+	String	BUNDLE_NATIVECODE_OSVERSION				= "osversion";
+
+	/**
+	 * Manifest header attribute identifying the language in which the native
+	 * bundle code is written specified in the Bundle-NativeCode manifest
+	 * header. See ISO 639 for possible values.
+	 * <p>
+	 * The attribute value is encoded in the Bundle-NativeCode manifest header
+	 * like:
+	 * 
+	 * <pre>
+	 *     Bundle-NativeCode: http.so ; language=nl_be ...
+	 * </pre>
+	 * 
+	 * @see #BUNDLE_NATIVECODE
+	 */
+	String	BUNDLE_NATIVECODE_LANGUAGE				= "language";
+
+	/**
+	 * Manifest header identifying the required execution environment for the
+	 * bundle. The service platform may run this bundle if any of the execution
+	 * environments named in this header matches one of the execution
+	 * environments it implements.
+	 * 
+	 * <p>
+	 * The header value may be retrieved from the {@code Dictionary} object
+	 * returned by the {@code Bundle.getHeaders} method.
+	 * 
+	 * @since 1.2
+	 * @deprecated As of 1.6. Replaced by the {@code osgi.ee} capability.
+	 */
+	String	BUNDLE_REQUIREDEXECUTIONENVIRONMENT		= "Bundle-RequiredExecutionEnvironment";
+
+	/**
+	 * Manifest header identifying the bundle's symbolic name.
+	 * 
+	 * <p>
+	 * The header value may be retrieved from the {@code Dictionary} object
+	 * returned by the {@code Bundle.getHeaders} method.
+	 * 
+	 * @since 1.3
+	 */
+	String	BUNDLE_SYMBOLICNAME						= "Bundle-SymbolicName";
+
+	/**
+	 * Manifest header directive identifying whether a bundle is a singleton.
+	 * The default value is {@code false}.
+	 * 
+	 * <p>
+	 * The directive value is encoded in the Bundle-SymbolicName manifest header
+	 * like:
+	 * 
+	 * <pre>
+	 *     Bundle-SymbolicName: com.acme.module.test; singleton:=true
+	 * </pre>
+	 * 
+	 * @see #BUNDLE_SYMBOLICNAME
+	 * @since 1.3
+	 */
+	String	SINGLETON_DIRECTIVE						= "singleton";
+
+	/**
+	 * Manifest header directive identifying if and when a fragment may attach
+	 * to a host bundle. The default value is
+	 * {@link #FRAGMENT_ATTACHMENT_ALWAYS always}.
+	 * 
+	 * <p>
+	 * The directive value is encoded in the Bundle-SymbolicName manifest header
+	 * like:
+	 * 
+	 * <pre>
+	 *     Bundle-SymbolicName: com.acme.module.test; fragment-attachment:="never"
+	 * </pre>
+	 * 
+	 * @see #BUNDLE_SYMBOLICNAME
+	 * @see #FRAGMENT_ATTACHMENT_ALWAYS
+	 * @see #FRAGMENT_ATTACHMENT_RESOLVETIME
+	 * @see #FRAGMENT_ATTACHMENT_NEVER
+	 * @since 1.3
+	 */
+	String	FRAGMENT_ATTACHMENT_DIRECTIVE			= "fragment-attachment";
+
+	/**
+	 * Manifest header directive value identifying a fragment attachment type of
+	 * always. A fragment attachment type of always indicates that fragments are
+	 * allowed to attach to the host bundle at any time (while the host is
+	 * resolved or during the process of resolving the host bundle).
+	 * 
+	 * <p>
+	 * The directive value is encoded in the Bundle-SymbolicName manifest header
+	 * like:
+	 * 
+	 * <pre>
+	 *     Bundle-SymbolicName: com.acme.module.test; fragment-attachment:="always"
+	 * </pre>
+	 * 
+	 * @see #FRAGMENT_ATTACHMENT_DIRECTIVE
+	 * @since 1.3
+	 */
+	String	FRAGMENT_ATTACHMENT_ALWAYS				= "always";
+
+	/**
+	 * Manifest header directive value identifying a fragment attachment type of
+	 * resolve-time. A fragment attachment type of resolve-time indicates that
+	 * fragments are allowed to attach to the host bundle only during the
+	 * process of resolving the host bundle.
+	 * 
+	 * <p>
+	 * The directive value is encoded in the Bundle-SymbolicName manifest header
+	 * like:
+	 * 
+	 * <pre>
+	 *     Bundle-SymbolicName: com.acme.module.test; fragment-attachment:="resolve-time"
+	 * </pre>
+	 * 
+	 * @see #FRAGMENT_ATTACHMENT_DIRECTIVE
+	 * @since 1.3
+	 */
+	String	FRAGMENT_ATTACHMENT_RESOLVETIME			= "resolve-time";
+
+	/**
+	 * Manifest header directive value identifying a fragment attachment type of
+	 * never. A fragment attachment type of never indicates that no fragments
+	 * are allowed to attach to the host bundle at any time.
+	 * 
+	 * <p>
+	 * The directive value is encoded in the Bundle-SymbolicName manifest header
+	 * like:
+	 * 
+	 * <pre>
+	 *     Bundle-SymbolicName: com.acme.module.test; fragment-attachment:="never"
+	 * </pre>
+	 * 
+	 * @see #FRAGMENT_ATTACHMENT_DIRECTIVE
+	 * @since 1.3
+	 */
+	String	FRAGMENT_ATTACHMENT_NEVER				= "never";
+
+	/**
+	 * Manifest header identifying the base name of the bundle's localization
+	 * entries.
+	 * 
+	 * <p>
+	 * The header value may be retrieved from the {@code Dictionary} object
+	 * returned by the {@code Bundle.getHeaders} method.
+	 * 
+	 * @see #BUNDLE_LOCALIZATION_DEFAULT_BASENAME
+	 * @since 1.3
+	 */
+	String	BUNDLE_LOCALIZATION						= "Bundle-Localization";
+
+	/**
+	 * Default value for the {@code Bundle-Localization} manifest header.
+	 * 
+	 * @see #BUNDLE_LOCALIZATION
+	 * @since 1.3
+	 */
+	String	BUNDLE_LOCALIZATION_DEFAULT_BASENAME	= "OSGI-INF/l10n/bundle";
+
+	/**
+	 * Manifest header identifying the symbolic names of other bundles required
+	 * by the bundle.
+	 * 
+	 * <p>
+	 * The header value may be retrieved from the {@code Dictionary} object
+	 * returned by the {@code Bundle.getHeaders} method.
+	 * 
+	 * @since 1.3
+	 */
+	String	REQUIRE_BUNDLE							= "Require-Bundle";
+
+	/**
+	 * Manifest header attribute identifying a range of versions for a bundle
+	 * specified in the {@code Require-Bundle} or {@code Fragment-Host} manifest
+	 * headers. The default value is {@code 0.0.0}.
+	 * 
+	 * <p>
+	 * The attribute value is encoded in the Require-Bundle manifest header
+	 * like:
+	 * 
+	 * <pre>
+	 *     Require-Bundle: com.acme.module.test; bundle-version="1.1"
+	 *     Require-Bundle: com.acme.module.test; bundle-version="[1.0,2.0)"
+	 * </pre>
+	 * 
+	 * <p>
+	 * The bundle-version attribute value uses a mathematical interval notation
+	 * to specify a range of bundle versions. A bundle-version attribute value
+	 * specified as a single version means a version range that includes any
+	 * bundle version greater than or equal to the specified version.
+	 * 
+	 * @see #REQUIRE_BUNDLE
+	 * @since 1.3
+	 */
+	String	BUNDLE_VERSION_ATTRIBUTE				= "bundle-version";
+
+	/**
+	 * Manifest header identifying the symbolic name of another bundle for which
+	 * that the bundle is a fragment.
+	 * 
+	 * <p>
+	 * The header value may be retrieved from the {@code Dictionary} object
+	 * returned by the {@code Bundle.getHeaders} method.
+	 * 
+	 * @since 1.3
+	 */
+	String	FRAGMENT_HOST							= "Fragment-Host";
+
+	/**
+	 * Manifest header attribute is used for selection by filtering based upon
+	 * system properties.
+	 * 
+	 * <p>
+	 * The attribute value is encoded in manifest headers like:
+	 * 
+	 * <pre>
+	 *     Bundle-NativeCode: libgtk.so; selection-filter="(ws=gtk)"; ...
+	 * </pre>
+	 * 
+	 * @see #BUNDLE_NATIVECODE
+	 * @since 1.3
+	 */
+	String	SELECTION_FILTER_ATTRIBUTE				= "selection-filter";
+
+	/**
+	 * Manifest header identifying the bundle manifest version. A bundle
+	 * manifest may express the version of the syntax in which it is written by
+	 * specifying a bundle manifest version. Bundles exploiting OSGi Release 4,
+	 * or later, syntax must specify a bundle manifest version.
+	 * <p>
+	 * The bundle manifest version defined by OSGi Release 4 or, more
+	 * specifically, by version 1.3 of the OSGi Core Specification is "2".
+	 * 
+	 * <p>
+	 * The header value may be retrieved from the {@code Dictionary} object
+	 * returned by the {@code Bundle.getHeaders} method.
+	 * 
+	 * @since 1.3
+	 */
+	String	BUNDLE_MANIFESTVERSION					= "Bundle-ManifestVersion";
+
+	/**
+	 * Manifest header attribute identifying the version of a package specified
+	 * in the Export-Package or Import-Package manifest header.
+	 * 
+	 * <p>
+	 * The attribute value is encoded in the Export-Package or Import-Package
+	 * manifest header like:
+	 * 
+	 * <pre>
+	 *     Export-Package: org.osgi.framework; version="1.1"
+	 * </pre>
+	 * 
+	 * @see #EXPORT_PACKAGE
+	 * @see #IMPORT_PACKAGE
+	 * @since 1.3
+	 */
+	String	VERSION_ATTRIBUTE						= "version";
+
+	/**
+	 * Manifest header attribute identifying the symbolic name of a bundle that
+	 * exports a package specified in the Import-Package manifest header.
+	 * 
+	 * <p>
+	 * The attribute value is encoded in the Import-Package manifest header
+	 * like:
+	 * 
+	 * <pre>
+	 *     Import-Package: org.osgi.framework; bundle-symbolic-name="com.acme.module.test"
+	 * </pre>
+	 * 
+	 * @see #IMPORT_PACKAGE
+	 * @since 1.3
+	 */
+	String	BUNDLE_SYMBOLICNAME_ATTRIBUTE			= "bundle-symbolic-name";
+
+	/**
+	 * Manifest header directive identifying the resolution type in the
+	 * Import-Package, Require-Bundle or Require-Capability manifest header. The
+	 * default value is {@link #RESOLUTION_MANDATORY mandatory}.
+	 * 
+	 * <p>
+	 * The directive value is encoded in the Import-Package, Require-Bundle or
+	 * Require-Capability manifest header like:
+	 * 
+	 * <pre>
+	 *     Import-Package: org.osgi.framework; resolution:="optional"
+	 *     Require-Bundle: com.acme.module.test; resolution:="optional"
+	 *     Require-Capability: com.acme.capability; resolution:="optional"
+	 * </pre>
+	 * 
+	 * @see #IMPORT_PACKAGE
+	 * @see #REQUIRE_BUNDLE
+	 * @see #REQUIRE_CAPABILITY
+	 * @see #RESOLUTION_MANDATORY
+	 * @see #RESOLUTION_OPTIONAL
+	 * @since 1.3
+	 */
+	String	RESOLUTION_DIRECTIVE					= "resolution";
+
+	/**
+	 * Manifest header directive value identifying a mandatory resolution type.
+	 * A mandatory resolution type indicates that the import package, require
+	 * bundle or require capability must be resolved when the bundle is
+	 * resolved. If such an import, require bundle or require capability cannot
+	 * be resolved, the module fails to resolve.
+	 * 
+	 * <p>
+	 * The directive value is encoded in the Import-Package, Require-Bundle or
+	 * Require-Capability manifest header like:
+	 * 
+	 * <pre>
+	 *     Import-Package: org.osgi.framework; resolution:="mandatory"
+	 *     Require-Bundle: com.acme.module.test; resolution:="mandatory"
+	 *     Require-Capability: com.acme.capability; resolution:="mandatory"
+	 * </pre>
+	 * 
+	 * @see #RESOLUTION_DIRECTIVE
+	 * @since 1.3
+	 */
+	String	RESOLUTION_MANDATORY					= "mandatory";
+
+	/**
+	 * Manifest header directive value identifying an optional resolution type.
+	 * An optional resolution type indicates that the import, require bundle or
+	 * require capability is optional and the bundle may be resolved without the
+	 * import, require bundle or require capability being resolved. If the
+	 * import, require bundle or require capability is not resolved when the
+	 * bundle is resolved, the import, require bundle or require capability may
+	 * not be resolved until the bundle is refreshed.
+	 * 
+	 * <p>
+	 * The directive value is encoded in the Import-Package, Require-Bundle or
+	 * Require-Capability manifest header like:
+	 * 
+	 * <pre>
+	 *     Import-Package: org.osgi.framework; resolution:="optional"
+	 *     Require-Bundle: com.acme.module.test; resolution:="optional"
+	 *     Require-Capability: com.acme.capability; resolution:="optional"
+	 * </pre>
+	 * 
+	 * @see #RESOLUTION_DIRECTIVE
+	 * @since 1.3
+	 */
+	String	RESOLUTION_OPTIONAL						= "optional";
+
+	/**
+	 * Manifest header directive identifying a list of packages that an exported
+	 * package or provided capability uses.
+	 * 
+	 * <p>
+	 * The directive value is encoded in the Export-Package or
+	 * Provide-Capability manifest header like:
+	 * 
+	 * <pre>
+	 *     Export-Package: org.osgi.util.tracker; uses:="org.osgi.framework"
+	 *     Provide-Capability: com.acme.capability; uses:="com.acme.service"
+	 * </pre>
+	 * 
+	 * @see #EXPORT_PACKAGE
+	 * @see #PROVIDE_CAPABILITY
+	 * @since 1.3
+	 */
+	String	USES_DIRECTIVE							= "uses";
+
+	/**
+	 * Manifest header directive identifying a list of classes to include in the
+	 * exported package.
+	 * 
+	 * <p>
+	 * This directive is used by the Export-Package manifest header to identify
+	 * a list of classes of the specified package which must be allowed to be
+	 * exported. The directive value is encoded in the Export-Package manifest
+	 * header like:
+	 * 
+	 * <pre>
+	 *     Export-Package: org.osgi.framework; include:="MyClass*"
+	 * </pre>
+	 * 
+	 * <p>
+	 * This directive is also used by the Bundle-ActivationPolicy manifest
+	 * header to identify the packages from which class loads will trigger lazy
+	 * activation. The directive value is encoded in the Bundle-ActivationPolicy
+	 * manifest header like:
+	 * 
+	 * <pre>
+	 *     Bundle-ActivationPolicy: lazy; include:="org.osgi.framework"
+	 * </pre>
+	 * 
+	 * @see #EXPORT_PACKAGE
+	 * @see #BUNDLE_ACTIVATIONPOLICY
+	 * @since 1.3
+	 */
+	String	INCLUDE_DIRECTIVE						= "include";
+
+	/**
+	 * Manifest header directive identifying a list of classes to exclude in the
+	 * exported package..
+	 * <p>
+	 * This directive is used by the Export-Package manifest header to identify
+	 * a list of classes of the specified package which must not be allowed to
+	 * be exported. The directive value is encoded in the Export-Package
+	 * manifest header like:
+	 * 
+	 * <pre>
+	 *     Export-Package: org.osgi.framework; exclude:="*Impl"
+	 * </pre>
+	 * 
+	 * <p>
+	 * This directive is also used by the Bundle-ActivationPolicy manifest
+	 * header to identify the packages from which class loads will not trigger
+	 * lazy activation. The directive value is encoded in the
+	 * Bundle-ActivationPolicy manifest header like:
+	 * 
+	 * <pre>
+	 *     Bundle-ActivationPolicy: lazy; exclude:="org.osgi.framework"
+	 * </pre>
+	 * 
+	 * @see #EXPORT_PACKAGE
+	 * @see #BUNDLE_ACTIVATIONPOLICY
+	 * @since 1.3
+	 */
+	String	EXCLUDE_DIRECTIVE						= "exclude";
+
+	/**
+	 * Manifest header directive identifying names of matching attributes which
+	 * must be specified by matching Import-Package statements in the
+	 * Export-Package manifest header.
+	 * 
+	 * <p>
+	 * The directive value is encoded in the Export-Package manifest header
+	 * like:
+	 * 
+	 * <pre>
+	 *     Export-Package: org.osgi.framework; mandatory:="bundle-symbolic-name"
+	 * </pre>
+	 * 
+	 * @see #EXPORT_PACKAGE
+	 * @since 1.3
+	 */
+	String	MANDATORY_DIRECTIVE						= "mandatory";
+
+	/**
+	 * Manifest header directive identifying the visibility of a required bundle
+	 * in the Require-Bundle manifest header. The default value is
+	 * {@link #VISIBILITY_PRIVATE private}.
+	 * 
+	 * <p>
+	 * The directive value is encoded in the Require-Bundle manifest header
+	 * like:
+	 * 
+	 * <pre>
+	 *     Require-Bundle: com.acme.module.test; visibility:="reexport"
+	 * </pre>
+	 * 
+	 * @see #REQUIRE_BUNDLE
+	 * @see #VISIBILITY_PRIVATE
+	 * @see #VISIBILITY_REEXPORT
+	 * @since 1.3
+	 */
+	String	VISIBILITY_DIRECTIVE					= "visibility";
+
+	/**
+	 * Manifest header directive value identifying a private visibility type. A
+	 * private visibility type indicates that any packages that are exported by
+	 * the required bundle are not made visible on the export signature of the
+	 * requiring bundle.
+	 * 
+	 * <p>
+	 * The directive value is encoded in the Require-Bundle manifest header
+	 * like:
+	 * 
+	 * <pre>
+	 *     Require-Bundle: com.acme.module.test; visibility:="private"
+	 * </pre>
+	 * 
+	 * @see #VISIBILITY_DIRECTIVE
+	 * @since 1.3
+	 */
+	String	VISIBILITY_PRIVATE						= "private";
+
+	/**
+	 * Manifest header directive value identifying a reexport visibility type. A
+	 * reexport visibility type indicates any packages that are exported by the
+	 * required bundle are re-exported by the requiring bundle. Any arbitrary
+	 * arbitrary matching attributes with which they were exported by the
+	 * required bundle are deleted.
+	 * 
+	 * <p>
+	 * The directive value is encoded in the Require-Bundle manifest header
+	 * like:
+	 * 
+	 * <pre>
+	 *     Require-Bundle: com.acme.module.test; visibility:="reexport"
+	 * </pre>
+	 * 
+	 * @see #VISIBILITY_DIRECTIVE
+	 * @since 1.3
+	 */
+	String	VISIBILITY_REEXPORT						= "reexport";
+
+	/**
+	 * Manifest header directive identifying the type of the extension fragment.
+	 * 
+	 * <p>
+	 * The directive value is encoded in the Fragment-Host manifest header like:
+	 * 
+	 * <pre>
+	 *     Fragment-Host: system.bundle; extension:="framework"
+	 * </pre>
+	 * 
+	 * @see #FRAGMENT_HOST
+	 * @see #EXTENSION_FRAMEWORK
+	 * @see #EXTENSION_BOOTCLASSPATH
+	 * @since 1.3
+	 */
+	String	EXTENSION_DIRECTIVE						= "extension";
+
+	/**
+	 * Manifest header directive value identifying the type of extension
+	 * fragment. An extension fragment type of framework indicates that the
+	 * extension fragment is to be loaded by the framework's class loader.
+	 * 
+	 * <p>
+	 * The directive value is encoded in the Fragment-Host manifest header like:
+	 * 
+	 * <pre>
+	 *     Fragment-Host: system.bundle; extension:="framework"
+	 * </pre>
+	 * 
+	 * @see #EXTENSION_DIRECTIVE
+	 * @since 1.3
+	 */
+	String	EXTENSION_FRAMEWORK						= "framework";
+
+	/**
+	 * Manifest header directive value identifying the type of extension
+	 * fragment. An extension fragment type of bootclasspath indicates that the
+	 * extension fragment is to be loaded by the boot class loader.
+	 * 
+	 * <p>
+	 * The directive value is encoded in the Fragment-Host manifest header like:
+	 * 
+	 * <pre>
+	 *     Fragment-Host: system.bundle; extension:="bootclasspath"
+	 * </pre>
+	 * 
+	 * @see #EXTENSION_DIRECTIVE
+	 * @since 1.3
+	 */
+	String	EXTENSION_BOOTCLASSPATH					= "bootclasspath";
+
+	/**
+	 * Manifest header identifying the bundle's activation policy.
+	 * <p>
+	 * The header value may be retrieved from the {@code Dictionary} object
+	 * returned by the {@code Bundle.getHeaders} method.
+	 * 
+	 * @since 1.4
+	 * @see #ACTIVATION_LAZY
+	 * @see #INCLUDE_DIRECTIVE
+	 * @see #EXCLUDE_DIRECTIVE
+	 */
+	String	BUNDLE_ACTIVATIONPOLICY					= "Bundle-ActivationPolicy";
+
+	/**
+	 * Bundle activation policy declaring the bundle must be activated when the
+	 * first class load is made from the bundle.
+	 * <p>
+	 * A bundle with the lazy activation policy that is started with the
+	 * {@link Bundle#START_ACTIVATION_POLICY START_ACTIVATION_POLICY} option
+	 * will wait in the {@link Bundle#STARTING STARTING} state until the first
+	 * class load from the bundle occurs. The bundle will then be activated
+	 * before the class is returned to the requester.
+	 * <p>
+	 * The activation policy value is specified as in the
+	 * Bundle-ActivationPolicy manifest header like:
+	 * 
+	 * <pre>
+	 *       Bundle-ActivationPolicy: lazy
+	 * </pre>
+	 * 
+	 * @see #BUNDLE_ACTIVATIONPOLICY
+	 * @see Bundle#start(int)
+	 * @see Bundle#START_ACTIVATION_POLICY
+	 * @since 1.4
+	 */
+	String	ACTIVATION_LAZY							= "lazy";
+
+	/**
+	 * Framework environment property identifying the Framework version.
+	 * 
+	 * <p>
+	 * The value of this property may be retrieved by calling the
+	 * {@code BundleContext.getProperty} method.
+	 */
+	String	FRAMEWORK_VERSION						= "org.osgi.framework.version";
+
+	/**
+	 * Framework environment property identifying the Framework implementation
+	 * vendor.
+	 * 
+	 * <p>
+	 * The value of this property may be retrieved by calling the
+	 * {@code BundleContext.getProperty} method.
+	 */
+	String	FRAMEWORK_VENDOR						= "org.osgi.framework.vendor";
+
+	/**
+	 * Framework launching property identifying the Framework implementation
+	 * language (see ISO 639 for possible values).
+	 * 
+	 * <p>
+	 * The value of this property may be retrieved by calling the
+	 * {@code BundleContext.getProperty} method.
+	 */
+	String	FRAMEWORK_LANGUAGE						= "org.osgi.framework.language";
+
+	/**
+	 * Framework launching property identifying the Framework host-computer's
+	 * operating system.
+	 * 
+	 * <p>
+	 * The value of this property may be retrieved by calling the
+	 * {@code BundleContext.getProperty} method.
+	 */
+	String	FRAMEWORK_OS_NAME						= "org.osgi.framework.os.name";
+
+	/**
+	 * Framework launching property identifying the Framework host-computer's
+	 * operating system version number.
+	 * 
+	 * <p>
+	 * The value of this property may be retrieved by calling the
+	 * {@code BundleContext.getProperty} method.
+	 */
+	String	FRAMEWORK_OS_VERSION					= "org.osgi.framework.os.version";
+
+	/**
+	 * Framework launching property identifying the Framework host-computer's
+	 * processor name.
+	 * 
+	 * <p>
+	 * The value of this property may be retrieved by calling the
+	 * {@code BundleContext.getProperty} method.
+	 */
+	String	FRAMEWORK_PROCESSOR						= "org.osgi.framework.processor";
+
+	/**
+	 * Framework launching property identifying execution environments provided
+	 * by the Framework.
+	 * 
+	 * <p>
+	 * The value of this property may be retrieved by calling the
+	 * {@code BundleContext.getProperty} method.
+	 * 
+	 * @since 1.2
+	 * @deprecated As of 1.6. Replaced by the {@code osgi.ee} capability.
+	 */
+	String	FRAMEWORK_EXECUTIONENVIRONMENT			= "org.osgi.framework.executionenvironment";
+
+	/**
+	 * Framework launching property identifying packages for which the Framework
+	 * must delegate class loading to the parent class loader of the bundle.
+	 * 
+	 * <p>
+	 * The value of this property may be retrieved by calling the
+	 * {@code BundleContext.getProperty} method.
+	 * 
+	 * @see #FRAMEWORK_BUNDLE_PARENT
+	 * @since 1.3
+	 */
+	String	FRAMEWORK_BOOTDELEGATION				= "org.osgi.framework.bootdelegation";
+
+	/**
+	 * Framework launching property identifying packages which the system bundle
+	 * must export.
+	 * 
+	 * <p>
+	 * If this property is not specified then the framework must calculate a
+	 * reasonable default value for the current execution environment.
+	 * 
+	 * <p>
+	 * The value of this property may be retrieved by calling the
+	 * {@code BundleContext.getProperty} method.
+	 * 
+	 * @since 1.3
+	 */
+	String	FRAMEWORK_SYSTEMPACKAGES				= "org.osgi.framework.system.packages";
+
+	/**
+	 * Framework launching property identifying extra packages which the system
+	 * bundle must export from the current execution environment.
+	 * 
+	 * <p>
+	 * This property is useful for configuring extra system packages in addition
+	 * to the system packages calculated by the framework.
+	 * 
+	 * <p>
+	 * The value of this property may be retrieved by calling the
+	 * {@code BundleContext.getProperty} method.
+	 * 
+	 * @see #FRAMEWORK_SYSTEMPACKAGES
+	 * @since 1.5
+	 */
+	String	FRAMEWORK_SYSTEMPACKAGES_EXTRA			= "org.osgi.framework.system.packages.extra";
+
+	/**
+	 * Framework environment property identifying whether the Framework supports
+	 * framework extension bundles.
+	 * 
+	 * <p>
+	 * As of version 1.4, the value of this property must be {@code true}. The
+	 * Framework must support framework extension bundles.
+	 * 
+	 * <p>
+	 * The value of this property may be retrieved by calling the
+	 * {@code BundleContext.getProperty} method.
+	 * 
+	 * @since 1.3
+	 */
+	String	SUPPORTS_FRAMEWORK_EXTENSION			= "org.osgi.supports.framework.extension";
+
+	/**
+	 * Framework environment property identifying whether the Framework supports
+	 * bootclasspath extension bundles.
+	 * 
+	 * <p>
+	 * If the value of this property is {@code true}, then the Framework
+	 * supports bootclasspath extension bundles. The default value is
+	 * {@code false}.
+	 * <p>
+	 * The value of this property may be retrieved by calling the
+	 * {@code BundleContext.getProperty} method.
+	 * 
+	 * @since 1.3
+	 */
+	String	SUPPORTS_BOOTCLASSPATH_EXTENSION		= "org.osgi.supports.bootclasspath.extension";
+
+	/**
+	 * Framework environment property identifying whether the Framework supports
+	 * fragment bundles.
+	 * 
+	 * <p>
+	 * As of version 1.4, the value of this property must be {@code true}. The
+	 * Framework must support fragment bundles.
+	 * <p>
+	 * The value of this property may be retrieved by calling the
+	 * {@code BundleContext.getProperty} method.
+	 * 
+	 * @since 1.3
+	 */
+	String	SUPPORTS_FRAMEWORK_FRAGMENT				= "org.osgi.supports.framework.fragment";
+
+	/**
+	 * Framework environment property identifying whether the Framework supports
+	 * the {@link #REQUIRE_BUNDLE Require-Bundle} manifest header.
+	 * 
+	 * <p>
+	 * As of version 1.4, the value of this property must be {@code true}. The
+	 * Framework must support the {@code Require-Bundle} manifest header.
+	 * <p>
+	 * The value of this property may be retrieved by calling the
+	 * {@code BundleContext.getProperty} method.
+	 * 
+	 * @since 1.3
+	 */
+	String	SUPPORTS_FRAMEWORK_REQUIREBUNDLE		= "org.osgi.supports.framework.requirebundle";
+
+	/**
+	 * Framework launching property specifying the type of security manager the
+	 * framework must use. If not specified then the framework will not set the
+	 * VM security manager.
+	 * 
+	 * @see #FRAMEWORK_SECURITY_OSGI
+	 * @since 1.5
+	 */
+	String	FRAMEWORK_SECURITY						= "org.osgi.framework.security";
+
+	/**
+	 * Specifies that a security manager that supports all security aspects of
+	 * the OSGi core specification including postponed conditions must be
+	 * installed.
+	 * 
+	 * <p>
+	 * If this value is specified and there is a security manager already
+	 * installed, then a {@code SecurityException} must be thrown when the
+	 * Framework is initialized.
+	 * 
+	 * @see #FRAMEWORK_SECURITY
+	 * @since 1.5
+	 */
+	String	FRAMEWORK_SECURITY_OSGI					= "osgi";
+
+	/**
+	 * Framework launching property specifying the persistent storage area used
+	 * by the framework. The value of this property must be a valid file path in
+	 * the file system to a directory. If the specified directory does not exist
+	 * then the framework will create the directory. If the specified path
+	 * exists but is not a directory or if the framework fails to create the
+	 * storage directory, then framework initialization must fail. The framework
+	 * is free to use this directory as it sees fit. This area can not be shared
+	 * with anything else.
+	 * <p>
+	 * If this property is not set, the framework should use a reasonable
+	 * platform default for the persistent storage area.
+	 * 
+	 * @since 1.5
+	 */
+	String	FRAMEWORK_STORAGE						= "org.osgi.framework.storage";
+
+	/**
+	 * Framework launching property specifying if and when the persistent
+	 * storage area for the framework should be cleaned. If this property is not
+	 * set, then the framework storage area must not be cleaned.
+	 * 
+	 * @see #FRAMEWORK_STORAGE_CLEAN_ONFIRSTINIT
+	 * @since 1.5
+	 */
+	String	FRAMEWORK_STORAGE_CLEAN					= "org.osgi.framework.storage.clean";
+
+	/**
+	 * Specifies that the framework storage area must be cleaned before the
+	 * framework is initialized for the first time. Subsequent inits, starts or
+	 * updates of the framework will not result in cleaning the framework
+	 * storage area.
+	 * 
+	 * @since 1.5
+	 */
+	String	FRAMEWORK_STORAGE_CLEAN_ONFIRSTINIT		= "onFirstInit";
+
+	/**
+	 * Framework launching property specifying a comma separated list of
+	 * additional library file extensions that must be used when a bundle's
+	 * class loader is searching for native libraries. If this property is not
+	 * set, then only the library name returned by
+	 * {@code System.mapLibraryName(String)} will be used to search. This is
+	 * needed for certain operating systems which allow more than one extension
+	 * for a library. For example, AIX allows library extensions of {@code .a}
+	 * and {@code .so}, but {@code System.mapLibraryName(String)} will only
+	 * return names with the {@code .a} extension.
+	 * 
+	 * @since 1.5
+	 */
+	String	FRAMEWORK_LIBRARY_EXTENSIONS			= "org.osgi.framework.library.extensions";
+
+	/**
+	 * Framework launching property specifying an optional OS specific command
+	 * to set file permissions on extracted native code. On some operating
+	 * systems, it is required that native libraries be set to executable. This
+	 * optional property allows you to specify the command. For example, on a
+	 * UNIX style OS, this property could have the following value.
+	 * 
+	 * <pre>
+	 * chmod +rx ${abspath}
+	 * </pre>
+	 * 
+	 * The <code>${abspath}</code> is used by the framework to substitute the
+	 * actual absolute file path.
+	 * 
+	 * @since 1.5
+	 */
+	String	FRAMEWORK_EXECPERMISSION				= "org.osgi.framework.command.execpermission";
+
+	/**
+	 * Specified the substitution string for the absolute path of a file.
+	 * 
+	 * @see #FRAMEWORK_EXECPERMISSION
+	 * @since 1.6
+	 */
+	String	FRAMEWORK_COMMAND_ABSPATH				= "abspath";
+
+	/**
+	 * Framework launching property specifying the trust repositories used by
+	 * the framework. The value is a {@code java.io.File.pathSeparator}
+	 * separated list of valid file paths to files that contain key stores. Key
+	 * stores of type {@code JKS} must be supported and other key store types
+	 * may be supported. The framework will use the key stores as trust
+	 * repositories to authenticate certificates of trusted signers. The key
+	 * stores are only used as read-only trust repositories to access public
+	 * keys. No passwords are required to access the key stores' public keys.
+	 * <p>
+	 * Note that framework implementations are allowed to use other trust
+	 * repositories in addition to the trust repositories specified by this
+	 * property. How these other trust repositories are configured and populated
+	 * is implementation specific.
+	 * 
+	 * @since 1.5
+	 */
+	String	FRAMEWORK_TRUST_REPOSITORIES			= "org.osgi.framework.trust.repositories";
+
+	/**
+	 * Framework launching property specifying the current windowing system. The
+	 * framework should provide a reasonable default if this is not set.
+	 * 
+	 * @since 1.5
+	 */
+	String	FRAMEWORK_WINDOWSYSTEM					= "org.osgi.framework.windowsystem";
+
+	/**
+	 * Framework launching property specifying the beginning start level of the
+	 * framework.
+	 * 
+	 * @see "Core Specification, Starting the Framework."
+	 * @since 1.5
+	 */
+	String	FRAMEWORK_BEGINNING_STARTLEVEL			= "org.osgi.framework.startlevel.beginning";
+
+	/**
+	 * Framework launching property specifying the parent class loader type for
+	 * all bundle class loaders. Default value is
+	 * {@link #FRAMEWORK_BUNDLE_PARENT_BOOT boot}.
+	 * 
+	 * @see #FRAMEWORK_BUNDLE_PARENT_BOOT
+	 * @see #FRAMEWORK_BUNDLE_PARENT_EXT
+	 * @see #FRAMEWORK_BUNDLE_PARENT_APP
+	 * @see #FRAMEWORK_BUNDLE_PARENT_FRAMEWORK
+	 * @since 1.5
+	 */
+	String	FRAMEWORK_BUNDLE_PARENT					= "org.osgi.framework.bundle.parent";
+
+	/**
+	 * Specifies to use of the boot class loader as the parent class loader for
+	 * all bundle class loaders.
+	 * 
+	 * @since 1.5
+	 * @see #FRAMEWORK_BUNDLE_PARENT
+	 */
+	String	FRAMEWORK_BUNDLE_PARENT_BOOT			= "boot";
+
+	/**
+	 * Specifies to use the extension class loader as the parent class loader
+	 * for all bundle class loaders.
+	 * 
+	 * @since 1.5
+	 * @see #FRAMEWORK_BUNDLE_PARENT
+	 */
+	String	FRAMEWORK_BUNDLE_PARENT_EXT				= "ext";
+
+	/**
+	 * Specifies to use the application class loader as the parent class loader
+	 * for all bundle class loaders. Depending on how the framework is launched,
+	 * this may refer to the same class loader as
+	 * {@link #FRAMEWORK_BUNDLE_PARENT_FRAMEWORK}.
+	 * 
+	 * @since 1.5
+	 * @see #FRAMEWORK_BUNDLE_PARENT
+	 */
+	String	FRAMEWORK_BUNDLE_PARENT_APP				= "app";
+
+	/**
+	 * Specifies to use the framework class loader as the parent class loader
+	 * for all bundle class loaders. The framework class loader is the class
+	 * loader used to load the framework implementation. Depending on how the
+	 * framework is launched, this may refer to the same class loader as
+	 * {@link #FRAMEWORK_BUNDLE_PARENT_APP}.
+	 * 
+	 * @since 1.5
+	 * @see #FRAMEWORK_BUNDLE_PARENT
+	 */
+	String	FRAMEWORK_BUNDLE_PARENT_FRAMEWORK		= "framework";
+
+	/*
+	 * Service properties.
+	 */
+
+	/**
+	 * Service property identifying all of the class names under which a service
+	 * was registered in the Framework. The value of this property must be of
+	 * type {@code String[]}.
+	 * 
+	 * <p>
+	 * This property is set by the Framework when a service is registered.
+	 */
+	String	OBJECTCLASS								= "objectClass";
+
+	/**
+	 * Service property identifying a service's registration number. The value
+	 * of this property must be of type {@code Long}.
+	 * 
+	 * <p>
+	 * The value of this property is assigned by the Framework when a service is
+	 * registered. The Framework assigns a unique value that is larger than all
+	 * previously assigned values since the Framework was started. These values
+	 * are NOT persistent across restarts of the Framework.
+	 */
+	String	SERVICE_ID								= "service.id";
+
+	/**
+	 * Service property identifying a service's persistent identifier.
+	 * 
+	 * <p>
+	 * This property may be supplied in the {@code properties}
+	 * {@code Dictionary} object passed to the
+	 * {@code BundleContext.registerService} method. The value of this property
+	 * must be of type {@code String}, {@code String[]}, or {@code Collection}
+	 * of {@code String}.
+	 * 
+	 * <p>
+	 * A service's persistent identifier uniquely identifies the service and
+	 * persists across multiple Framework invocations.
+	 * 
+	 * <p>
+	 * By convention, every bundle has its own unique namespace, starting with
+	 * the bundle's identifier (see {@link Bundle#getBundleId()}) and followed
+	 * by a dot (.). A bundle may use this as the prefix of the persistent
+	 * identifiers for the services it registers.
+	 */
+	String	SERVICE_PID								= "service.pid";
+
+	/**
+	 * Service property identifying a service's ranking number.
+	 * 
+	 * <p>
+	 * This property may be supplied in the {@code properties
+	 * Dictionary} object passed to the {@code BundleContext.registerService}
+	 * method. The value of this property must be of type {@code Integer}.
+	 * 
+	 * <p>
+	 * The service ranking is used by the Framework to determine the <i>natural
+	 * order</i> of services, see {@link ServiceReference#compareTo(Object)},
+	 * and the <i>default</i> service to be returned from a call to the
+	 * {@link BundleContext#getServiceReference(Class)} or
+	 * {@link BundleContext#getServiceReference(String)} method.
+	 * 
+	 * <p>
+	 * The default ranking is zero (0). A service with a ranking of
+	 * {@code Integer.MAX_VALUE} is very likely to be returned as the default
+	 * service, whereas a service with a ranking of {@code Integer.MIN_VALUE} is
+	 * very unlikely to be returned.
+	 * 
+	 * <p>
+	 * If the supplied property value is not of type {@code Integer}, it is
+	 * deemed to have a ranking value of zero.
+	 */
+	String	SERVICE_RANKING							= "service.ranking";
+
+	/**
+	 * Service property identifying a service's vendor.
+	 * 
+	 * <p>
+	 * This property may be supplied in the properties {@code Dictionary} object
+	 * passed to the {@code BundleContext.registerService} method.
+	 */
+	String	SERVICE_VENDOR							= "service.vendor";
+
+	/**
+	 * Service property identifying a service's description.
+	 * 
+	 * <p>
+	 * This property may be supplied in the properties {@code Dictionary} object
+	 * passed to the {@code BundleContext.registerService} method.
+	 */
+	String	SERVICE_DESCRIPTION						= "service.description";
+
+	/**
+	 * Framework environment property identifying the Framework's universally
+	 * unique identifier (UUID). A UUID represents a 128-bit value. A new UUID
+	 * is generated by the {@link Framework#init()} method each time a framework
+	 * is initialized. The value of this property must conform to the UUID
+	 * string representation specified in <a
+	 * href="http://www.ietf.org/rfc/rfc4122.txt">RFC 4122</a>.
+	 * 
+	 * <p>
+	 * The value of this property may be retrieved by calling the
+	 * {@code BundleContext.getProperty} method.
+	 * 
+	 * @since 1.6
+	 */
+	String	FRAMEWORK_UUID							= "org.osgi.framework.uuid";
+
+	/**
+	 * Service property identifying the configuration types supported by a
+	 * distribution provider. Registered by the distribution provider on one of
+	 * its services to indicate the supported configuration types.
+	 * 
+	 * <p>
+	 * The value of this property must be of type {@code String},
+	 * {@code String[]}, or {@code Collection} of {@code String}.
+	 * 
+	 * @since 1.6
+	 * @see "Remote Services Specification"
+	 */
+	String	REMOTE_CONFIGS_SUPPORTED				= "remote.configs.supported";
+
+	/**
+	 * Service property identifying the intents supported by a distribution
+	 * provider. Registered by the distribution provider on one of its services
+	 * to indicate the vocabulary of implemented intents.
+	 * 
+	 * <p>
+	 * The value of this property must be of type {@code String},
+	 * {@code String[]}, or {@code Collection} of {@code String}.
+	 * 
+	 * @since 1.6
+	 * @see "Remote Services Specification"
+	 */
+	String	REMOTE_INTENTS_SUPPORTED				= "remote.intents.supported";
+
+	/**
+	 * Service property identifying the configuration types that should be used
+	 * to export the service. Each configuration type represents the
+	 * configuration parameters for an endpoint. A distribution provider should
+	 * create an endpoint for each configuration type that it supports.
+	 * 
+	 * <p>
+	 * This property may be supplied in the {@code properties}
+	 * {@code Dictionary} object passed to the
+	 * {@code BundleContext.registerService} method. The value of this property
+	 * must be of type {@code String}, {@code String[]}, or {@code Collection}
+	 * of {@code String}.
+	 * 
+	 * @since 1.6
+	 * @see "Remote Services Specification"
+	 */
+	String	SERVICE_EXPORTED_CONFIGS				= "service.exported.configs";
+
+	/**
+	 * Service property identifying the intents that the distribution provider
+	 * must implement to distribute the service. Intents listed in this property
+	 * are reserved for intents that are critical for the code to function
+	 * correctly, for example, ordering of messages. These intents should not be
+	 * configurable.
+	 * 
+	 * <p>
+	 * This property may be supplied in the {@code properties}
+	 * {@code Dictionary} object passed to the
+	 * {@code BundleContext.registerService} method. The value of this property
+	 * must be of type {@code String}, {@code String[]}, or {@code Collection}
+	 * of {@code String}.
+	 * 
+	 * @since 1.6
+	 * @see "Remote Services Specification"
+	 */
+	String	SERVICE_EXPORTED_INTENTS				= "service.exported.intents";
+
+	/**
+	 * Service property identifying the extra intents that the distribution
+	 * provider must implement to distribute the service. This property is
+	 * merged with the {@code service.exported.intents} property before the
+	 * distribution provider interprets the listed intents; it has therefore the
+	 * same semantics but the property should be configurable so the
+	 * administrator can choose the intents based on the topology. Bundles
+	 * should therefore make this property configurable, for example through the
+	 * Configuration Admin service.
+	 * 
+	 * <p>
+	 * This property may be supplied in the {@code properties}
+	 * {@code Dictionary} object passed to the
+	 * {@code BundleContext.registerService} method. The value of this property
+	 * must be of type {@code String}, {@code String[]}, or {@code Collection}
+	 * of {@code String}.
+	 * 
+	 * @since 1.6
+	 * @see "Remote Services Specification"
+	 */
+	String	SERVICE_EXPORTED_INTENTS_EXTRA			= "service.exported.intents.extra";
+
+	/**
+	 * Service property marking the service for export. It defines the
+	 * interfaces under which this service can be exported. This list must be a
+	 * subset of the types under which the service was registered. The single
+	 * value of an asterisk ({@code '*'} \u002A) indicates all the interface
+	 * types under which the service was registered excluding the non-interface
+	 * types. It is strongly recommended to only export interface types and not
+	 * concrete classes due to the complexity of creating proxies for some type
+	 * of concrete classes.
+	 * 
+	 * <p>
+	 * This property may be supplied in the {@code properties}
+	 * {@code Dictionary} object passed to the
+	 * {@code BundleContext.registerService} method. The value of this property
+	 * must be of type {@code String}, {@code String[]}, or {@code Collection}
+	 * of {@code String}.
+	 * 
+	 * @since 1.6
+	 * @see "Remote Services Specification"
+	 */
+	String	SERVICE_EXPORTED_INTERFACES				= "service.exported.interfaces";
+
+	/**
+	 * Service property identifying the service as imported. This service
+	 * property must be set by a distribution provider to any value when it
+	 * registers the endpoint proxy as an imported service. A bundle can use
+	 * this property to filter out imported services.
+	 * 
+	 * <p>
+	 * The value of this property may be of any type.
+	 * 
+	 * @since 1.6
+	 * @see "Remote Services Specification"
+	 */
+	String	SERVICE_IMPORTED						= "service.imported";
+
+	/**
+	 * Service property identifying the configuration types used to import the
+	 * service. Any associated properties for this configuration types must be
+	 * properly mapped to the importing system. For example, a URL in these
+	 * properties must point to a valid resource when used in the importing
+	 * framework. If multiple configuration types are listed in this property,
+	 * then they must be synonyms for exactly the same remote endpoint that is
+	 * used to export this service.
+	 * 
+	 * <p>
+	 * The value of this property must be of type {@code String},
+	 * {@code String[]}, or {@code Collection} of {@code String}.
+	 * 
+	 * @since 1.6
+	 * @see "Remote Services Specification"
+	 * @see #SERVICE_EXPORTED_CONFIGS
+	 */
+	String	SERVICE_IMPORTED_CONFIGS				= "service.imported.configs";
+
+	/**
+	 * Service property identifying the intents that this service implement.
+	 * This property has a dual purpose:
+	 * <ul>
+	 * <li>A bundle can use this service property to notify the distribution
+	 * provider that these intents are already implemented by the exported
+	 * service object.</li>
+	 * <li>A distribution provider must use this property to convey the combined
+	 * intents of: the exporting service, the intents that the exporting
+	 * distribution provider adds, and the intents that the importing
+	 * distribution provider adds.</li>
+	 * </ul>
+	 * 
+	 * To export a service, a distribution provider must expand any qualified
+	 * intents. Both the exporting and importing distribution providers must
+	 * recognize all intents before a service can be distributed.
+	 * 
+	 * <p>
+	 * The value of this property must be of type {@code String},
+	 * {@code String[]}, or {@code Collection} of {@code String}.
+	 * 
+	 * @since 1.6
+	 * @see "Remote Services Specification"
+	 */
+	String	SERVICE_INTENTS							= "service.intents";
+
+	/**
+	 * Manifest header identifying the capabilities that the bundle offers to
+	 * provide to other bundles.
+	 * 
+	 * <p>
+	 * The header value may be retrieved from the {@code Dictionary} object
+	 * returned by the {@code Bundle.getHeaders} method.
+	 * 
+	 * @since 1.6
+	 */
+	String	PROVIDE_CAPABILITY						= "Provide-Capability";
+
+	/**
+	 * Manifest header identifying the capabilities on which the bundle depends.
+	 * 
+	 * <p>
+	 * The header value may be retrieved from the {@code Dictionary} object
+	 * returned by the {@code Bundle.getHeaders} method.
+	 * 
+	 * @since 1.6
+	 */
+	String	REQUIRE_CAPABILITY						= "Require-Capability";
+
+	/**
+	 * Manifest header directive identifying the effective time of the provided
+	 * capability. The default value is {@link #EFFECTIVE_RESOLVE resolve}.
+	 * 
+	 * <p>
+	 * The directive value is encoded in the Provide-Capability manifest header
+	 * like:
+	 * 
+	 * <pre>
+	 *     Provide-Capability: com.acme.capability; effective:="resolve"
+	 * </pre>
+	 * 
+	 * @see #PROVIDE_CAPABILITY
+	 * @see #EFFECTIVE_RESOLVE
+	 * @see #EFFECTIVE_ACTIVE
+	 * @since 1.6
+	 */
+	String	EFFECTIVE_DIRECTIVE						= "effective";
+
+	/**
+	 * Manifest header directive value identifying a capability that is
+	 * effective at resolve time. Capabilities with an effective time of resolve
+	 * are the only capabilities which are processed by the resolver.
+	 * 
+	 * <p>
+	 * The directive value is encoded in the Provide-Capability manifest header
+	 * like:
+	 * 
+	 * <pre>
+	 *     Provide-Capability: com.acme.capability; effective:="resolve"
+	 * </pre>
+	 * 
+	 * @see #EFFECTIVE_DIRECTIVE
+	 * @since 1.6
+	 */
+	String	EFFECTIVE_RESOLVE						= "resolve";
+
+	/**
+	 * Manifest header directive value identifying a capability that is
+	 * effective at active time. Capabilities with an effective time of active
+	 * are ignored by the resolver.
+	 * 
+	 * <p>
+	 * The directive value is encoded in the Provide-Capability manifest header
+	 * like:
+	 * 
+	 * <pre>
+	 *     Provide-Capability: com.acme.capability; effective:="active"
+	 * </pre>
+	 * 
+	 * @see #EFFECTIVE_DIRECTIVE
+	 * @since 1.6
+	 */
+	String	EFFECTIVE_ACTIVE						= "active";
+
+	/**
+	 * Manifest header directive identifying the capability filter specified in
+	 * the Require-Capability manifest header.
+	 * 
+	 * <p>
+	 * The directive value is encoded in the Require-Capability manifest header
+	 * like:
+	 * 
+	 * <pre>
+	 *     Require-Capability: com.acme.capability; filter:="(someattr=somevalue)"
+	 * </pre>
+	 * 
+	 * @see #REQUIRE_CAPABILITY
+	 * @since 1.6
+	 */
+	String	FILTER_DIRECTIVE						= "filter";
+
+	/**
+	 * Framework launching property identifying capabilities which the system
+	 * bundle must provide.
+	 * 
+	 * <p>
+	 * If this property is not specified then the framework must calculate a
+	 * reasonable default value for the current execution environment.
+	 * 
+	 * <p>
+	 * The value of this property may be retrieved by calling the
+	 * {@code BundleContext.getProperty} method.
+	 * 
+	 * @since 1.6
+	 */
+	String	FRAMEWORK_SYSTEMCAPABILITIES			= "org.osgi.framework.system.capabilities";
+
+	/**
+	 * Framework launching property identifying extra capabilities which the
+	 * system bundle must additionally provide.
+	 * 
+	 * <p>
+	 * This property is useful for configuring extra system capabilities in
+	 * addition to the system capabilities calculated by the framework.
+	 * 
+	 * <p>
+	 * The value of this property may be retrieved by calling the
+	 * {@code BundleContext.getProperty} method.
+	 * 
+	 * @see #FRAMEWORK_SYSTEMCAPABILITIES
+	 * @since 1.6
+	 */
+	String	FRAMEWORK_SYSTEMCAPABILITIES_EXTRA		= "org.osgi.framework.system.capabilities.extra";
+
+	/**
+	 * Framework launching property specifying whether multiple bundles having
+	 * the same {@link #BUNDLE_SYMBOLICNAME symbolic name} and
+	 * {@link #BUNDLE_VERSION version} may be installed.
+	 * 
+	 * <p>
+	 * Default value is {@link #FRAMEWORK_BSNVERSION_MANAGED managed} in this
+	 * release of the specification. This default may change in a future
+	 * specification release. Therefore, code must not assume the default
+	 * behavior is {@code managed} and should interrogate the value of this
+	 * property to determine the behavior.
+	 * 
+	 * <p>
+	 * The value of this property may be retrieved by calling the
+	 * {@code BundleContext.getProperty} method.
+	 * 
+	 * @see #FRAMEWORK_BSNVERSION_MULTIPLE
+	 * @see #FRAMEWORK_BSNVERSION_SINGLE
+	 * @see #FRAMEWORK_BSNVERSION_MANAGED
+	 * @since 1.6
+	 */
+	String	FRAMEWORK_BSNVERSION					= "org.osgi.framework.bsnversion";
+
+	/**
+	 * Specifies the framework will allow multiple bundles to be installed
+	 * having the same symbolic name and version.
+	 * 
+	 * @since 1.6
+	 * @see #FRAMEWORK_BSNVERSION
+	 */
+	String	FRAMEWORK_BSNVERSION_MULTIPLE			= "multiple";
+
+	/**
+	 * Specifies the framework will only allow a single bundle to be installed
+	 * for a given symbolic name and version. It will be an error to install a
+	 * bundle or update a bundle to have the same symbolic name and version as
+	 * another installed bundle.
+	 * 
+	 * @since 1.6
+	 * @see #FRAMEWORK_BSNVERSION
+	 * @see BundleException#DUPLICATE_BUNDLE_ERROR
+	 */
+	String	FRAMEWORK_BSNVERSION_SINGLE				= "single";
+
+	/**
+	 * Specifies the framework must consult the {@link CollisionHook bundle
+	 * collision hook} services to determine if it will be an error to install a
+	 * bundle or update a bundle to have the same symbolic name and version as
+	 * another installed bundle. If no bundle collision hook services are
+	 * registered, then it will be an error to install a bundle or update a
+	 * bundle to have the same symbolic name and version as another installed
+	 * bundle.
+	 * 
+	 * @since 1.7
+	 * @see #FRAMEWORK_BSNVERSION
+	 * @see BundleException#DUPLICATE_BUNDLE_ERROR
+	 */
+	String	FRAMEWORK_BSNVERSION_MANAGED			= "managed";
+}
diff --git a/src/main/java/org/osgi/framework/Filter.java b/src/main/java/org/osgi/framework/Filter.java
new file mode 100644
index 0000000..230a6e9
--- /dev/null
+++ b/src/main/java/org/osgi/framework/Filter.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) OSGi Alliance (2000, 2012). All Rights Reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.framework;
+
+import java.util.Dictionary;
+import java.util.Map;
+
+/**
+ * An <a href="http://www.ietf.org/rfc/rfc1960.txt">RFC 1960</a>-based Filter.
+ * <p>
+ * {@code Filter}s can be created by calling
+ * {@link BundleContext#createFilter(String)} or
+ * {@link FrameworkUtil#createFilter(String)} with a filter string.
+ * <p>
+ * A {@code Filter} can be used numerous times to determine if the match
+ * argument matches the filter string that was used to create the {@code Filter}.
+ * <p>
+ * Some examples of LDAP filters are:
+ * 
+ * <pre>
+ *  "(cn=Babs Jensen)"
+ *  "(!(cn=Tim Howes))"
+ *  "(&(" + Constants.OBJECTCLASS + "=Person)(|(sn=Jensen)(cn=Babs J*)))"
+ *  "(o=univ*of*mich*)"
+ * </pre>
+ * 
+ * @since 1.1
+ * @see "Core Specification, Filters, for a description of the filter string syntax."
+ * @ThreadSafe
+ * @noimplement
+ * @version $Id: 807a04ac07c3230b8f4d4e0f9588a35fbdc41e18 $
+ */
+public interface Filter {
+	/**
+	 * Filter using a service's properties.
+	 * <p>
+	 * This {@code Filter} is executed using the keys and values of the
+	 * referenced service's properties. The keys are looked up in a case
+	 * insensitive manner.
+	 * 
+	 * @param reference The reference to the service whose properties are used
+	 *        in the match.
+	 * @return {@code true} if the service's properties match this
+	 *         {@code Filter}; {@code false} otherwise.
+	 */
+	boolean match(ServiceReference<?> reference);
+
+	/**
+	 * Filter using a {@code Dictionary} with case insensitive key lookup. This
+	 * {@code Filter} is executed using the specified {@code Dictionary}'s keys
+	 * and values. The keys are looked up in a case insensitive manner.
+	 * 
+	 * @param dictionary The {@code Dictionary} whose key/value pairs are used
+	 *        in the match.
+	 * @return {@code true} if the {@code Dictionary}'s values match this
+	 *         filter; {@code false} otherwise.
+	 * @throws IllegalArgumentException If {@code dictionary} contains case
+	 *         variants of the same key name.
+	 */
+	boolean match(Dictionary<String, ?> dictionary);
+
+	/**
+	 * Returns this {@code Filter}'s filter string.
+	 * <p>
+	 * The filter string is normalized by removing whitespace which does not
+	 * affect the meaning of the filter.
+	 * 
+	 * @return This {@code Filter}'s filter string.
+	 */
+	String toString();
+
+	/**
+	 * Compares this {@code Filter} to another {@code Filter}.
+	 * 
+	 * <p>
+	 * This implementation returns the result of calling
+	 * {@code this.toString().equals(obj.toString())}.
+	 * 
+	 * @param obj The object to compare against this {@code Filter}.
+	 * @return If the other object is a {@code Filter} object, then returns the
+	 *         result of calling {@code this.toString().equals(obj.toString())};
+	 *         {@code false} otherwise.
+	 */
+	boolean equals(Object obj);
+
+	/**
+	 * Returns the hashCode for this {@code Filter}.
+	 * 
+	 * <p>
+	 * This implementation returns the result of calling
+	 * {@code this.toString().hashCode()}.
+	 * 
+	 * @return The hashCode of this {@code Filter}.
+	 */
+	int hashCode();
+
+	/**
+	 * Filter using a {@code Dictionary}. This {@code Filter} is executed using
+	 * the specified {@code Dictionary}'s keys and values. The keys are looked
+	 * up in a normal manner respecting case.
+	 * 
+	 * @param dictionary The {@code Dictionary} whose key/value pairs are used
+	 *        in the match.
+	 * @return {@code true} if the {@code Dictionary}'s values match this
+	 *         filter; {@code false} otherwise.
+	 * @since 1.3
+	 */
+	boolean matchCase(Dictionary<String, ?> dictionary);
+
+	/**
+	 * Filter using a {@code Map}. This {@code Filter} is executed using the
+	 * specified {@code Map}'s keys and values. The keys are looked up in a
+	 * normal manner respecting case.
+	 * 
+	 * @param map The {@code Map} whose key/value pairs are used in the match.
+	 *        Maps with {@code null} key or values are not supported. A
+	 *        {@code null} value is considered not present to the filter.
+	 * @return {@code true} if the {@code Map}'s values match this filter;
+	 *         {@code false} otherwise.
+	 * @since 1.6
+	 */
+	boolean matches(Map<String, ?> map);
+}
diff --git a/src/main/java/org/osgi/framework/FrameworkEvent.java b/src/main/java/org/osgi/framework/FrameworkEvent.java
new file mode 100644
index 0000000..59fa92e
--- /dev/null
+++ b/src/main/java/org/osgi/framework/FrameworkEvent.java
@@ -0,0 +1,244 @@
+/*
+ * Copyright (c) OSGi Alliance (2004, 2012). All Rights Reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.framework;
+
+import java.util.EventObject;
+import org.osgi.framework.startlevel.FrameworkStartLevel;
+import org.osgi.framework.wiring.FrameworkWiring;
+
+/**
+ * A general event from the Framework.
+ * 
+ * <p>
+ * {@code FrameworkEvent} objects are delivered to {@code FrameworkListener}s
+ * when a general event occurs within the OSGi environment. A type code is used
+ * to identify the event type for future extendability.
+ * 
+ * <p>
+ * OSGi Alliance reserves the right to extend the set of event types.
+ * 
+ * @Immutable
+ * @see FrameworkListener
+ * @version $Id: f679c7581879a2e6006ecdd317a5dd5f735764e3 $
+ */
+
+public class FrameworkEvent extends EventObject {
+	static final long		serialVersionUID				= 207051004521261705L;
+	/**
+	 * Bundle related to the event.
+	 */
+	private final Bundle	bundle;
+
+	/**
+	 * Exception related to the event.
+	 */
+	private final Throwable	throwable;
+
+	/**
+	 * Type of event.
+	 */
+	private final int		type;
+
+	/**
+	 * The Framework has started.
+	 * 
+	 * <p>
+	 * This event is fired when the Framework has started after all installed
+	 * bundles that are marked to be started have been started and the Framework
+	 * has reached the initial start level. The source of this event is the
+	 * System Bundle.
+	 * 
+	 * @see "The Start Level Specification"
+	 */
+	public final static int	STARTED							= 0x00000001;
+
+	/**
+	 * An error has occurred.
+	 * 
+	 * <p>
+	 * There was an error associated with a bundle.
+	 */
+	public final static int	ERROR							= 0x00000002;
+
+	/**
+	 * A FrameworkWiring.refreshBundles operation has completed.
+	 * 
+	 * <p>
+	 * This event is fired when the Framework has completed the refresh bundles
+	 * operation initiated by a call to the FrameworkWiring.refreshBundles
+	 * method. The source of this event is the System Bundle.
+	 * 
+	 * @since 1.2
+	 * @see FrameworkWiring#refreshBundles(java.util.Collection,
+	 *      FrameworkListener...)
+	 */
+	public final static int	PACKAGES_REFRESHED				= 0x00000004;
+
+	/**
+	 * A FrameworkStartLevel.setStartLevel operation has completed.
+	 * 
+	 * <p>
+	 * This event is fired when the Framework has completed changing the active
+	 * start level initiated by a call to the StartLevel.setStartLevel method.
+	 * The source of this event is the System Bundle.
+	 * 
+	 * @since 1.2
+	 * @see FrameworkStartLevel#setStartLevel(int, FrameworkListener...)
+	 */
+	public final static int	STARTLEVEL_CHANGED				= 0x00000008;
+
+	/**
+	 * A warning has occurred.
+	 * 
+	 * <p>
+	 * There was a warning associated with a bundle.
+	 * 
+	 * @since 1.3
+	 */
+	public final static int	WARNING							= 0x00000010;
+
+	/**
+	 * An informational event has occurred.
+	 * 
+	 * <p>
+	 * There was an informational event associated with a bundle.
+	 * 
+	 * @since 1.3
+	 */
+	public final static int	INFO							= 0x00000020;
+
+	/**
+	 * The Framework has stopped.
+	 * 
+	 * <p>
+	 * This event is fired when the Framework has been stopped because of a stop
+	 * operation on the system bundle. The source of this event is the System
+	 * Bundle.
+	 * 
+	 * @since 1.5
+	 */
+	public final static int	STOPPED							= 0x00000040;
+
+	/**
+	 * The Framework has stopped during update.
+	 * 
+	 * <p>
+	 * This event is fired when the Framework has been stopped because of an
+	 * update operation on the system bundle. The Framework will be restarted
+	 * after this event is fired. The source of this event is the System Bundle.
+	 * 
+	 * @since 1.5
+	 */
+	public final static int	STOPPED_UPDATE					= 0x00000080;
+
+	/**
+	 * The Framework has stopped and the boot class path has changed.
+	 * 
+	 * <p>
+	 * This event is fired when the Framework has been stopped because of a stop
+	 * operation on the system bundle and a bootclasspath extension bundle has
+	 * been installed or updated. The source of this event is the System Bundle.
+	 * 
+	 * @since 1.5
+	 */
+	public final static int	STOPPED_BOOTCLASSPATH_MODIFIED	= 0x00000100;
+
+	/**
+	 * The Framework did not stop before the wait timeout expired.
+	 * 
+	 * <p>
+	 * This event is fired when the Framework did not stop before the wait
+	 * timeout expired. The source of this event is the System Bundle.
+	 * 
+	 * @since 1.5
+	 */
+	public final static int	WAIT_TIMEDOUT					= 0x00000200;
+
+	/**
+	 * Creates a Framework event.
+	 * 
+	 * @param type The event type.
+	 * @param source The event source object. This may not be {@code null}.
+	 * @deprecated As of 1.2. This constructor is deprecated in favor of using
+	 *             the other constructor with the System Bundle as the event
+	 *             source.
+	 */
+	public FrameworkEvent(int type, Object source) {
+		super(source);
+		this.type = type;
+		this.bundle = null;
+		this.throwable = null;
+	}
+
+	/**
+	 * Creates a Framework event regarding the specified bundle.
+	 * 
+	 * @param type The event type.
+	 * @param bundle The event source.
+	 * @param throwable The related exception. This argument may be {@code null}
+	 *        if there is no related exception.
+	 */
+	public FrameworkEvent(int type, Bundle bundle, Throwable throwable) {
+		super(bundle);
+		this.type = type;
+		this.bundle = bundle;
+		this.throwable = throwable;
+	}
+
+	/**
+	 * Returns the exception related to this event.
+	 * 
+	 * @return The related exception or {@code null} if none.
+	 */
+	public Throwable getThrowable() {
+		return throwable;
+	}
+
+	/**
+	 * Returns the bundle associated with the event. This bundle is also the
+	 * source of the event.
+	 * 
+	 * @return The bundle associated with the event.
+	 */
+	public Bundle getBundle() {
+		return bundle;
+	}
+
+	/**
+	 * Returns the type of framework event.
+	 * <p>
+	 * The type values are:
+	 * <ul>
+	 * <li>{@link #STARTED}
+	 * <li>{@link #ERROR}
+	 * <li>{@link #WARNING}
+	 * <li>{@link #INFO}
+	 * <li>{@link #PACKAGES_REFRESHED}
+	 * <li>{@link #STARTLEVEL_CHANGED}
+	 * <li>{@link #STOPPED}
+	 * <li>{@link #STOPPED_BOOTCLASSPATH_MODIFIED}
+	 * <li>{@link #STOPPED_UPDATE}
+	 * <li>{@link #WAIT_TIMEDOUT}
+	 * </ul>
+	 * 
+	 * @return The type of state change.
+	 */
+
+	public int getType() {
+		return type;
+	}
+}
diff --git a/src/main/java/org/osgi/framework/FrameworkListener.java b/src/main/java/org/osgi/framework/FrameworkListener.java
new file mode 100644
index 0000000..7e1e813
--- /dev/null
+++ b/src/main/java/org/osgi/framework/FrameworkListener.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) OSGi Alliance (2000, 2011). All Rights Reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.framework;
+
+import java.util.EventListener;
+
+/**
+ * A {@code FrameworkEvent} listener. {@code FrameworkListener} is a listener
+ * interface that may be implemented by a bundle developer. When a
+ * {@code FrameworkEvent} is fired, it is asynchronously delivered to a
+ * {@code FrameworkListener}. The Framework delivers {@code FrameworkEvent}
+ * objects to a {@code FrameworkListener} in order and must not concurrently
+ * call a {@code FrameworkListener}.
+ * 
+ * <p>
+ * A {@code FrameworkListener} object is registered with the Framework using the
+ * {@link BundleContext#addFrameworkListener(FrameworkListener)} method.
+ * {@code FrameworkListener} objects are called with a {@code FrameworkEvent}
+ * objects when the Framework starts and when asynchronous errors occur.
+ * 
+ * @see FrameworkEvent
+ * @NotThreadSafe
+ * @version $Id: ad7f563bd13b60e2b8a378f147057ca7f0accae2 $
+ */
+
+public interface FrameworkListener extends EventListener {
+
+	/**
+	 * Receives notification of a general {@code FrameworkEvent} object.
+	 * 
+	 * @param event The {@code FrameworkEvent} object.
+	 */
+	public void frameworkEvent(FrameworkEvent event);
+}
diff --git a/src/main/java/org/osgi/framework/FrameworkUtil.java b/src/main/java/org/osgi/framework/FrameworkUtil.java
new file mode 100644
index 0000000..c26ba43
--- /dev/null
+++ b/src/main/java/org/osgi/framework/FrameworkUtil.java
@@ -0,0 +1,2111 @@
+/*
+ * Copyright (c) OSGi Alliance (2005, 2012). All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.framework;
+
+import java.lang.reflect.AccessibleObject;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.AbstractMap;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import javax.security.auth.x500.X500Principal;
+
+/**
+ * Framework Utility class.
+ * 
+ * <p>
+ * This class contains utility methods which access Framework functions that may
+ * be useful to bundles.
+ * 
+ * @since 1.3
+ * @ThreadSafe
+ * @version $Id: 1f46ea2bbbe2a1242fdaf0877709fb9c02eefae1 $
+ */
+public class FrameworkUtil {
+	/**
+	 * FrameworkUtil objects may not be constructed.
+	 */
+	private FrameworkUtil() {
+		// private empty constructor to prevent construction
+	}
+
+	/**
+	 * Creates a {@code Filter} object. This {@code Filter} object may be used
+	 * to match a {@code ServiceReference} object or a {@code Dictionary}
+	 * object.
+	 * 
+	 * <p>
+	 * If the filter cannot be parsed, an {@link InvalidSyntaxException} will be
+	 * thrown with a human readable message where the filter became unparsable.
+	 * 
+	 * <p>
+	 * This method returns a Filter implementation which may not perform as well
+	 * as the framework implementation-specific Filter implementation returned
+	 * by {@link BundleContext#createFilter(String)}.
+	 * 
+	 * @param filter The filter string.
+	 * @return A {@code Filter} object encapsulating the filter string.
+	 * @throws InvalidSyntaxException If {@code filter} contains an invalid
+	 *         filter string that cannot be parsed.
+	 * @throws NullPointerException If {@code filter} is null.
+	 * 
+	 * @see Filter
+	 */
+	public static Filter createFilter(String filter) throws InvalidSyntaxException {
+		return FilterImpl.newInstance(filter);
+	}
+
+	/**
+	 * Match a Distinguished Name (DN) chain against a pattern. DNs can be
+	 * matched using wildcards. A wildcard ({@code '*'} \u002A) replaces all
+	 * possible values. Due to the structure of the DN, the comparison is more
+	 * complicated than string-based wildcard matching.
+	 * <p>
+	 * A wildcard can stand for zero or more DNs in a chain, a number of
+	 * relative distinguished names (RDNs) within a DN, or the value of a single
+	 * RDN. The DNs in the chain and the matching pattern are canonicalized
+	 * before processing. This means, among other things, that spaces must be
+	 * ignored, except in values.
+	 * <p>
+	 * The format of a wildcard match pattern is:
+	 * 
+	 * <pre>
+	 * matchPattern ::= dn-match ( ';' dn-match ) *
+	 * dn-match     ::= ( '*' | rdn-match ) ( ',' rdn-match ) * | '-'
+	 * rdn-match    ::= name '=' value-match
+	 * value-match  ::= '*' | value-star
+	 * value-star   ::= < value, requires escaped '*' and '-' >
+	 * </pre>
+	 * <p>
+	 * The most simple case is a single wildcard; it must match any DN. A
+	 * wildcard can also replace the first list of RDNs of a DN. The first RDNs
+	 * are the least significant. Such lists of matched RDNs can be empty.
+	 * <p>
+	 * For example, a match pattern with a wildcard that matches all DNs that
+	 * end with RDNs of o=ACME and c=US would look like this:
+	 * 
+	 * <pre>
+	 * *, o=ACME, c=US
+	 * </pre>
+	 * 
+	 * This match pattern would match the following DNs:
+	 * 
+	 * <pre>
+	 * cn = Bugs Bunny, o = ACME, c = US
+	 * ou = Carrots, cn=Daffy Duck, o=ACME, c=US
+	 * street = 9C\, Avenue St. Drézéry, o=ACME, c=US
+	 * dc=www, dc=acme, dc=com, o=ACME, c=US
+	 * o=ACME, c=US
+	 * </pre>
+	 * 
+	 * The following DNs would not match:
+	 * 
+	 * <pre>
+	 * street = 9C\, Avenue St. Drézéry, o=ACME, c=FR
+	 * dc=www, dc=acme, dc=com, c=US
+	 * </pre>
+	 * 
+	 * If a wildcard is used for a value of an RDN, the value must be exactly *.
+	 * The wildcard must match any value, and no substring matching must be
+	 * done. For example:
+	 * 
+	 * <pre>
+	 * cn=*,o=ACME,c=*
+	 * </pre>
+	 * 
+	 * This match pattern with wildcard must match the following DNs:
+	 * 
+	 * <pre>
+	 * cn=Bugs Bunny,o=ACME,c=US
+	 * cn = Daffy Duck , o = ACME , c = US
+	 * cn=Road Runner, o=ACME, c=NL
+	 * </pre>
+	 * 
+	 * But not:
+	 * 
+	 * <pre>
+	 * o=ACME, c=NL
+	 * dc=acme.com, cn=Bugs Bunny, o=ACME, c=US
+	 * </pre>
+	 * 
+	 * <p>
+	 * A match pattern may contain a chain of DN match patterns. The semicolon(
+	 * {@code ';'} \u003B) must be used to separate DN match patterns in a
+	 * chain. Wildcards can also be used to match against a complete DN within a
+	 * chain.
+	 * <p>
+	 * The following example matches a certificate signed by Tweety Inc. in the
+	 * US.
+	 * </p>
+	 * 
+	 * <pre>
+	 * * ; ou=S & V, o=Tweety Inc., c=US
+	 * </pre>
+	 * <p>
+	 * The wildcard ('*') matches zero or one DN in the chain, however,
+	 * sometimes it is necessary to match a longer chain. The minus sign (
+	 * {@code '-'} \u002D) represents zero or more DNs, whereas the asterisk
+	 * only represents a single DN. For example, to match a DN where the Tweety
+	 * Inc. is in the DN chain, use the following expression:
+	 * </p>
+	 * 
+	 * <pre>
+	 * - ; *, o=Tweety Inc., c=US
+	 * </pre>
+	 * 
+	 * @param matchPattern The pattern against which to match the DN chain.
+	 * @param dnChain The DN chain to match against the specified pattern. Each
+	 *        element of the chain must be of type {@code String} and use the
+	 *        format defined in <a
+	 *        href="http://www.ietf.org/rfc/rfc2253.txt">RFC 2253</a>.
+	 * @return {@code true} If the pattern matches the DN chain; otherwise
+	 *         {@code false} is returned.
+	 * @throws IllegalArgumentException If the specified match pattern or DN
+	 *         chain is invalid.
+	 * @since 1.5
+	 */
+	public static boolean matchDistinguishedNameChain(String matchPattern, List<String> dnChain) {
+		return DNChainMatching.match(matchPattern, dnChain);
+	}
+
+	/**
+	 * Return a {@code Bundle} for the specified bundle class. The returned
+	 * {@code Bundle} is the bundle associated with the bundle class loader
+	 * which defined the specified class.
+	 * 
+	 * @param classFromBundle A class defined by a bundle class loader.
+	 * @return A {@code Bundle} for the specified bundle class or {@code null}
+	 *         if the specified class was not defined by a bundle class loader.
+	 * @since 1.5
+	 */
+	public static Bundle getBundle(final Class<?> classFromBundle) {
+		// We use doPriv since the caller may not have permission
+		// to call getClassLoader.
+		Object cl = AccessController.doPrivileged(new PrivilegedAction<Object>() {
+			public Object run() {
+				return classFromBundle.getClassLoader();
+			}
+		});
+
+		if (cl instanceof BundleReference) {
+			return ((BundleReference) cl).getBundle();
+		}
+		return null;
+	}
+
+	/**
+	 * RFC 1960-based Filter. Filter objects can be created by calling the
+	 * constructor with the desired filter string. A Filter object can be called
+	 * numerous times to determine if the match argument matches the filter
+	 * string that was used to create the Filter object.
+	 * 
+	 * <p>
+	 * The syntax of a filter string is the string representation of LDAP search
+	 * filters as defined in RFC 1960: <i>A String Representation of LDAP Search
+	 * Filters</i> (available at http://www.ietf.org/rfc/rfc1960.txt). It should
+	 * be noted that RFC 2254: <i>A String Representation of LDAP Search
+	 * Filters</i> (available at http://www.ietf.org/rfc/rfc2254.txt) supersedes
+	 * RFC 1960 but only adds extensible matching and is not applicable for this
+	 * API.
+	 * 
+	 * <p>
+	 * The string representation of an LDAP search filter is defined by the
+	 * following grammar. It uses a prefix format.
+	 * 
+	 * <pre>
+	 *   <filter> ::= '(' <filtercomp> ')'
+	 *   <filtercomp> ::= <and> | <or> | <not> | <item>
+	 *   <and> ::= '&' <filterlist>
+	 *   <or> ::= '|' <filterlist>
+	 *   <not> ::= '!' <filter>
+	 *   <filterlist> ::= <filter> | <filter> <filterlist>
+	 *   <item> ::= <simple> | <present> | <substring>
+	 *   <simple> ::= <attr> <filtertype> <value>
+	 *   <filtertype> ::= <equal> | <approx> | <greater> | <less>
+	 *   <equal> ::= '='
+	 *   <approx> ::= '˜='
+	 *   <greater> ::= '>='
+	 *   <less> ::= '<='
+	 *   <present> ::= <attr> '=*'
+	 *   <substring> ::= <attr> '=' <initial> <any> <final>
+	 *   <initial> ::= NULL | <value>
+	 *   <any> ::= '*' <starval>
+	 *   <starval> ::= NULL | <value> '*' <starval>
+	 *   <final> ::= NULL | <value>
+	 * </pre>
+	 * 
+	 * {@code <attr>} is a string representing an attribute, or key, in
+	 * the properties objects of the registered services. Attribute names are
+	 * not case sensitive; that is cn and CN both refer to the same attribute.
+	 * {@code <value>} is a string representing the value, or part of one,
+	 * of a key in the properties objects of the registered services. If a
+	 * {@code <value>} must contain one of the characters ' {@code *}' or
+	 * '{@code (}' or '{@code )}', these characters should be escaped by
+	 * preceding them with the backslash '{@code \}' character. Note that
+	 * although both the {@code <substring>} and {@code <present>}
+	 * productions can produce the {@code 'attr=*'} construct, this construct is
+	 * used only to denote a presence filter.
+	 * 
+	 * <p>
+	 * Examples of LDAP filters are:
+	 * 
+	 * <pre>
+	 *   "(cn=Babs Jensen)"
+	 *   "(!(cn=Tim Howes))"
+	 *   "(&(" + Constants.OBJECTCLASS + "=Person)(|(sn=Jensen)(cn=Babs J*)))"
+	 *   "(o=univ*of*mich*)"
+	 * </pre>
+	 * 
+	 * <p>
+	 * The approximate match ({@code ~=}) is implementation specific but should
+	 * at least ignore case and white space differences. Optional are codes like
+	 * soundex or other smart "closeness" comparisons.
+	 * 
+	 * <p>
+	 * Comparison of values is not straightforward. Strings are compared
+	 * differently than numbers and it is possible for a key to have multiple
+	 * values. Note that that keys in the match argument must always be strings.
+	 * The comparison is defined by the object type of the key's value. The
+	 * following rules apply for comparison:
+	 * 
+	 * <blockquote>
+	 * <TABLE BORDER=0>
+	 * <TR>
+	 * <TD><b>Property Value Type </b></TD>
+	 * <TD><b>Comparison Type</b></TD>
+	 * </TR>
+	 * <TR>
+	 * <TD>String</TD>
+	 * <TD>String comparison</TD>
+	 * </TR>
+	 * <TR valign=top>
+	 * <TD>Integer, Long, Float, Double, Byte, Short, BigInteger, BigDecimal</TD>
+	 * <TD>numerical comparison</TD>
+	 * </TR>
+	 * <TR>
+	 * <TD>Character</TD>
+	 * <TD>character comparison</TD>
+	 * </TR>
+	 * <TR>
+	 * <TD>Boolean</TD>
+	 * <TD>equality comparisons only</TD>
+	 * </TR>
+	 * <TR>
+	 * <TD>[] (array)</TD>
+	 * <TD>recursively applied to values</TD>
+	 * </TR>
+	 * <TR>
+	 * <TD>Collection</TD>
+	 * <TD>recursively applied to values</TD>
+	 * </TR>
+	 * </TABLE>
+	 * Note: arrays of primitives are also supported. </blockquote>
+	 * 
+	 * A filter matches a key that has multiple values if it matches at least
+	 * one of those values. For example,
+	 * 
+	 * <pre>
+	 * Dictionary d = new Hashtable();
+	 * d.put("cn", new String[] {"a", "b", "c"});
+	 * </pre>
+	 * 
+	 * d will match {@code (cn=a)} and also {@code (cn=b)}
+	 * 
+	 * <p>
+	 * A filter component that references a key having an unrecognizable data
+	 * type will evaluate to {@code false} .
+	 */
+	static private final class FilterImpl implements Filter {
+		/* filter operators */
+		private static final int	EQUAL		= 1;
+		private static final int	APPROX		= 2;
+		private static final int	GREATER		= 3;
+		private static final int	LESS		= 4;
+		private static final int	PRESENT		= 5;
+		private static final int	SUBSTRING	= 6;
+		private static final int	AND			= 7;
+		private static final int	OR			= 8;
+		private static final int	NOT			= 9;
+
+		/** filter operation */
+		private final int			op;
+		/** filter attribute or null if operation AND, OR or NOT */
+		private final String		attr;
+		/** filter operands */
+		private final Object		value;
+
+		/* normalized filter string for Filter object */
+		private transient String	filterString;
+
+		/**
+		 * Constructs a {@link FilterImpl} object. This filter object may be
+		 * used to match a {@link ServiceReference} or a Dictionary.
+		 * 
+		 * <p>
+		 * If the filter cannot be parsed, an {@link InvalidSyntaxException}
+		 * will be thrown with a human readable message where the filter became
+		 * unparsable.
+		 * 
+		 * @param filterString the filter string.
+		 * @throws InvalidSyntaxException If the filter parameter contains an
+		 *         invalid filter string that cannot be parsed.
+		 */
+		static FilterImpl newInstance(String filterString) throws InvalidSyntaxException {
+			return new Parser(filterString).parse();
+		}
+
+		FilterImpl(int operation, String attr, Object value) {
+			this.op = operation;
+			this.attr = attr;
+			this.value = value;
+			filterString = null;
+		}
+
+		/**
+		 * Filter using a service's properties.
+		 * <p>
+		 * This {@code Filter} is executed using the keys and values of the
+		 * referenced service's properties. The keys are looked up in a case
+		 * insensitive manner.
+		 * 
+		 * @param reference The reference to the service whose properties are
+		 *        used in the match.
+		 * @return {@code true} if the service's properties match this
+		 *         {@code Filter}; {@code false} otherwise.
+		 */
+		public boolean match(ServiceReference<?> reference) {
+			return matches(new ServiceReferenceMap(reference));
+		}
+
+		/**
+		 * Filter using a {@code Dictionary} with case insensitive key lookup.
+		 * This {@code Filter} is executed using the specified
+		 * {@code Dictionary}'s keys and values. The keys are looked up in a
+		 * case insensitive manner.
+		 * 
+		 * @param dictionary The {@code Dictionary} whose key/value pairs are
+		 *        used in the match.
+		 * @return {@code true} if the {@code Dictionary}'s values match this
+		 *         filter; {@code false} otherwise.
+		 * @throws IllegalArgumentException If {@code dictionary} contains case
+		 *         variants of the same key name.
+		 */
+		public boolean match(Dictionary<String, ?> dictionary) {
+			return matches(new CaseInsensitiveMap(dictionary));
+		}
+
+		/**
+		 * Filter using a {@code Dictionary}. This {@code Filter} is executed
+		 * using the specified {@code Dictionary}'s keys and values. The keys
+		 * are looked up in a normal manner respecting case.
+		 * 
+		 * @param dictionary The {@code Dictionary} whose key/value pairs are
+		 *        used in the match.
+		 * @return {@code true} if the {@code Dictionary}'s values match this
+		 *         filter; {@code false} otherwise.
+		 * @since 1.3
+		 */
+		public boolean matchCase(Dictionary<String, ?> dictionary) {
+			switch (op) {
+				case AND : {
+					FilterImpl[] filters = (FilterImpl[]) value;
+					for (FilterImpl f : filters) {
+						if (!f.matchCase(dictionary)) {
+							return false;
+						}
+					}
+					return true;
+				}
+
+				case OR : {
+					FilterImpl[] filters = (FilterImpl[]) value;
+					for (FilterImpl f : filters) {
+						if (f.matchCase(dictionary)) {
+							return true;
+						}
+					}
+					return false;
+				}
+
+				case NOT : {
+					FilterImpl filter = (FilterImpl) value;
+					return !filter.matchCase(dictionary);
+				}
+
+				case SUBSTRING :
+				case EQUAL :
+				case GREATER :
+				case LESS :
+				case APPROX : {
+					Object prop = (dictionary == null) ? null : dictionary.get(attr);
+					return compare(op, prop, value);
+				}
+
+				case PRESENT : {
+					Object prop = (dictionary == null) ? null : dictionary.get(attr);
+					return prop != null;
+				}
+			}
+
+			return false;
+		}
+
+		/**
+		 * Filter using a {@code Map}. This {@code Filter} is executed using the
+		 * specified {@code Map}'s keys and values. The keys are looked up in a
+		 * normal manner respecting case.
+		 * 
+		 * @param map The {@code Map} whose key/value pairs are used in the
+		 *        match. Maps with {@code null} key or values are not supported.
+		 *        A {@code null} value is considered not present to the filter.
+		 * @return {@code true} if the {@code Map}'s values match this filter;
+		 *         {@code false} otherwise.
+		 * @since 1.6
+		 */
+		public boolean matches(Map<String, ?> map) {
+			switch (op) {
+				case AND : {
+					FilterImpl[] filters = (FilterImpl[]) value;
+					for (FilterImpl f : filters) {
+						if (!f.matches(map)) {
+							return false;
+						}
+					}
+					return true;
+				}
+
+				case OR : {
+					FilterImpl[] filters = (FilterImpl[]) value;
+					for (FilterImpl f : filters) {
+						if (f.matches(map)) {
+							return true;
+						}
+					}
+					return false;
+				}
+
+				case NOT : {
+					FilterImpl filter = (FilterImpl) value;
+					return !filter.matches(map);
+				}
+
+				case SUBSTRING :
+				case EQUAL :
+				case GREATER :
+				case LESS :
+				case APPROX : {
+					Object prop = (map == null) ? null : map.get(attr);
+					return compare(op, prop, value);
+				}
+
+				case PRESENT : {
+					Object prop = (map == null) ? null : map.get(attr);
+					return prop != null;
+				}
+			}
+
+			return false;
+		}
+
+		/**
+		 * Returns this {@code Filter}'s filter string.
+		 * <p>
+		 * The filter string is normalized by removing whitespace which does not
+		 * affect the meaning of the filter.
+		 * 
+		 * @return This {@code Filter}'s filter string.
+		 */
+		public String toString() {
+			String result = filterString;
+			if (result == null) {
+				filterString = result = normalize().toString();
+			}
+			return result;
+		}
+
+		/**
+		 * Returns this {@code Filter}'s normalized filter string.
+		 * <p>
+		 * The filter string is normalized by removing whitespace which does not
+		 * affect the meaning of the filter.
+		 * 
+		 * @return This {@code Filter}'s filter string.
+		 */
+		private StringBuffer normalize() {
+			StringBuffer sb = new StringBuffer();
+			sb.append('(');
+
+			switch (op) {
+				case AND : {
+					sb.append('&');
+
+					FilterImpl[] filters = (FilterImpl[]) value;
+					for (FilterImpl f : filters) {
+						sb.append(f.normalize());
+					}
+
+					break;
+				}
+
+				case OR : {
+					sb.append('|');
+
+					FilterImpl[] filters = (FilterImpl[]) value;
+					for (FilterImpl f : filters) {
+						sb.append(f.normalize());
+					}
+
+					break;
+				}
+
+				case NOT : {
+					sb.append('!');
+					FilterImpl filter = (FilterImpl) value;
+					sb.append(filter.normalize());
+
+					break;
+				}
+
+				case SUBSTRING : {
+					sb.append(attr);
+					sb.append('=');
+
+					String[] substrings = (String[]) value;
+
+					for (String substr : substrings) {
+						if (substr == null) /* * */{
+							sb.append('*');
+						} else /* xxx */{
+							sb.append(encodeValue(substr));
+						}
+					}
+
+					break;
+				}
+				case EQUAL : {
+					sb.append(attr);
+					sb.append('=');
+					sb.append(encodeValue((String) value));
+
+					break;
+				}
+				case GREATER : {
+					sb.append(attr);
+					sb.append(">=");
+					sb.append(encodeValue((String) value));
+
+					break;
+				}
+				case LESS : {
+					sb.append(attr);
+					sb.append("<=");
+					sb.append(encodeValue((String) value));
+
+					break;
+				}
+				case APPROX : {
+					sb.append(attr);
+					sb.append("~=");
+					sb.append(encodeValue(approxString((String) value)));
+
+					break;
+				}
+
+				case PRESENT : {
+					sb.append(attr);
+					sb.append("=*");
+
+					break;
+				}
+			}
+
+			sb.append(')');
+
+			return sb;
+		}
+
+		/**
+		 * Compares this {@code Filter} to another {@code Filter}.
+		 * 
+		 * <p>
+		 * This implementation returns the result of calling
+		 * {@code this.toString().equals(obj.toString()}.
+		 * 
+		 * @param obj The object to compare against this {@code Filter}.
+		 * @return If the other object is a {@code Filter} object, then returns
+		 *         the result of calling
+		 *         {@code this.toString().equals(obj.toString()}; {@code false}
+		 *         otherwise.
+		 */
+		public boolean equals(Object obj) {
+			if (obj == this) {
+				return true;
+			}
+
+			if (!(obj instanceof Filter)) {
+				return false;
+			}
+
+			return this.toString().equals(obj.toString());
+		}
+
+		/**
+		 * Returns the hashCode for this {@code Filter}.
+		 * 
+		 * <p>
+		 * This implementation returns the result of calling
+		 * {@code this.toString().hashCode()}.
+		 * 
+		 * @return The hashCode of this {@code Filter}.
+		 */
+		public int hashCode() {
+			return this.toString().hashCode();
+		}
+
+		/**
+		 * Encode the value string such that '(', '*', ')' and '\' are escaped.
+		 * 
+		 * @param value unencoded value string.
+		 * @return encoded value string.
+		 */
+		private static String encodeValue(String value) {
+			boolean encoded = false;
+			int inlen = value.length();
+			int outlen = inlen << 1; /* inlen 2 */
+
+			char[] output = new char[outlen];
+			value.getChars(0, inlen, output, inlen);
+
+			int cursor = 0;
+			for (int i = inlen; i < outlen; i++) {
+				char c = output[i];
+
+				switch (c) {
+					case '(' :
+					case '*' :
+					case ')' :
+					case '\\' : {
+						output[cursor] = '\\';
+						cursor++;
+						encoded = true;
+
+						break;
+					}
+				}
+
+				output[cursor] = c;
+				cursor++;
+			}
+
+			return encoded ? new String(output, 0, cursor) : value;
+		}
+
+		private boolean compare(int operation, Object value1, Object value2) {
+			if (value1 == null) {
+				return false;
+			}
+			if (value1 instanceof String) {
+				return compare_String(operation, (String) value1, value2);
+			}
+
+			Class<?> clazz = value1.getClass();
+			if (clazz.isArray()) {
+				Class<?> type = clazz.getComponentType();
+				if (type.isPrimitive()) {
+					return compare_PrimitiveArray(operation, type, value1, value2);
+				}
+				return compare_ObjectArray(operation, (Object[]) value1, value2);
+			}
+			if (value1 instanceof Collection<?>) {
+				return compare_Collection(operation, (Collection<?>) value1, value2);
+			}
+			if (value1 instanceof Integer) {
+				return compare_Integer(operation, ((Integer) value1).intValue(), value2);
+			}
+			if (value1 instanceof Long) {
+				return compare_Long(operation, ((Long) value1).longValue(), value2);
+			}
+			if (value1 instanceof Byte) {
+				return compare_Byte(operation, ((Byte) value1).byteValue(), value2);
+			}
+			if (value1 instanceof Short) {
+				return compare_Short(operation, ((Short) value1).shortValue(), value2);
+			}
+			if (value1 instanceof Character) {
+				return compare_Character(operation, ((Character) value1).charValue(), value2);
+			}
+			if (value1 instanceof Float) {
+				return compare_Float(operation, ((Float) value1).floatValue(), value2);
+			}
+			if (value1 instanceof Double) {
+				return compare_Double(operation, ((Double) value1).doubleValue(), value2);
+			}
+			if (value1 instanceof Boolean) {
+				return compare_Boolean(operation, ((Boolean) value1).booleanValue(), value2);
+			}
+			if (value1 instanceof Comparable<?>) {
+				Comparable<Object> comparable = (Comparable<Object>) value1;
+				return compare_Comparable(operation, comparable, value2);
+			}
+			return compare_Unknown(operation, value1, value2);
+		}
+
+		private boolean compare_Collection(int operation, Collection<?> collection, Object value2) {
+			for (Object value1 : collection) {
+				if (compare(operation, value1, value2)) {
+					return true;
+				}
+			}
+			return false;
+		}
+
+		private boolean compare_ObjectArray(int operation, Object[] array, Object value2) {
+			for (Object value1 : array) {
+				if (compare(operation, value1, value2)) {
+					return true;
+				}
+			}
+			return false;
+		}
+
+		private boolean compare_PrimitiveArray(int operation, Class<?> type, Object primarray, Object value2) {
+			if (Integer.TYPE.isAssignableFrom(type)) {
+				int[] array = (int[]) primarray;
+				for (int value1 : array) {
+					if (compare_Integer(operation, value1, value2)) {
+						return true;
+					}
+				}
+				return false;
+			}
+			if (Long.TYPE.isAssignableFrom(type)) {
+				long[] array = (long[]) primarray;
+				for (long value1 : array) {
+					if (compare_Long(operation, value1, value2)) {
+						return true;
+					}
+				}
+				return false;
+			}
+			if (Byte.TYPE.isAssignableFrom(type)) {
+				byte[] array = (byte[]) primarray;
+				for (byte value1 : array) {
+					if (compare_Byte(operation, value1, value2)) {
+						return true;
+					}
+				}
+				return false;
+			}
+			if (Short.TYPE.isAssignableFrom(type)) {
+				short[] array = (short[]) primarray;
+				for (short value1 : array) {
+					if (compare_Short(operation, value1, value2)) {
+						return true;
+					}
+				}
+				return false;
+			}
+			if (Character.TYPE.isAssignableFrom(type)) {
+				char[] array = (char[]) primarray;
+				for (char value1 : array) {
+					if (compare_Character(operation, value1, value2)) {
+						return true;
+					}
+				}
+				return false;
+			}
+			if (Float.TYPE.isAssignableFrom(type)) {
+				float[] array = (float[]) primarray;
+				for (float value1 : array) {
+					if (compare_Float(operation, value1, value2)) {
+						return true;
+					}
+				}
+				return false;
+			}
+			if (Double.TYPE.isAssignableFrom(type)) {
+				double[] array = (double[]) primarray;
+				for (double value1 : array) {
+					if (compare_Double(operation, value1, value2)) {
+						return true;
+					}
+				}
+				return false;
+			}
+			if (Boolean.TYPE.isAssignableFrom(type)) {
+				boolean[] array = (boolean[]) primarray;
+				for (boolean value1 : array) {
+					if (compare_Boolean(operation, value1, value2)) {
+						return true;
+					}
+				}
+				return false;
+			}
+			return false;
+		}
+
+		private boolean compare_String(int operation, String string, Object value2) {
+			switch (operation) {
+				case SUBSTRING : {
+					String[] substrings = (String[]) value2;
+					int pos = 0;
+					for (int i = 0, size = substrings.length; i < size; i++) {
+						String substr = substrings[i];
+
+						if (i + 1 < size) /* if this is not that last substr */{
+							if (substr == null) /* * */{
+								String substr2 = substrings[i + 1];
+
+								if (substr2 == null) /* ** */
+									continue; /* ignore first star */
+								/* xxx */
+								int index = string.indexOf(substr2, pos);
+								if (index == -1) {
+									return false;
+								}
+
+								pos = index + substr2.length();
+								if (i + 2 < size) // if there are more
+									// substrings, increment
+									// over the string we just
+									// matched; otherwise need
+									// to do the last substr
+									// check
+									i++;
+							} else /* xxx */{
+								int len = substr.length();
+								if (string.regionMatches(pos, substr, 0, len)) {
+									pos += len;
+								} else {
+									return false;
+								}
+							}
+						} else /* last substr */{
+							if (substr == null) /* * */{
+								return true;
+							}
+							/* xxx */
+							return string.endsWith(substr);
+						}
+					}
+
+					return true;
+				}
+				case EQUAL : {
+					return string.equals(value2);
+				}
+				case APPROX : {
+					string = approxString(string);
+					String string2 = approxString((String) value2);
+
+					return string.equalsIgnoreCase(string2);
+				}
+				case GREATER : {
+					return string.compareTo((String) value2) >= 0;
+				}
+				case LESS : {
+					return string.compareTo((String) value2) <= 0;
+				}
+			}
+			return false;
+		}
+
+		private boolean compare_Integer(int operation, int intval, Object value2) {
+			if (operation == SUBSTRING) {
+				return false;
+			}
+			int intval2;
+			try {
+				intval2 = Integer.parseInt(((String) value2).trim());
+			} catch (IllegalArgumentException e) {
+				return false;
+			}
+			switch (operation) {
+				case APPROX :
+				case EQUAL : {
+					return intval == intval2;
+				}
+				case GREATER : {
+					return intval >= intval2;
+				}
+				case LESS : {
+					return intval <= intval2;
+				}
+			}
+			return false;
+		}
+
+		private boolean compare_Long(int operation, long longval, Object value2) {
+			if (operation == SUBSTRING) {
+				return false;
+			}
+			long longval2;
+			try {
+				longval2 = Long.parseLong(((String) value2).trim());
+			} catch (IllegalArgumentException e) {
+				return false;
+			}
+
+			switch (operation) {
+				case APPROX :
+				case EQUAL : {
+					return longval == longval2;
+				}
+				case GREATER : {
+					return longval >= longval2;
+				}
+				case LESS : {
+					return longval <= longval2;
+				}
+			}
+			return false;
+		}
+
+		private boolean compare_Byte(int operation, byte byteval, Object value2) {
+			if (operation == SUBSTRING) {
+				return false;
+			}
+			byte byteval2;
+			try {
+				byteval2 = Byte.parseByte(((String) value2).trim());
+			} catch (IllegalArgumentException e) {
+				return false;
+			}
+
+			switch (operation) {
+				case APPROX :
+				case EQUAL : {
+					return byteval == byteval2;
+				}
+				case GREATER : {
+					return byteval >= byteval2;
+				}
+				case LESS : {
+					return byteval <= byteval2;
+				}
+			}
+			return false;
+		}
+
+		private boolean compare_Short(int operation, short shortval, Object value2) {
+			if (operation == SUBSTRING) {
+				return false;
+			}
+			short shortval2;
+			try {
+				shortval2 = Short.parseShort(((String) value2).trim());
+			} catch (IllegalArgumentException e) {
+				return false;
+			}
+
+			switch (operation) {
+				case APPROX :
+				case EQUAL : {
+					return shortval == shortval2;
+				}
+				case GREATER : {
+					return shortval >= shortval2;
+				}
+				case LESS : {
+					return shortval <= shortval2;
+				}
+			}
+			return false;
+		}
+
+		private boolean compare_Character(int operation, char charval, Object value2) {
+			if (operation == SUBSTRING) {
+				return false;
+			}
+			char charval2;
+			try {
+				charval2 = ((String) value2).charAt(0);
+			} catch (IndexOutOfBoundsException e) {
+				return false;
+			}
+
+			switch (operation) {
+				case EQUAL : {
+					return charval == charval2;
+				}
+				case APPROX : {
+					return (charval == charval2) || (Character.toUpperCase(charval) == Character.toUpperCase(charval2)) || (Character.toLowerCase(charval) == Character.toLowerCase(charval2));
+				}
+				case GREATER : {
+					return charval >= charval2;
+				}
+				case LESS : {
+					return charval <= charval2;
+				}
+			}
+			return false;
+		}
+
+		private boolean compare_Boolean(int operation, boolean boolval, Object value2) {
+			if (operation == SUBSTRING) {
+				return false;
+			}
+			boolean boolval2 = Boolean.valueOf(((String) value2).trim()).booleanValue();
+			switch (operation) {
+				case APPROX :
+				case EQUAL :
+				case GREATER :
+				case LESS : {
+					return boolval == boolval2;
+				}
+			}
+			return false;
+		}
+
+		private boolean compare_Float(int operation, float floatval, Object value2) {
+			if (operation == SUBSTRING) {
+				return false;
+			}
+			float floatval2;
+			try {
+				floatval2 = Float.parseFloat(((String) value2).trim());
+			} catch (IllegalArgumentException e) {
+				return false;
+			}
+
+			switch (operation) {
+				case APPROX :
+				case EQUAL : {
+					return Float.compare(floatval, floatval2) == 0;
+				}
+				case GREATER : {
+					return Float.compare(floatval, floatval2) >= 0;
+				}
+				case LESS : {
+					return Float.compare(floatval, floatval2) <= 0;
+				}
+			}
+			return false;
+		}
+
+		private boolean compare_Double(int operation, double doubleval, Object value2) {
+			if (operation == SUBSTRING) {
+				return false;
+			}
+			double doubleval2;
+			try {
+				doubleval2 = Double.parseDouble(((String) value2).trim());
+			} catch (IllegalArgumentException e) {
+				return false;
+			}
+
+			switch (operation) {
+				case APPROX :
+				case EQUAL : {
+					return Double.compare(doubleval, doubleval2) == 0;
+				}
+				case GREATER : {
+					return Double.compare(doubleval, doubleval2) >= 0;
+				}
+				case LESS : {
+					return Double.compare(doubleval, doubleval2) <= 0;
+				}
+			}
+			return false;
+		}
+
+		private static Object valueOf(Class<?> target, String value2) {
+			do {
+				Method method;
+				try {
+					method = target.getMethod("valueOf", String.class);
+				} catch (NoSuchMethodException e) {
+					break;
+				}
+				if (Modifier.isStatic(method.getModifiers()) && target.isAssignableFrom(method.getReturnType())) {
+					setAccessible(method);
+					try {
+						return method.invoke(null, value2.trim());
+					} catch (IllegalAccessException e) {
+						return null;
+					} catch (InvocationTargetException e) {
+						return null;
+					}
+				}
+			} while (false);
+
+			do {
+				Constructor<?> constructor;
+				try {
+					constructor = target.getConstructor(String.class);
+				} catch (NoSuchMethodException e) {
+					break;
+				}
+				setAccessible(constructor);
+				try {
+					return constructor.newInstance(value2.trim());
+				} catch (IllegalAccessException e) {
+					return null;
+				} catch (InvocationTargetException e) {
+					return null;
+				} catch (InstantiationException e) {
+					return null;
+				}
+			} while (false);
+
+			return null;
+		}
+
+		private static void setAccessible(AccessibleObject accessible) {
+			if (!accessible.isAccessible()) {
+				AccessController.doPrivileged(new SetAccessibleAction(accessible));
+			}
+		}
+
+		private boolean compare_Comparable(int operation, Comparable<Object> value1, Object value2) {
+			if (operation == SUBSTRING) {
+				return false;
+			}
+			value2 = valueOf(value1.getClass(), (String) value2);
+			if (value2 == null) {
+				return false;
+			}
+			try {
+				switch (operation) {
+					case APPROX :
+					case EQUAL : {
+						return value1.compareTo(value2) == 0;
+					}
+					case GREATER : {
+						return value1.compareTo(value2) >= 0;
+					}
+					case LESS : {
+						return value1.compareTo(value2) <= 0;
+					}
+				}
+			} catch (Exception e) {
+				// if the compareTo method throws an exception; return false
+				return false;
+			}
+			return false;
+		}
+
+		private boolean compare_Unknown(int operation, Object value1, Object value2) {
+			if (operation == SUBSTRING) {
+				return false;
+			}
+			value2 = valueOf(value1.getClass(), (String) value2);
+			if (value2 == null) {
+				return false;
+			}
+			try {
+				switch (operation) {
+					case APPROX :
+					case EQUAL :
+					case GREATER :
+					case LESS : {
+						return value1.equals(value2);
+					}
+				}
+			} catch (Exception e) {
+				// if the equals method throws an exception; return false
+				return false;
+			}
+			return false;
+		}
+
+		/**
+		 * Map a string for an APPROX (~=) comparison.
+		 * 
+		 * This implementation removes white spaces. This is the minimum
+		 * implementation allowed by the OSGi spec.
+		 * 
+		 * @param input Input string.
+		 * @return String ready for APPROX comparison.
+		 */
+		private static String approxString(String input) {
+			boolean changed = false;
+			char[] output = input.toCharArray();
+			int cursor = 0;
+			for (char c : output) {
+				if (Character.isWhitespace(c)) {
+					changed = true;
+					continue;
+				}
+
+				output[cursor] = c;
+				cursor++;
+			}
+
+			return changed ? new String(output, 0, cursor) : input;
+		}
+
+		/**
+		 * Parser class for OSGi filter strings. This class parses the complete
+		 * filter string and builds a tree of Filter objects rooted at the
+		 * parent.
+		 */
+		static private final class Parser {
+			private final String	filterstring;
+			private final char[]	filterChars;
+			private int				pos;
+
+			Parser(String filterstring) {
+				this.filterstring = filterstring;
+				filterChars = filterstring.toCharArray();
+				pos = 0;
+			}
+
+			FilterImpl parse() throws InvalidSyntaxException {
+				FilterImpl filter;
+				try {
+					filter = parse_filter();
+				} catch (ArrayIndexOutOfBoundsException e) {
+					throw new InvalidSyntaxException("Filter ended abruptly", filterstring, e);
+				}
+
+				if (pos != filterChars.length) {
+					throw new InvalidSyntaxException("Extraneous trailing characters: " + filterstring.substring(pos), filterstring);
+				}
+				return filter;
+			}
+
+			private FilterImpl parse_filter() throws InvalidSyntaxException {
+				FilterImpl filter;
+				skipWhiteSpace();
+
+				if (filterChars[pos] != '(') {
+					throw new InvalidSyntaxException("Missing '(': " + filterstring.substring(pos), filterstring);
+				}
+
+				pos++;
+
+				filter = parse_filtercomp();
+
+				skipWhiteSpace();
+
+				if (filterChars[pos] != ')') {
+					throw new InvalidSyntaxException("Missing ')': " + filterstring.substring(pos), filterstring);
+				}
+
+				pos++;
+
+				skipWhiteSpace();
+
+				return filter;
+			}
+
+			private FilterImpl parse_filtercomp() throws InvalidSyntaxException {
+				skipWhiteSpace();
+
+				char c = filterChars[pos];
+
+				switch (c) {
+					case '&' : {
+						pos++;
+						return parse_and();
+					}
+					case '|' : {
+						pos++;
+						return parse_or();
+					}
+					case '!' : {
+						pos++;
+						return parse_not();
+					}
+				}
+				return parse_item();
+			}
+
+			private FilterImpl parse_and() throws InvalidSyntaxException {
+				int lookahead = pos;
+				skipWhiteSpace();
+
+				if (filterChars[pos] != '(') {
+					pos = lookahead - 1;
+					return parse_item();
+				}
+
+				List<FilterImpl> operands = new ArrayList<FilterImpl>(10);
+
+				while (filterChars[pos] == '(') {
+					FilterImpl child = parse_filter();
+					operands.add(child);
+				}
+
+				return new FilterImpl(FilterImpl.AND, null, operands.toArray(new FilterImpl[operands.size()]));
+			}
+
+			private FilterImpl parse_or() throws InvalidSyntaxException {
+				int lookahead = pos;
+				skipWhiteSpace();
+
+				if (filterChars[pos] != '(') {
+					pos = lookahead - 1;
+					return parse_item();
+				}
+
+				List<FilterImpl> operands = new ArrayList<FilterImpl>(10);
+
+				while (filterChars[pos] == '(') {
+					FilterImpl child = parse_filter();
+					operands.add(child);
+				}
+
+				return new FilterImpl(FilterImpl.OR, null, operands.toArray(new FilterImpl[operands.size()]));
+			}
+
+			private FilterImpl parse_not() throws InvalidSyntaxException {
+				int lookahead = pos;
+				skipWhiteSpace();
+
+				if (filterChars[pos] != '(') {
+					pos = lookahead - 1;
+					return parse_item();
+				}
+
+				FilterImpl child = parse_filter();
+
+				return new FilterImpl(FilterImpl.NOT, null, child);
+			}
+
+			private FilterImpl parse_item() throws InvalidSyntaxException {
+				String attr = parse_attr();
+
+				skipWhiteSpace();
+
+				switch (filterChars[pos]) {
+					case '~' : {
+						if (filterChars[pos + 1] == '=') {
+							pos += 2;
+							return new FilterImpl(FilterImpl.APPROX, attr, parse_value());
+						}
+						break;
+					}
+					case '>' : {
+						if (filterChars[pos + 1] == '=') {
+							pos += 2;
+							return new FilterImpl(FilterImpl.GREATER, attr, parse_value());
+						}
+						break;
+					}
+					case '<' : {
+						if (filterChars[pos + 1] == '=') {
+							pos += 2;
+							return new FilterImpl(FilterImpl.LESS, attr, parse_value());
+						}
+						break;
+					}
+					case '=' : {
+						if (filterChars[pos + 1] == '*') {
+							int oldpos = pos;
+							pos += 2;
+							skipWhiteSpace();
+							if (filterChars[pos] == ')') {
+								return new FilterImpl(FilterImpl.PRESENT, attr, null);
+							}
+							pos = oldpos;
+						}
+
+						pos++;
+						Object string = parse_substring();
+
+						if (string instanceof String) {
+							return new FilterImpl(FilterImpl.EQUAL, attr, string);
+						}
+						return new FilterImpl(FilterImpl.SUBSTRING, attr, string);
+					}
+				}
+
+				throw new InvalidSyntaxException("Invalid operator: " + filterstring.substring(pos), filterstring);
+			}
+
+			private String parse_attr() throws InvalidSyntaxException {
+				skipWhiteSpace();
+
+				int begin = pos;
+				int end = pos;
+
+				char c = filterChars[pos];
+
+				while (c != '~' && c != '<' && c != '>' && c != '=' && c != '(' && c != ')') {
+					pos++;
+
+					if (!Character.isWhitespace(c)) {
+						end = pos;
+					}
+
+					c = filterChars[pos];
+				}
+
+				int length = end - begin;
+
+				if (length == 0) {
+					throw new InvalidSyntaxException("Missing attr: " + filterstring.substring(pos), filterstring);
+				}
+
+				return new String(filterChars, begin, length);
+			}
+
+			private String parse_value() throws InvalidSyntaxException {
+				StringBuffer sb = new StringBuffer(filterChars.length - pos);
+
+				parseloop: while (true) {
+					char c = filterChars[pos];
+
+					switch (c) {
+						case ')' : {
+							break parseloop;
+						}
+
+						case '(' : {
+							throw new InvalidSyntaxException("Invalid value: " + filterstring.substring(pos), filterstring);
+						}
+
+						case '\\' : {
+							pos++;
+							c = filterChars[pos];
+							/* fall through into default */
+						}
+
+						default : {
+							sb.append(c);
+							pos++;
+							break;
+						}
+					}
+				}
+
+				if (sb.length() == 0) {
+					throw new InvalidSyntaxException("Missing value: " + filterstring.substring(pos), filterstring);
+				}
+
+				return sb.toString();
+			}
+
+			private Object parse_substring() throws InvalidSyntaxException {
+				StringBuffer sb = new StringBuffer(filterChars.length - pos);
+
+				List<String> operands = new ArrayList<String>(10);
+
+				parseloop: while (true) {
+					char c = filterChars[pos];
+
+					switch (c) {
+						case ')' : {
+							if (sb.length() > 0) {
+								operands.add(sb.toString());
+							}
+
+							break parseloop;
+						}
+
+						case '(' : {
+							throw new InvalidSyntaxException("Invalid value: " + filterstring.substring(pos), filterstring);
+						}
+
+						case '*' : {
+							if (sb.length() > 0) {
+								operands.add(sb.toString());
+							}
+
+							sb.setLength(0);
+
+							operands.add(null);
+							pos++;
+
+							break;
+						}
+
+						case '\\' : {
+							pos++;
+							c = filterChars[pos];
+							/* fall through into default */
+						}
+
+						default : {
+							sb.append(c);
+							pos++;
+							break;
+						}
+					}
+				}
+
+				int size = operands.size();
+
+				if (size == 0) {
+					return "";
+				}
+
+				if (size == 1) {
+					Object single = operands.get(0);
+
+					if (single != null) {
+						return single;
+					}
+				}
+
+				return operands.toArray(new String[size]);
+			}
+
+			private void skipWhiteSpace() {
+				for (int length = filterChars.length; (pos < length) && Character.isWhitespace(filterChars[pos]);) {
+					pos++;
+				}
+			}
+		}
+	}
+
+	/**
+	 * This Map is used for case-insensitive key lookup during filter
+	 * evaluation. This Map implementation only supports the get operation using
+	 * a String key as no other operations are used by the Filter
+	 * implementation.
+	 */
+	static private final class CaseInsensitiveMap extends AbstractMap<String, Object> implements Map<String, Object> {
+		private final Dictionary<String, ?>	dictionary;
+		private final String[]				keys;
+
+		/**
+		 * Create a case insensitive map from the specified dictionary.
+		 * 
+		 * @param dictionary
+		 * @throws IllegalArgumentException If {@code dictionary} contains case
+		 *         variants of the same key name.
+		 */
+		CaseInsensitiveMap(Dictionary<String, ?> dictionary) {
+			if (dictionary == null) {
+				this.dictionary = null;
+				this.keys = new String[0];
+				return;
+			}
+			this.dictionary = dictionary;
+			List<String> keyList = new ArrayList<String>(dictionary.size());
+			for (Enumeration<?> e = dictionary.keys(); e.hasMoreElements();) {
+				Object k = e.nextElement();
+				if (k instanceof String) {
+					String key = (String) k;
+					for (String i : keyList) {
+						if (key.equalsIgnoreCase(i)) {
+							throw new IllegalArgumentException();
+						}
+					}
+					keyList.add(key);
+				}
+			}
+			this.keys = keyList.toArray(new String[keyList.size()]);
+		}
+
+		public Object get(Object o) {
+			String k = (String) o;
+			for (String key : keys) {
+				if (key.equalsIgnoreCase(k)) {
+					return dictionary.get(key);
+				}
+			}
+			return null;
+		}
+
+		public Set<java.util.Map.Entry<String, Object>> entrySet() {
+			throw new UnsupportedOperationException();
+		}
+	}
+
+	/**
+	 * This Map is used for key lookup from a ServiceReference during filter
+	 * evaluation. This Map implementation only supports the get operation using
+	 * a String key as no other operations are used by the Filter
+	 * implementation.
+	 */
+	static private final class ServiceReferenceMap extends AbstractMap<String, Object> implements Map<String, Object> {
+		private final ServiceReference<?>	reference;
+
+		ServiceReferenceMap(ServiceReference<?> reference) {
+			this.reference = reference;
+		}
+
+		public Object get(Object key) {
+			if (reference == null) {
+				return null;
+			}
+			return reference.getProperty((String) key);
+		}
+
+		public Set<java.util.Map.Entry<String, Object>> entrySet() {
+			throw new UnsupportedOperationException();
+		}
+	}
+
+	static private final class SetAccessibleAction implements PrivilegedAction<Object> {
+		private final AccessibleObject	accessible;
+
+		SetAccessibleAction(AccessibleObject accessible) {
+			this.accessible = accessible;
+		}
+
+		public Object run() {
+			accessible.setAccessible(true);
+			return null;
+		}
+	}
+
+	/**
+	 * This class contains a method to match a distinguished name (DN) chain
+	 * against and DN chain pattern.
+	 * <p>
+	 * The format of DNs are given in RFC 2253. We represent a signature chain
+	 * for an X.509 certificate as a semicolon separated list of DNs. This is
+	 * what we refer to as the DN chain. Each DN is made up of relative
+	 * distinguished names (RDN) which in turn are made up of key value pairs.
+	 * For example:
+	 * 
+	 * <pre>
+	 *   cn=ben+ou=research,o=ACME,c=us;ou=Super CA,c=CA
+	 * </pre>
+	 * 
+	 * is made up of two DNs: "{@code cn=ben+ou=research,o=ACME,c=us} " and "
+	 * {@code ou=Super CA,c=CA} ". The first DN is made of of three RDNs: "
+	 * {@code cn=ben+ou=research}" and "{@code o=ACME}" and " {@code c=us}
+	 * ". The first RDN has two name value pairs: " {@code cn=ben}" and "
+	 * {@code ou=research}".
+	 * <p>
+	 * A chain pattern makes use of wildcards ('*' or '-') to match against DNs,
+	 * and wildcards ('*') to match againts DN prefixes, and value. If a DN in a
+	 * match pattern chain is made up of a wildcard ("*"), that wildcard will
+	 * match zero or one DNs in the chain. If a DN in a match pattern chain is
+	 * made up of a wildcard ("-"), that wildcard will match zero or more DNs in
+	 * the chain. If the first RDN of a DN is the wildcard ("*"), that DN will
+	 * match any other DN with the same suffix (the DN with the wildcard RDN
+	 * removed). If a value of a name/value pair is a wildcard ("*"), the value
+	 * will match any value for that name.
+	 */
+	static private final class DNChainMatching {
+		private static final String	MINUS_WILDCARD	= "-";
+		private static final String	STAR_WILDCARD	= "*";
+
+		/**
+		 * Check the name/value pairs of the rdn against the pattern.
+		 * 
+		 * @param rdn List of name value pairs for a given RDN.
+		 * @param rdnPattern List of name value pattern pairs.
+		 * @return true if the list of name value pairs match the pattern.
+		 */
+		private static boolean rdnmatch(List<?> rdn, List<?> rdnPattern) {
+			if (rdn.size() != rdnPattern.size()) {
+				return false;
+			}
+			for (int i = 0; i < rdn.size(); i++) {
+				String rdnNameValue = (String) rdn.get(i);
+				String patNameValue = (String) rdnPattern.get(i);
+				int rdnNameEnd = rdnNameValue.indexOf('=');
+				int patNameEnd = patNameValue.indexOf('=');
+				if (rdnNameEnd != patNameEnd || !rdnNameValue.regionMatches(0, patNameValue, 0, rdnNameEnd)) {
+					return false;
+				}
+				String patValue = patNameValue.substring(patNameEnd);
+				String rdnValue = rdnNameValue.substring(rdnNameEnd);
+				if (!rdnValue.equals(patValue) && !patValue.equals("=*") && !patValue.equals("=#16012a")) {
+					return false;
+				}
+			}
+			return true;
+		}
+
+		private static boolean dnmatch(List<?> dn, List<?> dnPattern) {
+			int dnStart = 0;
+			int patStart = 0;
+			int patLen = dnPattern.size();
+			if (patLen == 0) {
+				return false;
+			}
+			if (dnPattern.get(0).equals(STAR_WILDCARD)) {
+				patStart = 1;
+				patLen--;
+			}
+			if (dn.size() < patLen) {
+				return false;
+			} else {
+				if (dn.size() > patLen) {
+					if (!dnPattern.get(0).equals(STAR_WILDCARD)) {
+						// If the number of rdns do not match we must have a
+						// prefix map
+						return false;
+					}
+					// The rdnPattern and rdn must have the same number of
+					// elements
+					dnStart = dn.size() - patLen;
+				}
+			}
+			for (int i = 0; i < patLen; i++) {
+				if (!rdnmatch((List<?>) dn.get(i + dnStart), (List<?>) dnPattern.get(i + patStart))) {
+					return false;
+				}
+			}
+			return true;
+		}
+
+		/**
+		 * Parses a distinguished name chain pattern and returns a List where
+		 * each element represents a distinguished name (DN) in the chain of
+		 * DNs. Each element will be either a String, if the element represents
+		 * a wildcard ("*" or "-"), or a List representing an RDN. Each element
+		 * in the RDN List will be a String, if the element represents a
+		 * wildcard ("*"), or a List of Strings, each String representing a
+		 * name/value pair in the RDN.
+		 * 
+		 * @param dnChain
+		 * @return a list of DNs.
+		 * @throws IllegalArgumentException
+		 */
+		private static List<Object> parseDNchainPattern(String dnChain) {
+			if (dnChain == null) {
+				throw new IllegalArgumentException("The DN chain must not be null.");
+			}
+			List<Object> parsed = new ArrayList<Object>();
+			int startIndex = 0;
+			startIndex = skipSpaces(dnChain, startIndex);
+			while (startIndex < dnChain.length()) {
+				int endIndex = startIndex;
+				boolean inQuote = false;
+				out: while (endIndex < dnChain.length()) {
+					char c = dnChain.charAt(endIndex);
+					switch (c) {
+						case '"' :
+							inQuote = !inQuote;
+							break;
+						case '\\' :
+							endIndex++; // skip the escaped char
+							break;
+						case ';' :
+							if (!inQuote)
+								break out;
+					}
+					endIndex++;
+				}
+				if (endIndex > dnChain.length()) {
+					throw new IllegalArgumentException("unterminated escape");
+				}
+				parsed.add(dnChain.substring(startIndex, endIndex));
+				startIndex = endIndex + 1;
+				startIndex = skipSpaces(dnChain, startIndex);
+			}
+
+			// Now we parse is a list of strings, lets make List of rdn out
+			// of them
+			for (int i = 0; i < parsed.size(); i++) {
+				String dn = (String) parsed.get(i);
+				if (dn.equals(STAR_WILDCARD) || dn.equals(MINUS_WILDCARD)) {
+					continue;
+				}
+				List<Object> rdns = new ArrayList<Object>();
+				if (dn.charAt(0) == '*') {
+					if (dn.charAt(1) != ',') {
+						throw new IllegalArgumentException("invalid wildcard prefix");
+					}
+					rdns.add(STAR_WILDCARD);
+					dn = new X500Principal(dn.substring(2)).getName(X500Principal.CANONICAL);
+				} else {
+					dn = new X500Principal(dn).getName(X500Principal.CANONICAL);
+				}
+				// Now dn is a nice CANONICAL DN
+				parseDN(dn, rdns);
+				parsed.set(i, rdns);
+			}
+			if (parsed.size() == 0) {
+				throw new IllegalArgumentException("empty DN chain");
+			}
+			return parsed;
+		}
+
+		private static List<Object> parseDNchain(List<String> chain) {
+			if (chain == null) {
+				throw new IllegalArgumentException("DN chain must not be null.");
+			}
+			List<Object> result = new ArrayList<Object>(chain.size());
+			// Now we parse is a list of strings, lets make List of rdn out
+			// of them
+			for (String dn : chain) {
+				dn = new X500Principal(dn).getName(X500Principal.CANONICAL);
+				// Now dn is a nice CANONICAL DN
+				List<Object> rdns = new ArrayList<Object>();
+				parseDN(dn, rdns);
+				result.add(rdns);
+			}
+			if (result.size() == 0) {
+				throw new IllegalArgumentException("empty DN chain");
+			}
+			return result;
+		}
+
+		/**
+		 * Increment startIndex until the end of dnChain is hit or until it is
+		 * the index of a non-space character.
+		 */
+		private static int skipSpaces(String dnChain, int startIndex) {
+			while (startIndex < dnChain.length() && dnChain.charAt(startIndex) == ' ') {
+				startIndex++;
+			}
+			return startIndex;
+		}
+
+		/**
+		 * Takes a distinguished name in canonical form and fills in the
+		 * rdnArray with the extracted RDNs.
+		 * 
+		 * @param dn the distinguished name in canonical form.
+		 * @param rdn the list to fill in with RDNs extracted from the dn
+		 * @throws IllegalArgumentException if a formatting error is found.
+		 */
+		private static void parseDN(String dn, List<Object> rdn) {
+			int startIndex = 0;
+			char c = '\0';
+			List<String> nameValues = new ArrayList<String>();
+			while (startIndex < dn.length()) {
+				int endIndex;
+				for (endIndex = startIndex; endIndex < dn.length(); endIndex++) {
+					c = dn.charAt(endIndex);
+					if (c == ',' || c == '+') {
+						break;
+					}
+					if (c == '\\') {
+						endIndex++; // skip the escaped char
+					}
+				}
+				if (endIndex > dn.length()) {
+					throw new IllegalArgumentException("unterminated escape " + dn);
+				}
+				nameValues.add(dn.substring(startIndex, endIndex));
+				if (c != '+') {
+					rdn.add(nameValues);
+					if (endIndex != dn.length()) {
+						nameValues = new ArrayList<String>();
+					} else {
+						nameValues = null;
+					}
+				}
+				startIndex = endIndex + 1;
+			}
+			if (nameValues != null) {
+				throw new IllegalArgumentException("improperly terminated DN " + dn);
+			}
+		}
+
+		/**
+		 * This method will return an 'index' which points to a non-wildcard DN
+		 * or the end-of-list.
+		 */
+		private static int skipWildCards(List<Object> dnChainPattern, int dnChainPatternIndex) {
+			int i;
+			for (i = dnChainPatternIndex; i < dnChainPattern.size(); i++) {
+				Object dnPattern = dnChainPattern.get(i);
+				if (dnPattern instanceof String) {
+					if (!dnPattern.equals(STAR_WILDCARD) && !dnPattern.equals(MINUS_WILDCARD)) {
+						throw new IllegalArgumentException("expected wildcard in DN pattern");
+					}
+					// otherwise continue skipping over wild cards
+				} else {
+					if (dnPattern instanceof List<?>) {
+						// if its a list then we have our 'non-wildcard' DN
+						break;
+					} else {
+						// unknown member of the DNChainPattern
+						throw new IllegalArgumentException("expected String or List in DN Pattern");
+					}
+				}
+			}
+			// i either points to end-of-list, or to the first
+			// non-wildcard pattern after dnChainPatternIndex
+			return i;
+		}
+
+		/**
+		 * recursively attempt to match the DNChain, and the DNChainPattern
+		 * where DNChain is of the format: "DN;DN;DN;" and DNChainPattern is of
+		 * the format: "DNPattern;*;DNPattern" (or combinations of this)
+		 */
+		private static boolean dnChainMatch(List<Object> dnChain, int dnChainIndex, List<Object> dnChainPattern, int dnChainPatternIndex) throws IllegalArgumentException {
+			if (dnChainIndex >= dnChain.size()) {
+				return false;
+			}
+			if (dnChainPatternIndex >= dnChainPattern.size()) {
+				return false;
+			}
+			// check to see what the pattern starts with
+			Object dnPattern = dnChainPattern.get(dnChainPatternIndex);
+			if (dnPattern instanceof String) {
+				if (!dnPattern.equals(STAR_WILDCARD) && !dnPattern.equals(MINUS_WILDCARD)) {
+					throw new IllegalArgumentException("expected wildcard in DN pattern");
+				}
+				// here we are processing a wild card as the first DN
+				// skip all wildcard DN's
+				if (dnPattern.equals(MINUS_WILDCARD)) {
+					dnChainPatternIndex = skipWildCards(dnChainPattern, dnChainPatternIndex);
+				} else {
+					dnChainPatternIndex++; // only skip the '*' wildcard
+				}
+				if (dnChainPatternIndex >= dnChainPattern.size()) {
+					// return true iff the wild card is '-' or if we are at the
+					// end of the chain
+					return dnPattern.equals(MINUS_WILDCARD) ? true : dnChain.size() - 1 == dnChainIndex;
+				}
+				//
+				// we will now recursively call to see if the rest of the
+				// DNChainPattern matches increasingly smaller portions of the
+				// rest of the DNChain
+				//
+				if (dnPattern.equals(STAR_WILDCARD)) {
+					// '*' option: only wildcard on 0 or 1
+					return dnChainMatch(dnChain, dnChainIndex, dnChainPattern, dnChainPatternIndex) || dnChainMatch(dnChain, dnChainIndex + 1, dnChainPattern, dnChainPatternIndex);
+				}
+				for (int i = dnChainIndex; i < dnChain.size(); i++) {
+					// '-' option: wildcard 0 or more
+					if (dnChainMatch(dnChain, i, dnChainPattern, dnChainPatternIndex)) {
+						return true;
+					}
+				}
+				// if we are here, then we didn't find a match.. fall through to
+				// failure
+			} else {
+				if (dnPattern instanceof List<?>) {
+					// here we have to do a deeper check for each DN in the
+					// pattern until we hit a wild card
+					do {
+						if (!dnmatch((List<?>) dnChain.get(dnChainIndex), (List<?>) dnPattern)) {
+							return false;
+						}
+						// go to the next set of DN's in both chains
+						dnChainIndex++;
+						dnChainPatternIndex++;
+						// if we finished the pattern then it all matched
+						if ((dnChainIndex >= dnChain.size()) && (dnChainPatternIndex >= dnChainPattern.size())) {
+							return true;
+						}
+						// if the DN Chain is finished, but the pattern isn't
+						// finished then if the rest of the pattern is not
+						// wildcard then we are done
+						if (dnChainIndex >= dnChain.size()) {
+							dnChainPatternIndex = skipWildCards(dnChainPattern, dnChainPatternIndex);
+							// return TRUE iff the pattern index moved past the
+							// list-size (implying that the rest of the pattern
+							// is all wildcards)
+							return dnChainPatternIndex >= dnChainPattern.size();
+						}
+						// if the pattern finished, but the chain continues then
+						// we have a mis-match
+						if (dnChainPatternIndex >= dnChainPattern.size()) {
+							return false;
+						}
+						// get the next DN Pattern
+						dnPattern = dnChainPattern.get(dnChainPatternIndex);
+						if (dnPattern instanceof String) {
+							if (!dnPattern.equals(STAR_WILDCARD) && !dnPattern.equals(MINUS_WILDCARD)) {
+								throw new IllegalArgumentException("expected wildcard in DN pattern");
+							}
+							// if the next DN is a 'wildcard', then we will
+							// recurse
+							return dnChainMatch(dnChain, dnChainIndex, dnChainPattern, dnChainPatternIndex);
+						} else {
+							if (!(dnPattern instanceof List<?>)) {
+								throw new IllegalArgumentException("expected String or List in DN Pattern");
+							}
+						}
+						// if we are here, then we will just continue to the
+						// match the next set of DN's from the DNChain, and the
+						// DNChainPattern since both are lists
+					} while (true);
+					// should never reach here?
+				} else {
+					throw new IllegalArgumentException("expected String or List in DN Pattern");
+				}
+			}
+			// if we get here, the the default return is 'mis-match'
+			return false;
+		}
+
+		/**
+		 * Matches a distinguished name chain against a pattern of a
+		 * distinguished name chain.
+		 * 
+		 * @param dnChain
+		 * @param pattern the pattern of distinguished name (DN) chains to match
+		 *        against the dnChain. Wildcards ("*" or "-") can be used in
+		 *        three cases:
+		 *        <ol>
+		 *        <li>As a DN. In this case, the DN will consist of just the "*"
+		 *        or "-". When "*" is used it will match zero or one DNs. When
+		 *        "-" is used it will match zero or more DNs. For example,
+		 *        "cn=me,c=US;*;cn=you" will match
+		 *        "cn=me,c=US";cn=you" and "cn=me,c=US;cn=her;cn=you". The
+		 *        pattern "cn=me,c=US;-;cn=you" will match "cn=me,c=US";cn=you"
+		 *        and "cn=me,c=US;cn=her;cn=him;cn=you".
+		 *        <li>As a DN prefix. In this case, the DN must start with "*,".
+		 *        The wild card will match zero or more RDNs at the start of a
+		 *        DN. For example, "*,cn=me,c=US;cn=you" will match
+		 *        "cn=me,c=US";cn=you" and
+		 *        "ou=my org unit,o=my org,cn=me,c=US;cn=you"</li>
+		 *        <li>As a value. In this case the value of a name value pair in
+		 *        an RDN will be a "*". The wildcard will match any value for
+		 *        the given name. For example, "cn=*,c=US;cn=you" will match
+		 *        "cn=me,c=US";cn=you" and "cn=her,c=US;cn=you", but it will not
+		 *        match "ou=my org unit,c=US;cn=you". If the wildcard does not
+		 *        occur by itself in the value, it will not be used as a
+		 *        wildcard. In other words, "cn=m*,c=US;cn=you" represents the
+		 *        common name of "m*" not any common name starting with "m".</li>
+		 *        </ol>
+		 * @return true if dnChain matches the pattern.
+		 * @throws IllegalArgumentException
+		 */
+		static boolean match(String pattern, List<String> dnChain) {
+			List<Object> parsedDNChain;
+			List<Object> parsedDNPattern;
+			try {
+				parsedDNChain = parseDNchain(dnChain);
+			} catch (RuntimeException e) {
+				IllegalArgumentException iae = new IllegalArgumentException("Invalid DN chain: " + toString(dnChain));
+				iae.initCause(e);
+				throw iae;
+			}
+			try {
+				parsedDNPattern = parseDNchainPattern(pattern);
+			} catch (RuntimeException e) {
+				IllegalArgumentException iae = new IllegalArgumentException("Invalid match pattern: " + pattern);
+				iae.initCause(e);
+				throw iae;
+			}
+			return dnChainMatch(parsedDNChain, 0, parsedDNPattern, 0);
+		}
+
+		private static String toString(List<?> dnChain) {
+			if (dnChain == null) {
+				return null;
+			}
+			StringBuffer sb = new StringBuffer();
+			for (Iterator<?> iChain = dnChain.iterator(); iChain.hasNext();) {
+				sb.append(iChain.next());
+				if (iChain.hasNext()) {
+					sb.append("; ");
+				}
+			}
+			return sb.toString();
+		}
+	}
+}
diff --git a/src/main/java/org/osgi/framework/InvalidSyntaxException.java b/src/main/java/org/osgi/framework/InvalidSyntaxException.java
new file mode 100644
index 0000000..e2296c0
--- /dev/null
+++ b/src/main/java/org/osgi/framework/InvalidSyntaxException.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) OSGi Alliance (2000, 2012). All Rights Reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.framework;
+
+/**
+ * A Framework exception used to indicate that a filter string has an invalid
+ * syntax.
+ * 
+ * <p>
+ * An {@code InvalidSyntaxException} object indicates that a filter string
+ * parameter has an invalid syntax and cannot be parsed. See {@link Filter} for
+ * a description of the filter string syntax.
+ * 
+ * <p>
+ * This exception conforms to the general purpose exception chaining mechanism.
+ * 
+ * @version $Id: 8820ca2db85b557cef8da09ee861249dfb5ee914 $
+ */
+
+public class InvalidSyntaxException extends Exception {
+	static final long		serialVersionUID	= -4295194420816491875L;
+	/**
+	 * The invalid filter string.
+	 */
+	private final String	filter;
+
+	/**
+	 * Creates an exception of type {@code InvalidSyntaxException}.
+	 * 
+	 * <p>
+	 * This method creates an {@code InvalidSyntaxException} object with the
+	 * specified message and the filter string which generated the exception.
+	 * 
+	 * @param msg The message.
+	 * @param filter The invalid filter string.
+	 */
+	public InvalidSyntaxException(String msg, String filter) {
+		super(message(msg, filter));
+		this.filter = filter;
+	}
+
+	/**
+	 * Creates an exception of type {@code InvalidSyntaxException}.
+	 * 
+	 * <p>
+	 * This method creates an {@code InvalidSyntaxException} object with the
+	 * specified message and the filter string which generated the exception.
+	 * 
+	 * @param msg The message.
+	 * @param filter The invalid filter string.
+	 * @param cause The cause of this exception.
+	 * @since 1.3
+	 */
+	public InvalidSyntaxException(String msg, String filter, Throwable cause) {
+		super(message(msg, filter), cause);
+		this.filter = filter;
+	}
+
+	/**
+	 * Return message string for super constructor.
+	 */
+	private static String message(String msg, String filter) {
+		if ((msg == null) || (filter == null) || msg.indexOf(filter) >= 0) {
+			return msg;
+		}
+		return msg + ": " + filter;
+	}
+
+	/**
+	 * Returns the filter string that generated the
+	 * {@code InvalidSyntaxException} object.
+	 * 
+	 * @return The invalid filter string.
+	 * @see BundleContext#getServiceReferences(Class, String)
+	 * @see BundleContext#getServiceReferences(String, String)
+	 * @see BundleContext#addServiceListener(ServiceListener,String)
+	 */
+	public String getFilter() {
+		return filter;
+	}
+
+	/**
+	 * Returns the cause of this exception or {@code null} if no cause was set.
+	 * 
+	 * @return The cause of this exception or {@code null} if no cause was set.
+	 * @since 1.3
+	 */
+	public Throwable getCause() {
+		return super.getCause();
+	}
+
+	/**
+	 * Initializes the cause of this exception to the specified value.
+	 * 
+	 * @param cause The cause of this exception.
+	 * @return This exception.
+	 * @throws IllegalArgumentException If the specified cause is this
+	 *         exception.
+	 * @throws IllegalStateException If the cause of this exception has already
+	 *         been set.
+	 * @since 1.3
+	 */
+	public Throwable initCause(Throwable cause) {
+		return super.initCause(cause);
+	}
+}
diff --git a/src/main/java/org/osgi/framework/PackagePermission.java b/src/main/java/org/osgi/framework/PackagePermission.java
new file mode 100644
index 0000000..62d0d8d
--- /dev/null
+++ b/src/main/java/org/osgi/framework/PackagePermission.java
@@ -0,0 +1,777 @@
+/*
+ * Copyright (c) OSGi Alliance (2000, 2012). All Rights Reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.framework;
+
+import java.io.IOException;
+import java.io.NotSerializableException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.ObjectStreamField;
+import java.security.AccessController;
+import java.security.BasicPermission;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A bundle's authority to import or export a package.
+ * 
+ * <p>
+ * A package is a dot-separated string that defines a fully qualified Java
+ * package.
+ * <p>
+ * For example:
+ * 
+ * <pre>
+ * org.osgi.service.http
+ * </pre>
+ * 
+ * <p>
+ * {@code PackagePermission} has three actions: {@code exportonly},
+ * {@code import} and {@code export}. The {@code export} action, which is
+ * deprecated, implies the {@code import} action.
+ * 
+ * @ThreadSafe
+ * @version $Id: e993fbc36b6bff84182a8594af5af3cad8c4e2a3 $
+ */
+
+public final class PackagePermission extends BasicPermission {
+	static final long								serialVersionUID	= -5107705877071099135L;
+
+	/**
+	 * The action string {@code export}. The {@code export} action implies the
+	 * {@code import} action.
+	 * 
+	 * @deprecated Since 1.5. Use {@code exportonly} instead.
+	 */
+	public final static String						EXPORT				= "export";
+
+	/**
+	 * The action string {@code exportonly}. The {@code exportonly} action does
+	 * not imply the {@code import} action.
+	 * 
+	 * @since 1.5
+	 */
+	public final static String						EXPORTONLY			= "exportonly";
+
+	/**
+	 * The action string {@code import}.
+	 */
+	public final static String						IMPORT				= "import";
+
+	private final static int						ACTION_EXPORT		= 0x00000001;
+	private final static int						ACTION_IMPORT		= 0x00000002;
+	private final static int						ACTION_ALL			= ACTION_EXPORT | ACTION_IMPORT;
+	final static int								ACTION_NONE			= 0;
+
+	/**
+	 * The actions mask.
+	 */
+	transient int									action_mask;
+
+	/**
+	 * The actions in canonical form.
+	 * 
+	 * @serial
+	 */
+	private volatile String							actions				= null;
+
+	/**
+	 * The bundle used by this PackagePermission.
+	 */
+	transient final Bundle							bundle;
+
+	/**
+	 * If this PackagePermission was constructed with a filter, this holds a
+	 * Filter matching object used to evaluate the filter in implies.
+	 */
+	transient Filter								filter;
+
+	/**
+	 * This map holds the properties of the permission, used to match a filter
+	 * in implies. This is not initialized until necessary, and then cached in
+	 * this object.
+	 */
+	private transient volatile Map<String, Object>	properties;
+
+	/**
+	 * Creates a new {@code PackagePermission} object.
+	 * 
+	 * <p>
+	 * The name is specified as a normal Java package name: a dot-separated
+	 * string. Wildcards may be used.
+	 * 
+	 * <pre>
+	 * name ::= <package name> | <package name ending in ".*"> | *
+	 * </pre>
+	 * 
+	 * Examples:
+	 * 
+	 * <pre>
+	 * org.osgi.service.http
+	 * javax.servlet.*
+	 * *
+	 * </pre>
+	 * 
+	 * For the {@code import} action, the name can also be a filter expression.
+	 * The filter gives access to the following attributes:
+	 * <ul>
+	 * <li>signer - A Distinguished Name chain used to sign the exporting
+	 * bundle. Wildcards in a DN are not matched according to the filter string
+	 * rules, but according to the rules defined for a DN chain.</li>
+	 * <li>location - The location of the exporting bundle.</li>
+	 * <li>id - The bundle ID of the exporting bundle.</li>
+	 * <li>name - The symbolic name of the exporting bundle.</li>
+	 * <li>package.name - The name of the requested package.</li>
+	 * </ul>
+	 * Filter attribute names are processed in a case sensitive manner.
+	 * 
+	 * <p>
+	 * Package Permissions are granted over all possible versions of a package.
+	 * 
+	 * A bundle that needs to export a package must have the appropriate
+	 * {@code PackagePermission} for that package; similarly, a bundle that
+	 * needs to import a package must have the appropriate
+	 * {@code PackagePermssion} for that package.
+	 * <p>
+	 * Permission is granted for both classes and resources.
+	 * 
+	 * @param name Package name or filter expression. A filter expression can
+	 *        only be specified if the specified action is {@code import}.
+	 * @param actions {@code exportonly},{@code import} (canonical order).
+	 * @throws IllegalArgumentException If the specified name is a filter
+	 *         expression and either the specified action is not {@code import}
+	 *         or the filter has an invalid syntax.
+	 */
+	public PackagePermission(String name, String actions) {
+		this(name, parseActions(actions));
+		if ((filter != null) && ((action_mask & ACTION_ALL) != ACTION_IMPORT)) {
+			throw new IllegalArgumentException("invalid action string for filter expression");
+		}
+	}
+
+	/**
+	 * Creates a new requested {@code PackagePermission} object to be used by
+	 * code that must perform {@code checkPermission} for the {@code import}
+	 * action. {@code PackagePermission} objects created with this constructor
+	 * cannot be added to a {@code PackagePermission} permission collection.
+	 * 
+	 * @param name The name of the requested package to import.
+	 * @param exportingBundle The bundle exporting the requested package.
+	 * @param actions The action {@code import}.
+	 * @throws IllegalArgumentException If the specified action is not
+	 *         {@code import} or the name is a filter expression.
+	 * @since 1.5
+	 */
+	public PackagePermission(String name, Bundle exportingBundle, String actions) {
+		super(name);
+		setTransients(name, parseActions(actions));
+		this.bundle = exportingBundle;
+		if (exportingBundle == null) {
+			throw new IllegalArgumentException("bundle must not be null");
+		}
+		if (filter != null) {
+			throw new IllegalArgumentException("invalid name");
+		}
+		if ((action_mask & ACTION_ALL) != ACTION_IMPORT) {
+			throw new IllegalArgumentException("invalid action string");
+		}
+	}
+
+	/**
+	 * Package private constructor used by PackagePermissionCollection.
+	 * 
+	 * @param name package name
+	 * @param mask action mask
+	 */
+	PackagePermission(String name, int mask) {
+		super(name);
+		setTransients(name, mask);
+		this.bundle = null;
+	}
+
+	/**
+	 * Called by constructors and when deserialized.
+	 * 
+	 * @param mask action mask
+	 */
+	private void setTransients(String name, int mask) {
+		if ((mask == ACTION_NONE) || ((mask & ACTION_ALL) != mask)) {
+			throw new IllegalArgumentException("invalid action string");
+		}
+		action_mask = mask;
+		filter = parseFilter(name);
+	}
+
+	/**
+	 * Parse action string into action mask.
+	 * 
+	 * @param actions Action string.
+	 * @return action mask.
+	 */
+	private static int parseActions(String actions) {
+		boolean seencomma = false;
+
+		int mask = ACTION_NONE;
+
+		if (actions == null) {
+			return mask;
+		}
+
+		char[] a = actions.toCharArray();
+
+		int i = a.length - 1;
+		if (i < 0)
+			return mask;
+
+		while (i != -1) {
+			char c;
+
+			// skip whitespace
+			while ((i != -1) && ((c = a[i]) == ' ' || c == '\r' || c == '\n' || c == '\f' || c == '\t'))
+				i--;
+
+			// check for the known strings
+			int matchlen;
+
+			if (i >= 5 && (a[i - 5] == 'i' || a[i - 5] == 'I')
+					&& (a[i - 4] == 'm' || a[i - 4] == 'M')
+					&& (a[i - 3] == 'p' || a[i - 3] == 'P')
+					&& (a[i - 2] == 'o' || a[i - 2] == 'O')
+					&& (a[i - 1] == 'r' || a[i - 1] == 'R')
+					&& (a[i] == 't' || a[i] == 'T')) {
+				matchlen = 6;
+				mask |= ACTION_IMPORT;
+
+			} else
+				if (i >= 5 && (a[i - 5] == 'e' || a[i - 5] == 'E')
+						&& (a[i - 4] == 'x' || a[i - 4] == 'X')
+						&& (a[i - 3] == 'p' || a[i - 3] == 'P')
+						&& (a[i - 2] == 'o' || a[i - 2] == 'O')
+						&& (a[i - 1] == 'r' || a[i - 1] == 'R')
+						&& (a[i] == 't' || a[i] == 'T')) {
+					matchlen = 6;
+					mask |= ACTION_EXPORT | ACTION_IMPORT;
+
+				} else {
+					if (i >= 9 && (a[i - 9] == 'e' || a[i - 9] == 'E')
+							&& (a[i - 8] == 'x' || a[i - 8] == 'X')
+							&& (a[i - 7] == 'p' || a[i - 7] == 'P')
+							&& (a[i - 6] == 'o' || a[i - 6] == 'O')
+							&& (a[i - 5] == 'r' || a[i - 5] == 'R')
+							&& (a[i - 4] == 't' || a[i - 4] == 'T')
+							&& (a[i - 3] == 'o' || a[i - 3] == 'O')
+							&& (a[i - 2] == 'n' || a[i - 2] == 'N')
+							&& (a[i - 1] == 'l' || a[i - 1] == 'L')
+							&& (a[i] == 'y' || a[i] == 'Y')) {
+						matchlen = 10;
+						mask |= ACTION_EXPORT;
+
+					} else {
+						// parse error
+						throw new IllegalArgumentException("invalid permission: " + actions);
+					}
+				}
+
+			// make sure we didn't just match the tail of a word
+			// like "ackbarfimport". Also, skip to the comma.
+			seencomma = false;
+			while (i >= matchlen && !seencomma) {
+				switch (a[i - matchlen]) {
+					case ',' :
+						seencomma = true;
+						/* FALLTHROUGH */
+					case ' ' :
+					case '\r' :
+					case '\n' :
+					case '\f' :
+					case '\t' :
+						break;
+					default :
+						throw new IllegalArgumentException("invalid permission: " + actions);
+				}
+				i--;
+			}
+
+			// point i at the location of the comma minus one (or -1).
+			i -= matchlen;
+		}
+
+		if (seencomma) {
+			throw new IllegalArgumentException("invalid permission: " + actions);
+		}
+
+		return mask;
+	}
+
+	/**
+	 * Parse filter string into a Filter object.
+	 * 
+	 * @param filterString The filter string to parse.
+	 * @return a Filter for this bundle. If the specified filterString is not a
+	 *         filter expression, then {@code null} is returned.
+	 * @throws IllegalArgumentException If the filter syntax is invalid.
+	 */
+	private static Filter parseFilter(String filterString) {
+		filterString = filterString.trim();
+		if (filterString.charAt(0) != '(') {
+			return null;
+		}
+
+		try {
+			return FrameworkUtil.createFilter(filterString);
+		} catch (InvalidSyntaxException e) {
+			IllegalArgumentException iae = new IllegalArgumentException("invalid filter");
+			iae.initCause(e);
+			throw iae;
+		}
+	}
+
+	/**
+	 * Determines if the specified permission is implied by this object.
+	 * 
+	 * <p>
+	 * This method checks that the package name of the target is implied by the
+	 * package name of this object. The list of {@code PackagePermission}
+	 * actions must either match or allow for the list of the target object to
+	 * imply the target {@code PackagePermission} action.
+	 * <p>
+	 * The permission to export a package implies the permission to import the
+	 * named package.
+	 * 
+	 * <pre>
+	 * x.y.*,"export" -> x.y.z,"export" is true
+	 * *,"import" -> x.y, "import"      is true
+	 * *,"export" -> x.y, "import"      is true
+	 * x.y,"export" -> x.y.z, "export"  is false
+	 * </pre>
+	 * 
+	 * @param p The requested permission.
+	 * @return {@code true} if the specified permission is implied by this
+	 *         object; {@code false} otherwise.
+	 */
+	public boolean implies(Permission p) {
+		if (!(p instanceof PackagePermission)) {
+			return false;
+		}
+		PackagePermission requested = (PackagePermission) p;
+		if (bundle != null) {
+			return false;
+		}
+		// if requested permission has a filter, then it is an invalid argument
+		if (requested.filter != null) {
+			return false;
+		}
+		return implies0(requested, ACTION_NONE);
+	}
+
+	/**
+	 * Internal implies method. Used by the implies and the permission
+	 * collection implies methods.
+	 * 
+	 * @param requested The requested PackagePermission which has already be
+	 *        validated as a proper argument. The requested PackagePermission
+	 *        must not have a filter expression.
+	 * @param effective The effective actions with which to start.
+	 * @return {@code true} if the specified permission is implied by this
+	 *         object; {@code false} otherwise.
+	 */
+	boolean implies0(PackagePermission requested, int effective) {
+		/* check actions first - much faster */
+		effective |= action_mask;
+		final int desired = requested.action_mask;
+		if ((effective & desired) != desired) {
+			return false;
+		}
+		/* Get filter if any */
+		Filter f = filter;
+		if (f == null) {
+			return super.implies(requested);
+		}
+		return f.matches(requested.getProperties());
+	}
+
+	/**
+	 * Returns the canonical string representation of the
+	 * {@code PackagePermission} actions.
+	 * 
+	 * <p>
+	 * Always returns present {@code PackagePermission} actions in the following
+	 * order: {@code EXPORTONLY},{@code IMPORT}.
+	 * 
+	 * @return Canonical string representation of the {@code PackagePermission}
+	 *         actions.
+	 */
+	public String getActions() {
+		String result = actions;
+		if (result == null) {
+			StringBuffer sb = new StringBuffer();
+			boolean comma = false;
+
+			int mask = action_mask;
+			if ((mask & ACTION_EXPORT) == ACTION_EXPORT) {
+				sb.append(EXPORTONLY);
+				comma = true;
+			}
+
+			if ((mask & ACTION_IMPORT) == ACTION_IMPORT) {
+				if (comma)
+					sb.append(',');
+				sb.append(IMPORT);
+			}
+
+			actions = result = sb.toString();
+		}
+		return result;
+	}
+
+	/**
+	 * Returns a new {@code PermissionCollection} object suitable for storing
+	 * {@code PackagePermission} objects.
+	 * 
+	 * @return A new {@code PermissionCollection} object.
+	 */
+	public PermissionCollection newPermissionCollection() {
+		return new PackagePermissionCollection();
+	}
+
+	/**
+	 * Determines the equality of two {@code PackagePermission} objects.
+	 * 
+	 * This method checks that specified package has the same package name and
+	 * {@code PackagePermission} actions as this {@code PackagePermission}
+	 * object.
+	 * 
+	 * @param obj The object to test for equality with this
+	 *        {@code PackagePermission} object.
+	 * @return {@code true} if {@code obj} is a {@code PackagePermission}, and
+	 *         has the same package name and actions as this
+	 *         {@code PackagePermission} object; {@code false} otherwise.
+	 */
+	public boolean equals(Object obj) {
+		if (obj == this) {
+			return true;
+		}
+
+		if (!(obj instanceof PackagePermission)) {
+			return false;
+		}
+
+		PackagePermission pp = (PackagePermission) obj;
+
+		return (action_mask == pp.action_mask) && getName().equals(pp.getName()) && ((bundle == pp.bundle) || ((bundle != null) && bundle.equals(pp.bundle)));
+	}
+
+	/**
+	 * Returns the hash code value for this object.
+	 * 
+	 * @return A hash code value for this object.
+	 */
+	public int hashCode() {
+		int h = 31 * 17 + getName().hashCode();
+		h = 31 * h + getActions().hashCode();
+		if (bundle != null) {
+			h = 31 * h + bundle.hashCode();
+		}
+		return h;
+	}
+
+	/**
+	 * WriteObject is called to save the state of this permission object to a
+	 * stream. The actions are serialized, and the superclass takes care of the
+	 * name.
+	 */
+	private synchronized void writeObject(java.io.ObjectOutputStream s) throws IOException {
+		if (bundle != null) {
+			throw new NotSerializableException("cannot serialize");
+		}
+		// Write out the actions. The superclass takes care of the name
+		// call getActions to make sure actions field is initialized
+		if (actions == null)
+			getActions();
+		s.defaultWriteObject();
+	}
+
+	/**
+	 * readObject is called to restore the state of this permission from a
+	 * stream.
+	 */
+	private synchronized void readObject(java.io.ObjectInputStream s) throws IOException, ClassNotFoundException {
+		// Read in the action, then initialize the rest
+		s.defaultReadObject();
+		setTransients(getName(), parseActions(actions));
+	}
+
+	/**
+	 * Called by {@code <@link PackagePermission#implies(Permission)>}. This
+	 * method is only called on a requested permission which cannot have a
+	 * filter set.
+	 * 
+	 * @return a map of properties for this permission.
+	 */
+	private Map<String, Object> getProperties() {
+		Map<String, Object> result = properties;
+		if (result != null) {
+			return result;
+		}
+		final Map<String, Object> map = new HashMap<String, Object>(5);
+		map.put("package.name", getName());
+		if (bundle != null) {
+			AccessController.doPrivileged(new PrivilegedAction<Object>() {
+				public Object run() {
+					map.put("id", new Long(bundle.getBundleId()));
+					map.put("location", bundle.getLocation());
+					String name = bundle.getSymbolicName();
+					if (name != null) {
+						map.put("name", name);
+					}
+					SignerProperty signer = new SignerProperty(bundle);
+					if (signer.isBundleSigned()) {
+						map.put("signer", signer);
+					}
+					return null;
+				}
+			});
+		}
+		return properties = map;
+	}
+}
+
+/**
+ * Stores a set of {@code PackagePermission} permissions.
+ * 
+ * @see java.security.Permission
+ * @see java.security.Permissions
+ * @see java.security.PermissionCollection
+ */
+
+final class PackagePermissionCollection extends PermissionCollection {
+	static final long									serialVersionUID	= -3350758995234427603L;
+	/**
+	 * Table of permissions with names.
+	 * 
+	 * @GuardedBy this
+	 */
+	private transient Map<String, PackagePermission>	permissions;
+
+	/**
+	 * Boolean saying if "*" is in the collection.
+	 * 
+	 * @serial
+	 * @GuardedBy this
+	 */
+	private boolean										all_allowed;
+
+	/**
+	 * Table of permissions with filter expressions.
+	 * 
+	 * @serial
+	 * @GuardedBy this
+	 */
+	private Map<String, PackagePermission>				filterPermissions;
+
+	/**
+	 * Create an empty PackagePermissions object.
+	 */
+	public PackagePermissionCollection() {
+		permissions = new HashMap<String, PackagePermission>();
+		all_allowed = false;
+	}
+
+	/**
+	 * Adds a permission to this permission collection.
+	 * 
+	 * @param permission The {@code PackagePermission} object to add.
+	 * @throws IllegalArgumentException If the specified permission is not a
+	 *         {@code PackagePermission} instance or was constructed with a
+	 *         Bundle object.
+	 * @throws SecurityException If this {@code PackagePermissionCollection}
+	 *         object has been marked read-only.
+	 */
+	public void add(final Permission permission) {
+		if (!(permission instanceof PackagePermission)) {
+			throw new IllegalArgumentException("invalid permission: " + permission);
+		}
+		if (isReadOnly()) {
+			throw new SecurityException("attempt to add a Permission to a " + "readonly PermissionCollection");
+		}
+
+		final PackagePermission pp = (PackagePermission) permission;
+		if (pp.bundle != null) {
+			throw new IllegalArgumentException("cannot add to collection: " + pp);
+		}
+
+		final String name = pp.getName();
+		final Filter f = pp.filter;
+		synchronized (this) {
+			/* select the bucket for the permission */
+			Map<String, PackagePermission> pc;
+			if (f != null) {
+				pc = filterPermissions;
+				if (pc == null) {
+					filterPermissions = pc = new HashMap<String, PackagePermission>();
+				}
+			} else {
+				pc = permissions;
+			}
+
+			final PackagePermission existing = pc.get(name);
+			if (existing != null) {
+				final int oldMask = existing.action_mask;
+				final int newMask = pp.action_mask;
+				if (oldMask != newMask) {
+					pc.put(name, new PackagePermission(name, oldMask | newMask));
+
+				}
+			} else {
+				pc.put(name, pp);
+			}
+
+			if (!all_allowed) {
+				if (name.equals("*")) {
+					all_allowed = true;
+				}
+			}
+		}
+	}
+
+	/**
+	 * Determines if the specified permissions implies the permissions expressed
+	 * in {@code permission}.
+	 * 
+	 * @param permission The Permission object to compare with this
+	 *        {@code PackagePermission} object.
+	 * @return {@code true} if {@code permission} is a proper subset of a
+	 *         permission in the set; {@code false} otherwise.
+	 */
+	public boolean implies(final Permission permission) {
+		if (!(permission instanceof PackagePermission)) {
+			return false;
+		}
+		final PackagePermission requested = (PackagePermission) permission;
+		/* if requested permission has a filter, then it is an invalid argument */
+		if (requested.filter != null) {
+			return false;
+		}
+		String requestedName = requested.getName();
+		final int desired = requested.action_mask;
+		int effective = PackagePermission.ACTION_NONE;
+
+		Collection<PackagePermission> perms;
+		synchronized (this) {
+			Map<String, PackagePermission> pc = permissions;
+			PackagePermission pp;
+			/* short circuit if the "*" Permission was added */
+			if (all_allowed) {
+				pp = pc.get("*");
+				if (pp != null) {
+					effective |= pp.action_mask;
+					if ((effective & desired) == desired) {
+						return true;
+					}
+				}
+			}
+			/*
+			 * strategy: Check for full match first. Then work our way up the
+			 * name looking for matches on a.b.*
+			 */
+			pp = pc.get(requestedName);
+			if (pp != null) {
+				/* we have a direct hit! */
+				effective |= pp.action_mask;
+				if ((effective & desired) == desired) {
+					return true;
+				}
+			}
+			/* work our way up the tree... */
+			int last;
+			int offset = requestedName.length() - 1;
+			while ((last = requestedName.lastIndexOf(".", offset)) != -1) {
+				requestedName = requestedName.substring(0, last + 1) + "*";
+				pp = pc.get(requestedName);
+				if (pp != null) {
+					effective |= pp.action_mask;
+					if ((effective & desired) == desired) {
+						return true;
+					}
+				}
+				offset = last - 1;
+			}
+			/*
+			 * we don't have to check for "*" as it was already checked before
+			 * we were called.
+			 */
+			pc = filterPermissions;
+			if (pc == null) {
+				return false;
+			}
+			perms = pc.values();
+		}
+		/* iterate one by one over filteredPermissions */
+		for (PackagePermission perm : perms) {
+			if (perm.implies0(requested, effective)) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+	/**
+	 * Returns an enumeration of all {@code PackagePermission} objects in the
+	 * container.
+	 * 
+	 * @return Enumeration of all {@code PackagePermission} objects.
+	 */
+	public synchronized Enumeration<Permission> elements() {
+		List<Permission> all = new ArrayList<Permission>(permissions.values());
+		Map<String, PackagePermission> pc = filterPermissions;
+		if (pc != null) {
+			all.addAll(pc.values());
+		}
+		return Collections.enumeration(all);
+	}
+
+	/* serialization logic */
+	private static final ObjectStreamField[]	serialPersistentFields	= {new ObjectStreamField("permissions", Hashtable.class), new ObjectStreamField("all_allowed", Boolean.TYPE),
+			new ObjectStreamField("filterPermissions", HashMap.class)	};
+
+	private synchronized void writeObject(ObjectOutputStream out) throws IOException {
+		Hashtable<String, PackagePermission> hashtable = new Hashtable<String, PackagePermission>(permissions);
+		ObjectOutputStream.PutField pfields = out.putFields();
+		pfields.put("permissions", hashtable);
+		pfields.put("all_allowed", all_allowed);
+		pfields.put("filterPermissions", filterPermissions);
+		out.writeFields();
+	}
+
+	private synchronized void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
+		ObjectInputStream.GetField gfields = in.readFields();
+		Hashtable<String, PackagePermission> hashtable = (Hashtable<String, PackagePermission>) gfields.get("permissions", null);
+		permissions = new HashMap<String, PackagePermission>(hashtable);
+		all_allowed = gfields.get("all_allowed", false);
+		HashMap<String, PackagePermission> fp = (HashMap<String, PackagePermission>) gfields.get("filterPermissions", null);
+		filterPermissions = fp;
+	}
+}
diff --git a/src/main/java/org/osgi/framework/ServiceEvent.java b/src/main/java/org/osgi/framework/ServiceEvent.java
new file mode 100644
index 0000000..2a59fe8
--- /dev/null
+++ b/src/main/java/org/osgi/framework/ServiceEvent.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) OSGi Alliance (2000, 2012). All Rights Reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.framework;
+
+import java.util.Dictionary;
+import java.util.EventObject;
+
+/**
+ * An event from the Framework describing a service lifecycle change.
+ * <p>
+ * {@code ServiceEvent} objects are delivered to {@code ServiceListener}s and
+ * {@code AllServiceListener}s when a change occurs in this service's lifecycle.
+ * A type code is used to identify the event type for future extendability.
+ * 
+ * <p>
+ * OSGi Alliance reserves the right to extend the set of types.
+ * 
+ * @Immutable
+ * @see ServiceListener
+ * @see AllServiceListener
+ * @version $Id: 49e34e0ad5564d6f4ca0ab0053b272c22b9fb917 $
+ */
+
+public class ServiceEvent extends EventObject {
+	static final long					serialVersionUID	= 8792901483909409299L;
+	/**
+	 * Reference to the service that had a change occur in its lifecycle.
+	 */
+	private final ServiceReference<?>	reference;
+
+	/**
+	 * Type of service lifecycle change.
+	 */
+	private final int					type;
+
+	/**
+	 * This service has been registered.
+	 * <p>
+	 * This event is synchronously delivered <strong>after</strong> the service
+	 * has been registered with the Framework.
+	 * 
+	 * @see BundleContext#registerService(String[],Object,Dictionary)
+	 */
+	public final static int				REGISTERED			= 0x00000001;
+
+	/**
+	 * The properties of a registered service have been modified.
+	 * <p>
+	 * This event is synchronously delivered <strong>after</strong> the service
+	 * properties have been modified.
+	 * 
+	 * @see ServiceRegistration#setProperties(Dictionary)
+	 */
+	public final static int				MODIFIED			= 0x00000002;
+
+	/**
+	 * This service is in the process of being unregistered.
+	 * <p>
+	 * This event is synchronously delivered <strong>before</strong> the service
+	 * has completed unregistering.
+	 * 
+	 * <p>
+	 * If a bundle is using a service that is {@code UNREGISTERING}, the bundle
+	 * should release its use of the service when it receives this event. If the
+	 * bundle does not release its use of the service when it receives this
+	 * event, the Framework will automatically release the bundle's use of the
+	 * service while completing the service unregistration operation.
+	 * 
+	 * @see ServiceRegistration#unregister()
+	 * @see BundleContext#ungetService(ServiceReference)
+	 */
+	public final static int				UNREGISTERING		= 0x00000004;
+
+	/**
+	 * The properties of a registered service have been modified and the new
+	 * properties no longer match the listener's filter.
+	 * <p>
+	 * This event is synchronously delivered <strong>after</strong> the service
+	 * properties have been modified. This event is only delivered to listeners
+	 * which were added with a non-{@code null} filter where the filter matched
+	 * the service properties prior to the modification but the filter does not
+	 * match the modified service properties.
+	 * 
+	 * @see ServiceRegistration#setProperties(Dictionary)
+	 * @since 1.5
+	 */
+	public final static int				MODIFIED_ENDMATCH	= 0x00000008;
+
+	/**
+	 * Creates a new service event object.
+	 * 
+	 * @param type The event type.
+	 * @param reference A {@code ServiceReference} object to the service that
+	 *        had a lifecycle change.
+	 */
+	public ServiceEvent(int type, ServiceReference<?> reference) {
+		super(reference);
+		this.reference = reference;
+		this.type = type;
+	}
+
+	/**
+	 * Returns a reference to the service that had a change occur in its
+	 * lifecycle.
+	 * <p>
+	 * This reference is the source of the event.
+	 * 
+	 * @return Reference to the service that had a lifecycle change.
+	 */
+	public ServiceReference<?> getServiceReference() {
+		return reference;
+	}
+
+	/**
+	 * Returns the type of event. The event type values are:
+	 * <ul>
+	 * <li>{@link #REGISTERED}</li>
+	 * <li>{@link #MODIFIED}</li>
+	 * <li>{@link #MODIFIED_ENDMATCH}</li>
+	 * <li>{@link #UNREGISTERING}</li>
+	 * </ul>
+	 * 
+	 * @return Type of service lifecycle change.
+	 */
+
+	public int getType() {
+		return type;
+	}
+}
diff --git a/src/main/java/org/osgi/framework/ServiceException.java b/src/main/java/org/osgi/framework/ServiceException.java
new file mode 100644
index 0000000..de90784
--- /dev/null
+++ b/src/main/java/org/osgi/framework/ServiceException.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) OSGi Alliance (2007, 2012). All Rights Reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.framework;
+
+/**
+ * A service exception used to indicate that a service problem occurred.
+ * 
+ * <p>
+ * A {@code ServiceException} object is created by the Framework or service
+ * implementation to denote an exception condition in the service. A type code
+ * is used to identify the exception type for future extendability. Service
+ * implementations may also create subclasses of {@code ServiceException}. When
+ * subclassing, the subclass should set the type to {@link #SUBCLASSED} to
+ * indicate that {@code ServiceException} has been subclassed.
+ * 
+ * <p>
+ * This exception conforms to the general purpose exception chaining mechanism.
+ * 
+ * @version $Id: 9f763412635f59585bb615cbc449fc7ab72b7103 $
+ * @since 1.5
+ */
+
+public class ServiceException extends RuntimeException {
+	static final long		serialVersionUID	= 3038963223712959631L;
+
+	/**
+	 * Type of service exception.
+	 */
+	private final int		type;
+
+	/**
+	 * No exception type is unspecified.
+	 */
+	public static final int	UNSPECIFIED			= 0;
+	/**
+	 * The service has been unregistered.
+	 */
+	public static final int	UNREGISTERED		= 1;
+	/**
+	 * The service factory produced an invalid service object.
+	 */
+	public static final int	FACTORY_ERROR		= 2;
+	/**
+	 * The service factory threw an exception.
+	 */
+	public static final int	FACTORY_EXCEPTION	= 3;
+	/**
+	 * The exception is a subclass of ServiceException. The subclass should be
+	 * examined for the type of the exception.
+	 */
+	public static final int	SUBCLASSED			= 4;
+	/**
+	 * An error occurred invoking a remote service.
+	 */
+	public static final int	REMOTE				= 5;
+	/**
+	 * The service factory resulted in a recursive call to itself for the
+	 * requesting bundle.
+	 * 
+	 * @since 1.6
+	 */
+	public static final int	FACTORY_RECURSION	= 6;
+
+	/**
+	 * Creates a {@code ServiceException} with the specified message and
+	 * exception cause.
+	 * 
+	 * @param msg The associated message.
+	 * @param cause The cause of this exception.
+	 */
+	public ServiceException(String msg, Throwable cause) {
+		this(msg, UNSPECIFIED, cause);
+	}
+
+	/**
+	 * Creates a {@code ServiceException} with the specified message.
+	 * 
+	 * @param msg The message.
+	 */
+	public ServiceException(String msg) {
+		this(msg, UNSPECIFIED);
+	}
+
+	/**
+	 * Creates a {@code ServiceException} with the specified message, type and
+	 * exception cause.
+	 * 
+	 * @param msg The associated message.
+	 * @param type The type for this exception.
+	 * @param cause The cause of this exception.
+	 */
+	public ServiceException(String msg, int type, Throwable cause) {
+		super(msg, cause);
+		this.type = type;
+	}
+
+	/**
+	 * Creates a {@code ServiceException} with the specified message and type.
+	 * 
+	 * @param msg The message.
+	 * @param type The type for this exception.
+	 */
+	public ServiceException(String msg, int type) {
+		super(msg);
+		this.type = type;
+	}
+
+	/**
+	 * Returns the type for this exception or {@code UNSPECIFIED} if the type
+	 * was unspecified or unknown.
+	 * 
+	 * @return The type of this exception.
+	 */
+	public int getType() {
+		return type;
+	}
+}
diff --git a/src/main/java/org/osgi/framework/ServiceFactory.java b/src/main/java/org/osgi/framework/ServiceFactory.java
new file mode 100644
index 0000000..4b52fee
--- /dev/null
+++ b/src/main/java/org/osgi/framework/ServiceFactory.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) OSGi Alliance (2000, 2012). All Rights Reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.framework;
+
+/**
+ * Allows services to provide customized service objects in the OSGi
+ * environment.
+ * 
+ * <p>
+ * When registering a service, a {@code ServiceFactory} object can be used
+ * instead of a service object, so that the bundle developer can gain control of
+ * the specific service object granted to a bundle that is using the service.
+ * 
+ * <p>
+ * When this happens, the {@code BundleContext.getService(ServiceReference)}
+ * method calls the {@code ServiceFactory.getService} method to create a service
+ * object specifically for the requesting bundle. The service object returned by
+ * the {@code ServiceFactory} is cached by the Framework until the bundle
+ * releases its use of the service.
+ * 
+ * <p>
+ * When the bundle's use count for the service is decremented to zero (including
+ * the bundle stopping or the service being unregistered), the
+ * {@code ServiceFactory.ungetService} method is called.
+ * 
+ * <p>
+ * {@code ServiceFactory} objects are only used by the Framework and are not
+ * made available to other bundles in the OSGi environment. The Framework may
+ * concurrently call a {@code ServiceFactory}.
+ * 
+ * @param <S> Type of Service
+ * @see BundleContext#getService(ServiceReference)
+ * @ThreadSafe
+ * @version $Id: 535776e702ec5ace54f577218ff8f7920741558b $
+ */
+
+public interface ServiceFactory<S> {
+	/**
+	 * Creates a new service object.
+	 * 
+	 * <p>
+	 * The Framework invokes this method the first time the specified
+	 * {@code bundle} requests a service object using the
+	 * {@code BundleContext.getService(ServiceReference)} method. The service
+	 * factory can then return a specific service object for each bundle.
+	 * 
+	 * <p>
+	 * The Framework must check that the returned service object is valid. If
+	 * the returned service object is {@code null} or is not an
+	 * {@code instanceof} all the classes named when the service was registered,
+	 * a framework event of type {@link FrameworkEvent#ERROR} is fired
+	 * containing a service exception of type
+	 * {@link ServiceException#FACTORY_ERROR} and {@code null} is returned to
+	 * the bundle. If this method throws an exception, a framework event of type
+	 * {@link FrameworkEvent#ERROR} is fired containing a service exception of
+	 * type {@link ServiceException#FACTORY_EXCEPTION} with the thrown exception
+	 * as the cause and {@code null} is returned to the bundle. If this method
+	 * is recursively called for the specified bundle, a framework event of type
+	 * {@link FrameworkEvent#ERROR} is fired containing a service exception of
+	 * type {@link ServiceException#FACTORY_RECURSION} and {@code null} is
+	 * returned to the bundle.
+	 * 
+	 * <p>
+	 * The Framework caches the valid service object and will return the same
+	 * service object on any future call to {@code BundleContext.getService} for
+	 * the specified bundle. This means the Framework must not allow this method
+	 * to be concurrently called for the specified bundle.
+	 * 
+	 * @param bundle The bundle requesting the service.
+	 * @param registration The {@code ServiceRegistration} object for the
+	 *        requested service.
+	 * @return A service object that <strong>must</strong> be an instance of all
+	 *         the classes named when the service was registered.
+	 * @see BundleContext#getService(ServiceReference)
+	 */
+	public S getService(Bundle bundle, ServiceRegistration<S> registration);
+
+	/**
+	 * Releases a service object.
+	 * 
+	 * <p>
+	 * The Framework invokes this method when a service has been released by a
+	 * bundle. The service object may then be destroyed.
+	 * 
+	 * <p>
+	 * If this method throws an exception, a framework event of type
+	 * {@link FrameworkEvent#ERROR} is fired containing a service exception of
+	 * type {@link ServiceException#FACTORY_EXCEPTION} with the thrown exception
+	 * as the cause.
+	 * 
+	 * @param bundle The bundle releasing the service.
+	 * @param registration The {@code ServiceRegistration} object for the
+	 *        service being released.
+	 * @param service The service object returned by a previous call to the
+	 *        {@link #getService(Bundle, ServiceRegistration) getService}
+	 *        method.
+	 * @see BundleContext#ungetService(ServiceReference)
+	 */
+	public void ungetService(Bundle bundle, ServiceRegistration<S> registration, S service);
+}
diff --git a/src/main/java/org/osgi/framework/ServiceListener.java b/src/main/java/org/osgi/framework/ServiceListener.java
new file mode 100644
index 0000000..839c1bc
--- /dev/null
+++ b/src/main/java/org/osgi/framework/ServiceListener.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) OSGi Alliance (2000, 2012). All Rights Reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.framework;
+
+import java.util.EventListener;
+
+/**
+ * A {@code ServiceEvent} listener. {@code ServiceListener} is a listener
+ * interface that may be implemented by a bundle developer. When a
+ * {@code ServiceEvent} is fired, it is synchronously delivered to a
+ * {@code ServiceListener}. The Framework may deliver {@code ServiceEvent}
+ * objects to a {@code ServiceListener} out of order and may concurrently call
+ * and/or reenter a {@code ServiceListener}.
+ * 
+ * <p>
+ * A {@code ServiceListener} object is registered with the Framework using the
+ * {@code BundleContext.addServiceListener} method. {@code ServiceListener}
+ * objects are called with a {@code ServiceEvent} object when a service is
+ * registered, modified, or is in the process of unregistering.
+ * 
+ * <p>
+ * {@code ServiceEvent} object delivery to {@code ServiceListener} objects is
+ * filtered by the filter specified when the listener was registered. If the
+ * Java Runtime Environment supports permissions, then additional filtering is
+ * done. {@code ServiceEvent} objects are only delivered to the listener if the
+ * bundle which defines the listener object's class has the appropriate
+ * {@code ServicePermission} to get the service using at least one of the named
+ * classes under which the service was registered.
+ * 
+ * <p>
+ * {@code ServiceEvent} object delivery to {@code ServiceListener} objects is
+ * further filtered according to package sources as defined in
+ * {@link ServiceReference#isAssignableTo(Bundle, String)}.
+ * 
+ * @see ServiceEvent
+ * @see ServicePermission
+ * @ThreadSafe
+ * @version $Id: 601dfda6183ab7f18cd3916958a39734ea141c25 $
+ */
+
+public interface ServiceListener extends EventListener {
+	/**
+	 * Receives notification that a service has had a lifecycle change.
+	 * 
+	 * @param event The {@code ServiceEvent} object.
+	 */
+	public void serviceChanged(ServiceEvent event);
+}
diff --git a/src/main/java/org/osgi/framework/ServicePermission.java b/src/main/java/org/osgi/framework/ServicePermission.java
new file mode 100644
index 0000000..23ecfba
--- /dev/null
+++ b/src/main/java/org/osgi/framework/ServicePermission.java
@@ -0,0 +1,910 @@
+/*
+ * Copyright (c) OSGi Alliance (2000, 2012). All Rights Reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.framework;
+
+import java.io.IOException;
+import java.io.NotSerializableException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.ObjectStreamField;
+import java.security.AccessController;
+import java.security.BasicPermission;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.PrivilegedAction;
+import java.util.AbstractMap;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * A bundle's authority to register or get a service.
+ * <ul>
+ * <li>The {@code register} action allows a bundle to register a service on the
+ * specified names.
+ * <li>The {@code get} action allows a bundle to detect a service and get it.
+ * </ul>
+ * Permission to get a service is required in order to detect events regarding
+ * the service. Untrusted bundles should not be able to detect the presence of
+ * certain services unless they have the appropriate {@code ServicePermission}
+ * to get the specific service.
+ * 
+ * @ThreadSafe
+ * @version $Id: 96438ad164d7f0f4273787226298bf8208cf0034 $
+ */
+
+public final class ServicePermission extends BasicPermission {
+	static final long								serialVersionUID	= -7662148639076511574L;
+	/**
+	 * The action string {@code get}.
+	 */
+	public final static String						GET					= "get";
+	/**
+	 * The action string {@code register}.
+	 */
+	public final static String						REGISTER			= "register";
+
+	private final static int						ACTION_GET			= 0x00000001;
+	private final static int						ACTION_REGISTER		= 0x00000002;
+	private final static int						ACTION_ALL			= ACTION_GET | ACTION_REGISTER;
+	final static int								ACTION_NONE			= 0;
+
+	/**
+	 * The actions mask.
+	 */
+	transient int									action_mask;
+
+	/**
+	 * The actions in canonical form.
+	 * 
+	 * @serial
+	 */
+	private volatile String							actions				= null;
+
+	/**
+	 * The service used by this ServicePermission. Must be null if not
+	 * constructed with a service.
+	 */
+	transient final ServiceReference<?>				service;
+
+	/**
+	 * The object classes for this ServicePermission. Must be null if not
+	 * constructed with a service.
+	 */
+	transient final String[]						objectClass;
+
+	/**
+	 * If this ServicePermission was constructed with a filter, this holds a
+	 * Filter matching object used to evaluate the filter in implies.
+	 */
+	transient Filter								filter;
+
+	/**
+	 * This map holds the properties of the permission, used to match a filter
+	 * in implies. This is not initialized until necessary, and then cached in
+	 * this object.
+	 */
+	private transient volatile Map<String, Object>	properties;
+
+	/**
+	 * True if constructed with a name and the name is "*" or ends with ".*".
+	 */
+	private transient boolean						wildcard;
+
+	/**
+	 * If constructed with a name and the name ends with ".*", this contains the
+	 * name without the final "*".
+	 */
+	private transient String						prefix;
+
+	/**
+	 * Create a new ServicePermission.
+	 * 
+	 * <p>
+	 * The name of the service is specified as a fully qualified class name.
+	 * Wildcards may be used.
+	 * 
+	 * <pre>
+	 * name ::= <class name> | <class name ending in ".*"> | *
+	 * </pre>
+	 * 
+	 * Examples:
+	 * 
+	 * <pre>
+	 * org.osgi.service.http.HttpService
+	 * org.osgi.service.http.*
+	 * *
+	 * </pre>
+	 * 
+	 * For the {@code get} action, the name can also be a filter expression. The
+	 * filter gives access to the service properties as well as the following
+	 * attributes:
+	 * <ul>
+	 * <li>signer - A Distinguished Name chain used to sign the bundle
+	 * publishing the service. Wildcards in a DN are not matched according to
+	 * the filter string rules, but according to the rules defined for a DN
+	 * chain.</li>
+	 * <li>location - The location of the bundle publishing the service.</li>
+	 * <li>id - The bundle ID of the bundle publishing the service.</li>
+	 * <li>name - The symbolic name of the bundle publishing the service.</li>
+	 * </ul>
+	 * Since the above attribute names may conflict with service property names
+	 * used by a service, you can prefix an attribute name with '@' in the
+	 * filter expression to match against the service property and not one of
+	 * the above attributes. Filter attribute names are processed in a case
+	 * sensitive manner unless the attribute references a service property.
+	 * Service properties names are case insensitive.
+	 * 
+	 * <p>
+	 * There are two possible actions: {@code get} and {@code register}. The
+	 * {@code get} permission allows the owner of this permission to obtain a
+	 * service with this name. The {@code register} permission allows the bundle
+	 * to register a service under that name.
+	 * 
+	 * @param name The service class name
+	 * @param actions {@code get},{@code register} (canonical order)
+	 * @throws IllegalArgumentException If the specified name is a filter
+	 *         expression and either the specified action is not {@code get} or
+	 *         the filter has an invalid syntax.
+	 */
+	public ServicePermission(String name, String actions) {
+		this(name, parseActions(actions));
+		if ((filter != null) && ((action_mask & ACTION_ALL) != ACTION_GET)) {
+			throw new IllegalArgumentException("invalid action string for filter expression");
+		}
+	}
+
+	/**
+	 * Creates a new requested {@code ServicePermission} object to be used by
+	 * code that must perform {@code checkPermission} for the {@code get}
+	 * action. {@code ServicePermission} objects created with this constructor
+	 * cannot be added to a {@code ServicePermission} permission collection.
+	 * 
+	 * @param reference The requested service.
+	 * @param actions The action {@code get}.
+	 * @throws IllegalArgumentException If the specified action is not
+	 *         {@code get} or reference is {@code null}.
+	 * @since 1.5
+	 */
+	public ServicePermission(ServiceReference<?> reference, String actions) {
+		super(createName(reference));
+		setTransients(null, parseActions(actions));
+		this.service = reference;
+		this.objectClass = (String[]) reference.getProperty(Constants.OBJECTCLASS);
+		if ((action_mask & ACTION_ALL) != ACTION_GET) {
+			throw new IllegalArgumentException("invalid action string");
+		}
+	}
+
+	/**
+	 * Create a permission name from a ServiceReference
+	 * 
+	 * @param reference ServiceReference to use to create permission name.
+	 * @return permission name.
+	 */
+	private static String createName(ServiceReference<?> reference) {
+		if (reference == null) {
+			throw new IllegalArgumentException("reference must not be null");
+		}
+		StringBuffer sb = new StringBuffer("(service.id=");
+		sb.append(reference.getProperty(Constants.SERVICE_ID));
+		sb.append(")");
+		return sb.toString();
+	}
+
+	/**
+	 * Package private constructor used by ServicePermissionCollection.
+	 * 
+	 * @param name class name
+	 * @param mask action mask
+	 */
+	ServicePermission(String name, int mask) {
+		super(name);
+		setTransients(parseFilter(name), mask);
+		this.service = null;
+		this.objectClass = null;
+	}
+
+	/**
+	 * Called by constructors and when deserialized.
+	 * 
+	 * @param mask action mask
+	 */
+	private void setTransients(Filter f, int mask) {
+		if ((mask == ACTION_NONE) || ((mask & ACTION_ALL) != mask)) {
+			throw new IllegalArgumentException("invalid action string");
+		}
+		action_mask = mask;
+		filter = f;
+		if (f == null) {
+			String name = getName();
+			int l = name.length();
+			/* if "*" or endsWith ".*" */
+			wildcard = ((name.charAt(l - 1) == '*') && ((l == 1) || (name.charAt(l - 2) == '.')));
+			if (wildcard && (l > 1)) {
+				prefix = name.substring(0, l - 1);
+			}
+		}
+	}
+
+	/**
+	 * Parse action string into action mask.
+	 * 
+	 * @param actions Action string.
+	 * @return action mask.
+	 */
+	private static int parseActions(String actions) {
+		boolean seencomma = false;
+
+		int mask = ACTION_NONE;
+
+		if (actions == null) {
+			return mask;
+		}
+
+		char[] a = actions.toCharArray();
+
+		int i = a.length - 1;
+		if (i < 0)
+			return mask;
+
+		while (i != -1) {
+			char c;
+
+			// skip whitespace
+			while ((i != -1) && ((c = a[i]) == ' ' || c == '\r' || c == '\n' || c == '\f' || c == '\t'))
+				i--;
+
+			// check for the known strings
+			int matchlen;
+
+			if (i >= 2 && (a[i - 2] == 'g' || a[i - 2] == 'G')
+					&& (a[i - 1] == 'e' || a[i - 1] == 'E')
+					&& (a[i] == 't' || a[i] == 'T')) {
+				matchlen = 3;
+				mask |= ACTION_GET;
+
+			} else
+				if (i >= 7 && (a[i - 7] == 'r' || a[i - 7] == 'R')
+						&& (a[i - 6] == 'e' || a[i - 6] == 'E')
+						&& (a[i - 5] == 'g' || a[i - 5] == 'G')
+						&& (a[i - 4] == 'i' || a[i - 4] == 'I')
+						&& (a[i - 3] == 's' || a[i - 3] == 'S')
+						&& (a[i - 2] == 't' || a[i - 2] == 'T')
+						&& (a[i - 1] == 'e' || a[i - 1] == 'E')
+						&& (a[i] == 'r' || a[i] == 'R')) {
+					matchlen = 8;
+					mask |= ACTION_REGISTER;
+
+				} else {
+					// parse error
+					throw new IllegalArgumentException("invalid permission: " + actions);
+				}
+
+			// make sure we didn't just match the tail of a word
+			// like "ackbarfregister". Also, skip to the comma.
+			seencomma = false;
+			while (i >= matchlen && !seencomma) {
+				switch (a[i - matchlen]) {
+					case ',' :
+						seencomma = true;
+						/* FALLTHROUGH */
+					case ' ' :
+					case '\r' :
+					case '\n' :
+					case '\f' :
+					case '\t' :
+						break;
+					default :
+						throw new IllegalArgumentException("invalid permission: " + actions);
+				}
+				i--;
+			}
+
+			// point i at the location of the comma minus one (or -1).
+			i -= matchlen;
+		}
+
+		if (seencomma) {
+			throw new IllegalArgumentException("invalid permission: " + actions);
+		}
+
+		return mask;
+	}
+
+	/**
+	 * Parse filter string into a Filter object.
+	 * 
+	 * @param filterString The filter string to parse.
+	 * @return a Filter for this bundle. If the specified filterString is not a
+	 *         filter expression, then {@code null} is returned.
+	 * @throws IllegalArgumentException If the filter syntax is invalid.
+	 */
+	private static Filter parseFilter(String filterString) {
+		filterString = filterString.trim();
+		if (filterString.charAt(0) != '(') {
+			return null;
+		}
+
+		try {
+			return FrameworkUtil.createFilter(filterString);
+		} catch (InvalidSyntaxException e) {
+			IllegalArgumentException iae = new IllegalArgumentException("invalid filter");
+			iae.initCause(e);
+			throw iae;
+		}
+	}
+
+	/**
+	 * Determines if a {@code ServicePermission} object "implies" the specified
+	 * permission.
+	 * 
+	 * @param p The target permission to check.
+	 * @return {@code true} if the specified permission is implied by this
+	 *         object; {@code false} otherwise.
+	 */
+	public boolean implies(Permission p) {
+		if (!(p instanceof ServicePermission)) {
+			return false;
+		}
+		ServicePermission requested = (ServicePermission) p;
+		if (service != null) {
+			return false;
+		}
+		// if requested permission has a filter, then it is an invalid argument
+		if (requested.filter != null) {
+			return false;
+		}
+		return implies0(requested, ACTION_NONE);
+	}
+
+	/**
+	 * Internal implies method. Used by the implies and the permission
+	 * collection implies methods.
+	 * 
+	 * @param requested The requested ServicePermission which has already be
+	 *        validated as a proper argument. The requested ServicePermission
+	 *        must not have a filter expression.
+	 * @param effective The effective actions with which to start.
+	 * @return {@code true} if the specified permission is implied by this
+	 *         object; {@code false} otherwise.
+	 */
+	boolean implies0(ServicePermission requested, int effective) {
+		/* check actions first - much faster */
+		effective |= action_mask;
+		final int desired = requested.action_mask;
+		if ((effective & desired) != desired) {
+			return false;
+		}
+		/* we have name of "*" */
+		if (wildcard && (prefix == null)) {
+			return true;
+		}
+		/* if we have a filter */
+		Filter f = filter;
+		if (f != null) {
+			return f.matches(requested.getProperties());
+		}
+		/* if requested permission not created with ServiceReference */
+		String[] requestedNames = requested.objectClass;
+		if (requestedNames == null) {
+			return super.implies(requested);
+		}
+		/* requested permission created with ServiceReference */
+		if (wildcard) {
+			int pl = prefix.length();
+			for (int i = 0, l = requestedNames.length; i < l; i++) {
+				String requestedName = requestedNames[i];
+				if ((requestedName.length() > pl) && requestedName.startsWith(prefix)) {
+					return true;
+				}
+			}
+		} else {
+			String name = getName();
+			for (int i = 0, l = requestedNames.length; i < l; i++) {
+				if (requestedNames[i].equals(name)) {
+					return true;
+				}
+			}
+		}
+		return false;
+	}
+
+	/**
+	 * Returns the canonical string representation of the actions. Always
+	 * returns present actions in the following order: {@code get},
+	 * {@code register}.
+	 * 
+	 * @return The canonical string representation of the actions.
+	 */
+	public String getActions() {
+		String result = actions;
+		if (result == null) {
+			StringBuffer sb = new StringBuffer();
+			boolean comma = false;
+
+			int mask = action_mask;
+			if ((mask & ACTION_GET) == ACTION_GET) {
+				sb.append(GET);
+				comma = true;
+			}
+
+			if ((mask & ACTION_REGISTER) == ACTION_REGISTER) {
+				if (comma)
+					sb.append(',');
+				sb.append(REGISTER);
+			}
+
+			actions = result = sb.toString();
+		}
+
+		return result;
+	}
+
+	/**
+	 * Returns a new {@code PermissionCollection} object for storing
+	 * {@code ServicePermission} objects.
+	 * 
+	 * @return A new {@code PermissionCollection} object suitable for storing
+	 *         {@code ServicePermission} objects.
+	 */
+	public PermissionCollection newPermissionCollection() {
+		return new ServicePermissionCollection();
+	}
+
+	/**
+	 * Determines the equality of two ServicePermission objects.
+	 * 
+	 * Checks that specified object has the same class name and action as this
+	 * {@code ServicePermission}.
+	 * 
+	 * @param obj The object to test for equality.
+	 * @return true if obj is a {@code ServicePermission}, and has the same
+	 *         class name and actions as this {@code ServicePermission} object;
+	 *         {@code false} otherwise.
+	 */
+	public boolean equals(Object obj) {
+		if (obj == this) {
+			return true;
+		}
+
+		if (!(obj instanceof ServicePermission)) {
+			return false;
+		}
+
+		ServicePermission sp = (ServicePermission) obj;
+
+		return (action_mask == sp.action_mask) && getName().equals(sp.getName()) && ((service == sp.service) || ((service != null) && (service.compareTo(sp.service) == 0)));
+	}
+
+	/**
+	 * Returns the hash code value for this object.
+	 * 
+	 * @return Hash code value for this object.
+	 */
+	public int hashCode() {
+		int h = 31 * 17 + getName().hashCode();
+		h = 31 * h + getActions().hashCode();
+		if (service != null) {
+			h = 31 * h + service.hashCode();
+		}
+		return h;
+	}
+
+	/**
+	 * WriteObject is called to save the state of this permission to a stream.
+	 * The actions are serialized, and the superclass takes care of the name.
+	 */
+	private synchronized void writeObject(java.io.ObjectOutputStream s) throws IOException {
+		if (service != null) {
+			throw new NotSerializableException("cannot serialize");
+		}
+		// Write out the actions. The superclass takes care of the name
+		// call getActions to make sure actions field is initialized
+		if (actions == null)
+			getActions();
+		s.defaultWriteObject();
+	}
+
+	/**
+	 * readObject is called to restore the state of this permission from a
+	 * stream.
+	 */
+	private synchronized void readObject(java.io.ObjectInputStream s) throws IOException, ClassNotFoundException {
+		// Read in the action, then initialize the rest
+		s.defaultReadObject();
+		setTransients(parseFilter(getName()), parseActions(actions));
+	}
+
+	/**
+	 * Called by {@code <@link ServicePermission#implies(Permission)>}. This
+	 * method is only called on a requested permission which cannot have a
+	 * filter set.
+	 * 
+	 * @return a map of properties for this permission.
+	 */
+	private Map<String, Object> getProperties() {
+		Map<String, Object> result = properties;
+		if (result != null) {
+			return result;
+		}
+		if (service == null) {
+			result = new HashMap<String, Object>(1);
+			result.put(Constants.OBJECTCLASS, new String[] {getName()});
+			return properties = result;
+		}
+		final Map<String, Object> props = new HashMap<String, Object>(4);
+		final Bundle bundle = service.getBundle();
+		if (bundle != null) {
+			AccessController.doPrivileged(new PrivilegedAction<Object>() {
+				public Object run() {
+					props.put("id", new Long(bundle.getBundleId()));
+					props.put("location", bundle.getLocation());
+					String name = bundle.getSymbolicName();
+					if (name != null) {
+						props.put("name", name);
+					}
+					SignerProperty signer = new SignerProperty(bundle);
+					if (signer.isBundleSigned()) {
+						props.put("signer", signer);
+					}
+					return null;
+				}
+			});
+		}
+		return properties = new Properties(props, service);
+	}
+
+	static private final class Properties extends AbstractMap<String, Object> {
+		private final Map<String, Object>							properties;
+		private final ServiceReference<?>							service;
+		private transient volatile Set<Map.Entry<String, Object>>	entries;
+
+		Properties(Map<String, Object> properties, ServiceReference<?> service) {
+			this.properties = properties;
+			this.service = service;
+			entries = null;
+		}
+
+		public Object get(Object k) {
+			if (!(k instanceof String)) {
+				return null;
+			}
+			String key = (String) k;
+			if (key.charAt(0) == '@') {
+				return service.getProperty(key.substring(1));
+			}
+			Object value = properties.get(key);
+			if (value != null) { // fall back to service properties
+				return value;
+			}
+			return service.getProperty(key);
+		}
+
+		public Set<Map.Entry<String, Object>> entrySet() {
+			if (entries != null) {
+				return entries;
+			}
+			Set<Map.Entry<String, Object>> all = new HashSet<Map.Entry<String, Object>>(properties.entrySet());
+			add: for (String key : service.getPropertyKeys()) {
+				for (String k : properties.keySet()) {
+					if (key.equalsIgnoreCase(k)) {
+						continue add;
+					}
+				}
+				all.add(new Entry(key, service.getProperty(key)));
+			}
+			return entries = Collections.unmodifiableSet(all);
+		}
+
+		static private final class Entry implements Map.Entry<String, Object> {
+			private final String	k;
+			private final Object	v;
+
+			Entry(String key, Object value) {
+				this.k = key;
+				this.v = value;
+			}
+
+			public String getKey() {
+				return k;
+			}
+
+			public Object getValue() {
+				return v;
+			}
+
+			public Object setValue(Object value) {
+				throw new UnsupportedOperationException();
+			}
+
+			public String toString() {
+				return k + "=" + v;
+			}
+
+			public int hashCode() {
+				return ((k == null) ? 0 : k.hashCode()) ^ ((v == null) ? 0 : v.hashCode());
+			}
+
+			public boolean equals(Object obj) {
+				if (obj == this) {
+					return true;
+				}
+				if (!(obj instanceof Map.Entry)) {
+					return false;
+				}
+				Map.Entry<?, ?> e = (Map.Entry<?, ?>) obj;
+				final Object key = e.getKey();
+				if ((k == key) || ((k != null) && k.equals(key))) {
+					final Object value = e.getValue();
+					if ((v == value) || ((v != null) && v.equals(value))) {
+						return true;
+					}
+				}
+				return false;
+			}
+		}
+	}
+}
+
+/**
+ * Stores a set of ServicePermission permissions.
+ * 
+ * @see java.security.Permission
+ * @see java.security.Permissions
+ * @see java.security.PermissionCollection
+ */
+final class ServicePermissionCollection extends PermissionCollection {
+	static final long									serialVersionUID	= 662615640374640621L;
+	/**
+	 * Table of permissions.
+	 * 
+	 * @GuardedBy this
+	 */
+	private transient Map<String, ServicePermission>	permissions;
+
+	/**
+	 * Boolean saying if "*" is in the collection.
+	 * 
+	 * @serial
+	 * @GuardedBy this
+	 */
+	private boolean										all_allowed;
+
+	/**
+	 * Table of permissions with filter expressions.
+	 * 
+	 * @serial
+	 * @GuardedBy this
+	 */
+	private Map<String, ServicePermission>				filterPermissions;
+
+	/**
+	 * Creates an empty ServicePermissions object.
+	 */
+	public ServicePermissionCollection() {
+		permissions = new HashMap<String, ServicePermission>();
+		all_allowed = false;
+	}
+
+	/**
+	 * Adds a permission to this permission collection.
+	 * 
+	 * @param permission The Permission object to add.
+	 * @throws IllegalArgumentException If the specified permission is not a
+	 *         ServicePermission object.
+	 * @throws SecurityException If this {@code ServicePermissionCollection}
+	 *         object has been marked read-only.
+	 */
+	public void add(final Permission permission) {
+		if (!(permission instanceof ServicePermission)) {
+			throw new IllegalArgumentException("invalid permission: " + permission);
+		}
+		if (isReadOnly()) {
+			throw new SecurityException("attempt to add a Permission to a " + "readonly PermissionCollection");
+		}
+
+		final ServicePermission sp = (ServicePermission) permission;
+		if (sp.service != null) {
+			throw new IllegalArgumentException("cannot add to collection: " + sp);
+		}
+
+		final String name = sp.getName();
+		final Filter f = sp.filter;
+		synchronized (this) {
+			/* select the bucket for the permission */
+			Map<String, ServicePermission> pc;
+			if (f != null) {
+				pc = filterPermissions;
+				if (pc == null) {
+					filterPermissions = pc = new HashMap<String, ServicePermission>();
+				}
+			} else {
+				pc = permissions;
+			}
+			final ServicePermission existing = pc.get(name);
+
+			if (existing != null) {
+				final int oldMask = existing.action_mask;
+				final int newMask = sp.action_mask;
+				if (oldMask != newMask) {
+					pc.put(name, new ServicePermission(name, oldMask | newMask));
+				}
+			} else {
+				pc.put(name, sp);
+			}
+
+			if (!all_allowed) {
+				if (name.equals("*")) {
+					all_allowed = true;
+				}
+			}
+		}
+	}
+
+	/**
+	 * Determines if a set of permissions implies the permissions expressed in
+	 * {@code permission}.
+	 * 
+	 * @param permission The Permission object to compare.
+	 * @return {@code true} if {@code permission} is a proper subset of a
+	 *         permission in the set; {@code false} otherwise.
+	 */
+	public boolean implies(final Permission permission) {
+		if (!(permission instanceof ServicePermission)) {
+			return false;
+		}
+		final ServicePermission requested = (ServicePermission) permission;
+		/* if requested permission has a filter, then it is an invalid argument */
+		if (requested.filter != null) {
+			return false;
+		}
+
+		int effective = ServicePermission.ACTION_NONE;
+		Collection<ServicePermission> perms;
+		synchronized (this) {
+			final int desired = requested.action_mask;
+			/* short circuit if the "*" Permission was added */
+			if (all_allowed) {
+				ServicePermission sp = permissions.get("*");
+				if (sp != null) {
+					effective |= sp.action_mask;
+					if ((effective & desired) == desired) {
+						return true;
+					}
+				}
+			}
+
+			String[] requestedNames = requested.objectClass;
+			/* if requested permission not created with ServiceReference */
+			if (requestedNames == null) {
+				effective |= effective(requested.getName(), desired, effective);
+				if ((effective & desired) == desired) {
+					return true;
+				}
+			}
+			/* requested permission created with ServiceReference */
+			else {
+				for (int i = 0, l = requestedNames.length; i < l; i++) {
+					if ((effective(requestedNames[i], desired, effective) & desired) == desired) {
+						return true;
+					}
+				}
+			}
+			Map<String, ServicePermission> pc = filterPermissions;
+			if (pc == null) {
+				return false;
+			}
+			perms = pc.values();
+		}
+
+		/* iterate one by one over filteredPermissions */
+		for (ServicePermission perm : perms) {
+			if (perm.implies0(requested, effective)) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+	/**
+	 * Consult permissions map to compute the effective permission for the
+	 * requested permission name.
+	 * 
+	 * @param requestedName The requested service name.
+	 * @param desired The desired actions.
+	 * @param effective The effective actions.
+	 * @return The new effective actions.
+	 */
+	private int effective(String requestedName, final int desired, int effective) {
+		final Map<String, ServicePermission> pc = permissions;
+		ServicePermission sp = pc.get(requestedName);
+		// strategy:
+		// Check for full match first. Then work our way up the
+		// name looking for matches on a.b.*
+		if (sp != null) {
+			// we have a direct hit!
+			effective |= sp.action_mask;
+			if ((effective & desired) == desired) {
+				return effective;
+			}
+		}
+		// work our way up the tree...
+		int last;
+		int offset = requestedName.length() - 1;
+		while ((last = requestedName.lastIndexOf(".", offset)) != -1) {
+			requestedName = requestedName.substring(0, last + 1) + "*";
+			sp = pc.get(requestedName);
+			if (sp != null) {
+				effective |= sp.action_mask;
+				if ((effective & desired) == desired) {
+					return effective;
+				}
+			}
+			offset = last - 1;
+		}
+		/*
+		 * we don't have to check for "*" as it was already checked before we
+		 * were called.
+		 */
+		return effective;
+	}
+
+	/**
+	 * Returns an enumeration of all the {@code ServicePermission} objects in
+	 * the container.
+	 * 
+	 * @return Enumeration of all the ServicePermission objects.
+	 */
+	public synchronized Enumeration<Permission> elements() {
+		List<Permission> all = new ArrayList<Permission>(permissions.values());
+		Map<String, ServicePermission> pc = filterPermissions;
+		if (pc != null) {
+			all.addAll(pc.values());
+		}
+		return Collections.enumeration(all);
+	}
+
+	/* serialization logic */
+	private static final ObjectStreamField[]	serialPersistentFields	= {new ObjectStreamField("permissions", Hashtable.class), new ObjectStreamField("all_allowed", Boolean.TYPE),
+			new ObjectStreamField("filterPermissions", HashMap.class)	};
+
+	private synchronized void writeObject(ObjectOutputStream out) throws IOException {
+		Hashtable<String, ServicePermission> hashtable = new Hashtable<String, ServicePermission>(permissions);
+		ObjectOutputStream.PutField pfields = out.putFields();
+		pfields.put("permissions", hashtable);
+		pfields.put("all_allowed", all_allowed);
+		pfields.put("filterPermissions", filterPermissions);
+		out.writeFields();
+	}
+
+	private synchronized void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
+		ObjectInputStream.GetField gfields = in.readFields();
+		Hashtable<String, ServicePermission> hashtable = (Hashtable<String, ServicePermission>) gfields.get("permissions", null);
+		permissions = new HashMap<String, ServicePermission>(hashtable);
+		all_allowed = gfields.get("all_allowed", false);
+		HashMap<String, ServicePermission> fp = (HashMap<String, ServicePermission>) gfields.get("filterPermissions", null);
+		filterPermissions = fp;
+	}
+}
diff --git a/src/main/java/org/osgi/framework/ServiceReference.java b/src/main/java/org/osgi/framework/ServiceReference.java
new file mode 100644
index 0000000..7541517
--- /dev/null
+++ b/src/main/java/org/osgi/framework/ServiceReference.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (c) OSGi Alliance (2000, 2012). All Rights Reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.framework;
+
+import java.util.Dictionary;
+
+/**
+ * A reference to a service.
+ * 
+ * <p>
+ * The Framework returns {@code ServiceReference} objects from the
+ * {@code BundleContext.getServiceReference} and
+ * {@code BundleContext.getServiceReferences} methods.
+ * <p>
+ * A {@code ServiceReference} object may be shared between bundles and can be
+ * used to examine the properties of the service and to get the service object.
+ * <p>
+ * Every service registered in the Framework has a unique
+ * {@code ServiceRegistration} object and may have multiple, distinct
+ * {@code ServiceReference} objects referring to it. {@code ServiceReference}
+ * objects associated with a {@code ServiceRegistration} object have the same
+ * {@code hashCode} and are considered equal (more specifically, their
+ * {@code equals()} method will return {@code true} when compared).
+ * <p>
+ * If the same service object is registered multiple times,
+ * {@code ServiceReference} objects associated with different
+ * {@code ServiceRegistration} objects are not equal.
+ * 
+ * @param <S> Type of Service.
+ * @see BundleContext#getServiceReference(Class)
+ * @see BundleContext#getServiceReference(String)
+ * @see BundleContext#getServiceReferences(Class, String)
+ * @see BundleContext#getServiceReferences(String, String)
+ * @see BundleContext#getService(ServiceReference)
+ * @ThreadSafe
+ * @noimplement
+ * @version $Id: 75352193f9f11a2c19692890153c6ff91611023b $
+ */
+
+public interface ServiceReference<S> extends Comparable<Object> {
+	/**
+	 * Returns the property value to which the specified property key is mapped
+	 * in the properties {@code Dictionary} object of the service referenced by
+	 * this {@code ServiceReference} object.
+	 * 
+	 * <p>
+	 * Property keys are case-insensitive.
+	 * 
+	 * <p>
+	 * This method must continue to return property values after the service has
+	 * been unregistered. This is so references to unregistered services (for
+	 * example, {@code ServiceReference} objects stored in the log) can still be
+	 * interrogated.
+	 * 
+	 * @param key The property key.
+	 * @return The property value to which the key is mapped; {@code null} if
+	 *         there is no property named after the key.
+	 */
+	public Object getProperty(String key);
+
+	/**
+	 * Returns an array of the keys in the properties {@code Dictionary} object
+	 * of the service referenced by this {@code ServiceReference} object.
+	 * 
+	 * <p>
+	 * This method will continue to return the keys after the service has been
+	 * unregistered. This is so references to unregistered services (for
+	 * example, {@code ServiceReference} objects stored in the log) can still be
+	 * interrogated.
+	 * 
+	 * <p>
+	 * This method is <i>case-preserving </i>; this means that every key in the
+	 * returned array must have the same case as the corresponding key in the
+	 * properties {@code Dictionary} that was passed to the
+	 * {@link BundleContext#registerService(String[],Object,Dictionary)} or
+	 * {@link ServiceRegistration#setProperties(Dictionary)} methods.
+	 * 
+	 * @return An array of property keys.
+	 */
+	public String[] getPropertyKeys();
+
+	/**
+	 * Returns the bundle that registered the service referenced by this
+	 * {@code ServiceReference} object.
+	 * 
+	 * <p>
+	 * This method must return {@code null} when the service has been
+	 * unregistered. This can be used to determine if the service has been
+	 * unregistered.
+	 * 
+	 * @return The bundle that registered the service referenced by this
+	 *         {@code ServiceReference} object; {@code null} if that service has
+	 *         already been unregistered.
+	 * @see BundleContext#registerService(String[],Object,Dictionary)
+	 */
+	public Bundle getBundle();
+
+	/**
+	 * Returns the bundles that are using the service referenced by this
+	 * {@code ServiceReference} object. Specifically, this method returns the
+	 * bundles whose usage count for that service is greater than zero.
+	 * 
+	 * @return An array of bundles whose usage count for the service referenced
+	 *         by this {@code ServiceReference} object is greater than zero;
+	 *         {@code null} if no bundles are currently using that service.
+	 * 
+	 * @since 1.1
+	 */
+	public Bundle[] getUsingBundles();
+
+	/**
+	 * Tests if the bundle that registered the service referenced by this
+	 * {@code ServiceReference} and the specified bundle use the same source for
+	 * the package of the specified class name.
+	 * <p>
+	 * This method performs the following checks:
+	 * <ol>
+	 * <li>Get the package name from the specified class name.</li>
+	 * <li>For the bundle that registered the service referenced by this
+	 * {@code ServiceReference} (registrant bundle); find the source for the
+	 * package. If no source is found then return {@code true} if the registrant
+	 * bundle is equal to the specified bundle; otherwise return {@code false}.</li>
+	 * <li>If the package source of the registrant bundle is equal to the
+	 * package source of the specified bundle then return {@code true};
+	 * otherwise return {@code false}.</li>
+	 * </ol>
+	 * 
+	 * @param bundle The {@code Bundle} object to check.
+	 * @param className The class name to check.
+	 * @return {@code true} if the bundle which registered the service
+	 *         referenced by this {@code ServiceReference} and the specified
+	 *         bundle use the same source for the package of the specified class
+	 *         name. Otherwise {@code false} is returned.
+	 * @throws IllegalArgumentException If the specified {@code Bundle} was not
+	 *         created by the same framework instance as this
+	 *         {@code ServiceReference}.
+	 * @since 1.3
+	 */
+	public boolean isAssignableTo(Bundle bundle, String className);
+
+	/**
+	 * Compares this {@code ServiceReference} with the specified
+	 * {@code ServiceReference} for order.
+	 * 
+	 * <p>
+	 * If this {@code ServiceReference} and the specified
+	 * {@code ServiceReference} have the same {@link Constants#SERVICE_ID
+	 * service id} they are equal. This {@code ServiceReference} is less than
+	 * the specified {@code ServiceReference} if it has a lower
+	 * {@link Constants#SERVICE_RANKING service ranking} and greater if it has a
+	 * higher service ranking. Otherwise, if this {@code ServiceReference} and
+	 * the specified {@code ServiceReference} have the same
+	 * {@link Constants#SERVICE_RANKING service ranking}, this
+	 * {@code ServiceReference} is less than the specified
+	 * {@code ServiceReference} if it has a higher {@link Constants#SERVICE_ID
+	 * service id} and greater if it has a lower service id.
+	 * 
+	 * @param reference The {@code ServiceReference} to be compared.
+	 * @return Returns a negative integer, zero, or a positive integer if this
+	 *         {@code ServiceReference} is less than, equal to, or greater than
+	 *         the specified {@code ServiceReference}.
+	 * @throws IllegalArgumentException If the specified
+	 *         {@code ServiceReference} was not created by the same framework
+	 *         instance as this {@code ServiceReference}.
+	 * @since 1.4
+	 */
+	public int compareTo(Object reference);
+}
diff --git a/src/main/java/org/osgi/framework/ServiceRegistration.java b/src/main/java/org/osgi/framework/ServiceRegistration.java
new file mode 100644
index 0000000..702e918
--- /dev/null
+++ b/src/main/java/org/osgi/framework/ServiceRegistration.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) OSGi Alliance (2000, 2012). All Rights Reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.framework;
+
+import java.util.Dictionary;
+
+/**
+ * A registered service.
+ * 
+ * <p>
+ * The Framework returns a {@code ServiceRegistration} object when a
+ * {@code BundleContext.registerService} method invocation is successful. The
+ * {@code ServiceRegistration} object is for the private use of the registering
+ * bundle and should not be shared with other bundles.
+ * <p>
+ * The {@code ServiceRegistration} object may be used to update the properties
+ * of the service or to unregister the service.
+ * 
+ * @param <S> Type of Service.
+ * @see BundleContext#registerService(String[],Object,Dictionary)
+ * @ThreadSafe
+ * @noimplement
+ * @version $Id: a84248da0db0538708d2394a9478153e06b8afb9 $
+ */
+
+public interface ServiceRegistration<S> {
+	/**
+	 * Returns a {@code ServiceReference} object for a service being registered.
+	 * <p>
+	 * The {@code ServiceReference} object may be shared with other bundles.
+	 * 
+	 * @throws IllegalStateException If this {@code ServiceRegistration} object
+	 *         has already been unregistered.
+	 * @return {@code ServiceReference} object.
+	 */
+	public ServiceReference<S> getReference();
+
+	/**
+	 * Updates the properties associated with a service.
+	 * 
+	 * <p>
+	 * The {@link Constants#OBJECTCLASS} and {@link Constants#SERVICE_ID} keys
+	 * cannot be modified by this method. These values are set by the Framework
+	 * when the service is registered in the OSGi environment.
+	 * 
+	 * <p>
+	 * The following steps are required to modify service properties:
+	 * <ol>
+	 * <li>The service's properties are replaced with the provided properties.
+	 * <li>A service event of type {@link ServiceEvent#MODIFIED} is fired.
+	 * </ol>
+	 * 
+	 * @param properties The properties for this service. See {@link Constants}
+	 *        for a list of standard service property keys. Changes should not
+	 *        be made to this object after calling this method. To update the
+	 *        service's properties this method should be called again.
+	 * 
+	 * @throws IllegalStateException If this {@code ServiceRegistration} object
+	 *         has already been unregistered.
+	 * @throws IllegalArgumentException If {@code properties} contains case
+	 *         variants of the same key name.
+	 */
+	public void setProperties(Dictionary<String, ?> properties);
+
+	/**
+	 * Unregisters a service. Remove a {@code ServiceRegistration} object from
+	 * the Framework service registry. All {@code ServiceReference} objects
+	 * associated with this {@code ServiceRegistration} object can no longer be
+	 * used to interact with the service once unregistration is complete.
+	 * 
+	 * <p>
+	 * The following steps are required to unregister a service:
+	 * <ol>
+	 * <li>The service is removed from the Framework service registry so that it
+	 * can no longer be obtained.
+	 * <li>A service event of type {@link ServiceEvent#UNREGISTERING} is fired
+	 * so that bundles using this service can release their use of the service.
+	 * Once delivery of the service event is complete, the
+	 * {@code ServiceReference} objects for the service may no longer be used to
+	 * get a service object for the service.
+	 * <li>For each bundle whose use count for this service is greater than
+	 * zero: <br>
+	 * The bundle's use count for this service is set to zero. <br>
+	 * If the service was registered with a {@link ServiceFactory} object, the
+	 * {@code ServiceFactory.ungetService} method is called to release the
+	 * service object for the bundle.
+	 * </ol>
+	 * 
+	 * @throws IllegalStateException If this {@code ServiceRegistration} object
+	 *         has already been unregistered.
+	 * @see BundleContext#ungetService(ServiceReference)
+	 * @see ServiceFactory#ungetService(Bundle, ServiceRegistration, Object)
+	 */
+	public void unregister();
+}
diff --git a/src/main/java/org/osgi/framework/SignerProperty.java b/src/main/java/org/osgi/framework/SignerProperty.java
new file mode 100644
index 0000000..9236dfa
--- /dev/null
+++ b/src/main/java/org/osgi/framework/SignerProperty.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) OSGi Alliance (2009, 2012). All Rights Reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.framework;
+
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Package private class used by permissions for filter matching on signer key
+ * during filter expression evaluation in the permission implies method.
+ * 
+ * @Immutable
+ * @version $Id: 94eea19050b84907f1257d7a12ebf8ab404f4473 $
+ */
+final class SignerProperty {
+	private final Bundle	bundle;
+	private final String	pattern;
+
+	/**
+	 * String constructor used by the filter matching algorithm to construct a
+	 * SignerProperty from the attribute value in a filter expression.
+	 * 
+	 * @param pattern Attribute value in the filter expression.
+	 */
+	public SignerProperty(String pattern) {
+		this.pattern = pattern;
+		this.bundle = null;
+	}
+
+	/**
+	 * Used by the permission implies method to build the properties for a
+	 * filter match.
+	 * 
+	 * @param bundle The bundle whose signers are to be matched.
+	 */
+	SignerProperty(Bundle bundle) {
+		this.bundle = bundle;
+		this.pattern = null;
+	}
+
+	/**
+	 * Used by the filter matching algorithm. This methods does NOT satisfy the
+	 * normal equals contract. Since the class is only used in filter expression
+	 * evaluations, it only needs to support comparing an instance created with
+	 * a Bundle to an instance created with a pattern string from the filter
+	 * expression.
+	 * 
+	 * @param o SignerProperty to compare against.
+	 * @return true if the DN name chain matches the pattern.
+	 */
+	public boolean equals(Object o) {
+		if (!(o instanceof SignerProperty))
+			return false;
+		SignerProperty other = (SignerProperty) o;
+		Bundle matchBundle = bundle != null ? bundle : other.bundle;
+		String matchPattern = bundle != null ? other.pattern : pattern;
+		Map<X509Certificate, List<X509Certificate>> signers = matchBundle.getSignerCertificates(Bundle.SIGNERS_TRUSTED);
+		for (List<X509Certificate> signerCerts : signers.values()) {
+			List<String> dnChain = new ArrayList<String>(signerCerts.size());
+			for (X509Certificate signerCert : signerCerts) {
+				dnChain.add(signerCert.getSubjectDN().getName());
+			}
+			try {
+				if (FrameworkUtil.matchDistinguishedNameChain(matchPattern, dnChain)) {
+					return true;
+				}
+			} catch (IllegalArgumentException e) {
+				continue; // bad pattern
+			}
+		}
+		return false;
+	}
+
+	/**
+	 * Since the equals method does not obey the general equals contract, this
+	 * method cannot generate hash codes which obey the equals contract.
+	 */
+	public int hashCode() {
+		return 31;
+	}
+
+	/**
+	 * Check if the bundle is signed.
+	 * 
+	 * @return true if constructed with a bundle that is signed.
+	 */
+	boolean isBundleSigned() {
+		if (bundle == null) {
+			return false;
+		}
+		Map<X509Certificate, List<X509Certificate>> signers = bundle.getSignerCertificates(Bundle.SIGNERS_TRUSTED);
+		return !signers.isEmpty();
+	}
+}
diff --git a/src/main/java/org/osgi/framework/SynchronousBundleListener.java b/src/main/java/org/osgi/framework/SynchronousBundleListener.java
new file mode 100644
index 0000000..ee8474c
--- /dev/null
+++ b/src/main/java/org/osgi/framework/SynchronousBundleListener.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) OSGi Alliance (2001, 2011). All Rights Reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.framework;
+
+/**
+ * A synchronous {@code BundleEvent} listener. {@code SynchronousBundleListener}
+ * is a listener interface that may be implemented by a bundle developer. When a
+ * {@code BundleEvent} is fired, it is synchronously delivered to a
+ * {@code SynchronousBundleListener}. The Framework may deliver
+ * {@code BundleEvent} objects to a {@code SynchronousBundleListener} out of
+ * order and may concurrently call and/or reenter a
+ * {@code SynchronousBundleListener}.
+ * 
+ * <p>
+ * For {@code BundleEvent} types {@link BundleEvent#STARTED STARTED} and
+ * {@link BundleEvent#LAZY_ACTIVATION LAZY_ACTIVATION}, the Framework must not
+ * hold the referenced bundle's "state change" lock when the
+ * {@code BundleEvent} is delivered to a {@code SynchronousBundleListener}. For
+ * the other {@code BundleEvent} types, the Framework must hold the referenced
+ * bundle's "state change" lock when the {@code BundleEvent} is
+ * delivered to a {@code SynchronousBundleListener}. A
+ * {@code SynchronousBundleListener} cannot directly call life cycle methods on
+ * the referenced bundle when the Framework is holding the referenced bundle's
+ * "state change" lock.
+ * 
+ * <p>
+ * A {@code SynchronousBundleListener} object is registered with the Framework
+ * using the {@link BundleContext#addBundleListener(BundleListener)} method.
+ * {@code SynchronousBundleListener} objects are called with a
+ * {@code BundleEvent} object when a bundle has been installed, resolved,
+ * starting, started, stopping, stopped, updated, unresolved, or uninstalled.
+ * <p>
+ * Unlike normal {@code BundleListener} objects,
+ * {@code SynchronousBundleListener}s are synchronously called during bundle
+ * lifecycle processing. The bundle lifecycle processing will not proceed until
+ * all {@code SynchronousBundleListener}s have completed.
+ * {@code SynchronousBundleListener} objects will be called prior to
+ * {@code BundleListener} objects.
+ * <p>
+ * {@code AdminPermission[bundle,LISTENER]} is required to add or remove a
+ * {@code SynchronousBundleListener} object.
+ * 
+ * @since 1.1
+ * @see BundleEvent
+ * @ThreadSafe
+ * @version $Id: 74246f4ceeba7f9a5ee198048522f93d4691c51a $
+ */
+
+public interface SynchronousBundleListener extends BundleListener {
+	// This is a marker interface
+}
diff --git a/src/main/java/org/osgi/framework/UnfilteredServiceListener.java b/src/main/java/org/osgi/framework/UnfilteredServiceListener.java
new file mode 100644
index 0000000..d712fcf
--- /dev/null
+++ b/src/main/java/org/osgi/framework/UnfilteredServiceListener.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) OSGi Alliance (2011). All Rights Reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.framework;
+
+import org.osgi.framework.hooks.service.ListenerHook;
+
+/**
+ * A {@code ServiceEvent} listener that does <i>not</i> filter based upon any
+ * filter string specified to
+ * {@link BundleContext#addServiceListener(ServiceListener, String)}. Using an
+ * {@code UnfilteredServiceListener} and specifying a filter string to
+ * {@link BundleContext#addServiceListener(ServiceListener, String)} allows the
+ * listener to receive all {@code ServiceEvent} objects while still advising
+ * {@link ListenerHook} implementation of the service interests in the filter
+ * string.
+ * 
+ * For example, an implementation of Declarative Services would add an
+ * {@code UnfilteredServiceListener} with a filter string listing all the
+ * services referenced by all the service components. The Declarative Services
+ * implementation would receive all {@code ServiceEvent} objects for internal
+ * processing and a Remote Services discovery service implementation can observe
+ * the service interests of the service components using a {@link ListenerHook}.
+ * When the set of service components being processed changes, the Declarative
+ * Services implementation would re-add the {@code UnfilteredServiceListener}
+ * with an updated filter string.
+ * 
+ * <p>
+ * When a {@code ServiceEvent} is fired, it is synchronously delivered to an
+ * {@code UnfilteredServiceListener}. The Framework may deliver
+ * {@code ServiceEvent} objects to an {@code UnfilteredServiceListener} out of
+ * order and may concurrently call and/or reenter an
+ * {@code UnfilteredServiceListener}.
+ * 
+ * <p>
+ * An {@code UnfilteredServiceListener} object is registered with the Framework
+ * using the {@code BundleContext.addServiceListener} method.
+ * {@code UnfilteredServiceListener} objects are called with a
+ * {@code ServiceEvent} object when a service is registered, modified, or is in
+ * the process of unregistering.
+ * 
+ * <p>
+ * {@code ServiceEvent} object delivery to {@code UnfilteredServiceListener}
+ * objects are <i>not</i> filtered by the filter specified when the listener was
+ * registered. If the Java Runtime Environment supports permissions, then some
+ * filtering is done. {@code ServiceEvent} objects are only delivered to the
+ * listener if the bundle which defines the listener object's class has the
+ * appropriate {@code ServicePermission} to get the service using at least one
+ * of the named classes under which the service was registered.
+ * 
+ * @see ServiceEvent
+ * @see ServicePermission
+ * @ThreadSafe
+ * @since 1.7
+ * @version $Id: 543a345802f8dc7a49d29e8fb7aee7004ee2b329 $
+ */
+
+public interface UnfilteredServiceListener extends ServiceListener {
+	// This is a marker interface
+}
diff --git a/src/main/java/org/osgi/framework/Version.java b/src/main/java/org/osgi/framework/Version.java
new file mode 100644
index 0000000..02f6ddd
--- /dev/null
+++ b/src/main/java/org/osgi/framework/Version.java
@@ -0,0 +1,398 @@
+/*
+ * Copyright (c) OSGi Alliance (2004, 2012). All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.framework;
+
+import java.util.NoSuchElementException;
+import java.util.StringTokenizer;
+
+/**
+ * Version identifier for capabilities such as bundles and packages.
+ * 
+ * <p>
+ * Version identifiers have four components.
+ * <ol>
+ * <li>Major version. A non-negative integer.</li>
+ * <li>Minor version. A non-negative integer.</li>
+ * <li>Micro version. A non-negative integer.</li>
+ * <li>Qualifier. A text string. See {@code Version(String)} for the format of
+ * the qualifier string.</li>
+ * </ol>
+ * 
+ * <p>
+ * {@code Version} objects are immutable.
+ * 
+ * @since 1.3
+ * @Immutable
+ * @version $Id: a0b5a865f7fbf2b3dcb77a13b2e99da0b64702bb $
+ */
+
+public class Version implements Comparable<Version> {
+	private final int			major;
+	private final int			minor;
+	private final int			micro;
+	private final String		qualifier;
+	private static final String	SEPARATOR		= ".";
+	private transient String	versionString /* default to null */;
+	private transient int		hash /* default to 0 */;
+
+	/**
+	 * The empty version "0.0.0".
+	 */
+	public static final Version	emptyVersion	= new Version(0, 0, 0);
+
+	/**
+	 * Creates a version identifier from the specified numerical components.
+	 * 
+	 * <p>
+	 * The qualifier is set to the empty string.
+	 * 
+	 * @param major Major component of the version identifier.
+	 * @param minor Minor component of the version identifier.
+	 * @param micro Micro component of the version identifier.
+	 * @throws IllegalArgumentException If the numerical components are
+	 *         negative.
+	 */
+	public Version(int major, int minor, int micro) {
+		this(major, minor, micro, null);
+	}
+
+	/**
+	 * Creates a version identifier from the specified components.
+	 * 
+	 * @param major Major component of the version identifier.
+	 * @param minor Minor component of the version identifier.
+	 * @param micro Micro component of the version identifier.
+	 * @param qualifier Qualifier component of the version identifier. If
+	 *        {@code null} is specified, then the qualifier will be set to the
+	 *        empty string.
+	 * @throws IllegalArgumentException If the numerical components are negative
+	 *         or the qualifier string is invalid.
+	 */
+	public Version(int major, int minor, int micro, String qualifier) {
+		if (qualifier == null) {
+			qualifier = "";
+		}
+
+		this.major = major;
+		this.minor = minor;
+		this.micro = micro;
+		this.qualifier = qualifier;
+		validate();
+	}
+
+	/**
+	 * Creates a version identifier from the specified string.
+	 * 
+	 * <p>
+	 * Version string grammar:
+	 * 
+	 * <pre>
+	 * version ::= major('.'minor('.'micro('.'qualifier)?)?)?
+	 * major ::= digit+
+	 * minor ::= digit+
+	 * micro ::= digit+
+	 * qualifier ::= (alpha|digit|'_'|'-')+
+	 * digit ::= [0..9]
+	 * alpha ::= [a..zA..Z]
+	 * </pre>
+	 * 
+	 * @param version String representation of the version identifier. There
+	 *        must be no whitespace in the argument.
+	 * @throws IllegalArgumentException If {@code version} is improperly
+	 *         formatted.
+	 */
+	public Version(String version) {
+		int maj = 0;
+		int min = 0;
+		int mic = 0;
+		String qual = "";
+
+		try {
+			StringTokenizer st = new StringTokenizer(version, SEPARATOR, true);
+			maj = parseInt(st.nextToken(), version);
+
+			if (st.hasMoreTokens()) { // minor
+				st.nextToken(); // consume delimiter
+				min = parseInt(st.nextToken(), version);
+
+				if (st.hasMoreTokens()) { // micro
+					st.nextToken(); // consume delimiter
+					mic = parseInt(st.nextToken(), version);
+
+					if (st.hasMoreTokens()) { // qualifier separator
+						st.nextToken(); // consume delimiter
+						qual = st.nextToken(""); // remaining string
+
+						if (st.hasMoreTokens()) { // fail safe
+							throw new IllegalArgumentException("invalid version \"" + version + "\": invalid format");
+						}
+					}
+				}
+			}
+		} catch (NoSuchElementException e) {
+			IllegalArgumentException iae = new IllegalArgumentException("invalid version \"" + version + "\": invalid format");
+			iae.initCause(e);
+			throw iae;
+		}
+
+		major = maj;
+		minor = min;
+		micro = mic;
+		qualifier = qual;
+		validate();
+	}
+
+	/**
+	 * Parse numeric component into an int.
+	 * 
+	 * @param value Numeric component
+	 * @param version Complete version string for exception message, if any
+	 * @return int value of numeric component
+	 */
+	private static int parseInt(String value, String version) {
+		try {
+			return Integer.parseInt(value);
+		} catch (NumberFormatException e) {
+			IllegalArgumentException iae = new IllegalArgumentException("invalid version \"" + version + "\": non-numeric \"" + value + "\"");
+			iae.initCause(e);
+			throw iae;
+		}
+	}
+
+	/**
+	 * Called by the Version constructors to validate the version components.
+	 * 
+	 * @throws IllegalArgumentException If the numerical components are negative
+	 *         or the qualifier string is invalid.
+	 */
+	private void validate() {
+		if (major < 0) {
+			throw new IllegalArgumentException("invalid version \"" + toString0() + "\": negative number \"" + major + "\"");
+		}
+		if (minor < 0) {
+			throw new IllegalArgumentException("invalid version \"" + toString0() + "\": negative number \"" + minor + "\"");
+		}
+		if (micro < 0) {
+			throw new IllegalArgumentException("invalid version \"" + toString0() + "\": negative number \"" + micro + "\"");
+		}
+		for (char ch : qualifier.toCharArray()) {
+			if (('A' <= ch) && (ch <= 'Z')) {
+				continue;
+			}
+			if (('a' <= ch) && (ch <= 'z')) {
+				continue;
+			}
+			if (('0' <= ch) && (ch <= '9')) {
+				continue;
+			}
+			if ((ch == '_') || (ch == '-')) {
+				continue;
+			}
+			throw new IllegalArgumentException("invalid version \"" + toString0() + "\": invalid qualifier \"" + qualifier + "\"");
+		}
+	}
+
+	/**
+	 * Parses a version identifier from the specified string.
+	 * 
+	 * <p>
+	 * See {@code Version(String)} for the format of the version string.
+	 * 
+	 * @param version String representation of the version identifier. Leading
+	 *        and trailing whitespace will be ignored.
+	 * @return A {@code Version} object representing the version identifier. If
+	 *         {@code version} is {@code null} or the empty string then
+	 *         {@code emptyVersion} will be returned.
+	 * @throws IllegalArgumentException If {@code version} is improperly
+	 *         formatted.
+	 */
+	public static Version parseVersion(String version) {
+		if (version == null) {
+			return emptyVersion;
+		}
+
+		version = version.trim();
+		if (version.length() == 0) {
+			return emptyVersion;
+		}
+
+		return new Version(version);
+	}
+
+	/**
+	 * Returns the major component of this version identifier.
+	 * 
+	 * @return The major component.
+	 */
+	public int getMajor() {
+		return major;
+	}
+
+	/**
+	 * Returns the minor component of this version identifier.
+	 * 
+	 * @return The minor component.
+	 */
+	public int getMinor() {
+		return minor;
+	}
+
+	/**
+	 * Returns the micro component of this version identifier.
+	 * 
+	 * @return The micro component.
+	 */
+	public int getMicro() {
+		return micro;
+	}
+
+	/**
+	 * Returns the qualifier component of this version identifier.
+	 * 
+	 * @return The qualifier component.
+	 */
+	public String getQualifier() {
+		return qualifier;
+	}
+
+	/**
+	 * Returns the string representation of this version identifier.
+	 * 
+	 * <p>
+	 * The format of the version string will be {@code major.minor.micro} if
+	 * qualifier is the empty string or {@code major.minor.micro.qualifier}
+	 * otherwise.
+	 * 
+	 * @return The string representation of this version identifier.
+	 */
+	public String toString() {
+		return toString0();
+	}
+
+	/**
+	 * Internal toString behavior
+	 * 
+	 * @return The string representation of this version identifier.
+	 */
+	String toString0() {
+		if (versionString != null) {
+			return versionString;
+		}
+		int q = qualifier.length();
+		StringBuffer result = new StringBuffer(20 + q);
+		result.append(major);
+		result.append(SEPARATOR);
+		result.append(minor);
+		result.append(SEPARATOR);
+		result.append(micro);
+		if (q > 0) {
+			result.append(SEPARATOR);
+			result.append(qualifier);
+		}
+		return versionString = result.toString();
+	}
+
+	/**
+	 * Returns a hash code value for the object.
+	 * 
+	 * @return An integer which is a hash code value for this object.
+	 */
+	public int hashCode() {
+		if (hash != 0) {
+			return hash;
+		}
+		int h = 31 * 17;
+		h = 31 * h + major;
+		h = 31 * h + minor;
+		h = 31 * h + micro;
+		h = 31 * h + qualifier.hashCode();
+		return hash = h;
+	}
+
+	/**
+	 * Compares this {@code Version} object to another object.
+	 * 
+	 * <p>
+	 * A version is considered to be <b>equal to </b> another version if the
+	 * major, minor and micro components are equal and the qualifier component
+	 * is equal (using {@code String.equals}).
+	 * 
+	 * @param object The {@code Version} object to be compared.
+	 * @return {@code true} if {@code object} is a {@code Version} and is equal
+	 *         to this object; {@code false} otherwise.
+	 */
+	public boolean equals(Object object) {
+		if (object == this) { // quicktest
+			return true;
+		}
+
+		if (!(object instanceof Version)) {
+			return false;
+		}
+
+		Version other = (Version) object;
+		return (major == other.major) && (minor == other.minor) && (micro == other.micro) && qualifier.equals(other.qualifier);
+	}
+
+	/**
+	 * Compares this {@code Version} object to another {@code Version}.
+	 * 
+	 * <p>
+	 * A version is considered to be <b>less than</b> another version if its
+	 * major component is less than the other version's major component, or the
+	 * major components are equal and its minor component is less than the other
+	 * version's minor component, or the major and minor components are equal
+	 * and its micro component is less than the other version's micro component,
+	 * or the major, minor and micro components are equal and it's qualifier
+	 * component is less than the other version's qualifier component (using
+	 * {@code String.compareTo}).
+	 * 
+	 * <p>
+	 * A version is considered to be <b>equal to</b> another version if the
+	 * major, minor and micro components are equal and the qualifier component
+	 * is equal (using {@code String.compareTo}).
+	 * 
+	 * @param other The {@code Version} object to be compared.
+	 * @return A negative integer, zero, or a positive integer if this version
+	 *         is less than, equal to, or greater than the specified
+	 *         {@code Version} object.
+	 * @throws ClassCastException If the specified object is not a
+	 *         {@code Version} object.
+	 */
+	public int compareTo(Version other) {
+		if (other == this) { // quicktest
+			return 0;
+		}
+
+		int result = major - other.major;
+		if (result != 0) {
+			return result;
+		}
+
+		result = minor - other.minor;
+		if (result != 0) {
+			return result;
+		}
+
+		result = micro - other.micro;
+		if (result != 0) {
+			return result;
+		}
+
+		return qualifier.compareTo(other.qualifier);
+	}
+}
diff --git a/src/main/java/org/osgi/framework/VersionRange.java b/src/main/java/org/osgi/framework/VersionRange.java
new file mode 100644
index 0000000..0589a75
--- /dev/null
+++ b/src/main/java/org/osgi/framework/VersionRange.java
@@ -0,0 +1,509 @@
+/*
+ * Copyright (c) OSGi Alliance (2011, 2012). All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.framework;
+
+import java.util.NoSuchElementException;
+import java.util.StringTokenizer;
+
+/**
+ * Version range. A version range is an interval describing a set of
+ * {@link Version versions}.
+ * 
+ * <p>
+ * A range has a left (lower) endpoint and a right (upper) endpoint. Each
+ * endpoint can be open (excluded from the set) or closed (included in the set).
+ * 
+ * <p>
+ * {@code VersionRange} objects are immutable.
+ * 
+ * @since 1.7
+ * @Immutable
+ * @version $Id: d0c21e6a5015a7fa0b33179a29122ea7d137145a $
+ */
+
+public class VersionRange {
+	/**
+	 * The left endpoint is open and is excluded from the range.
+	 * <p>
+	 * The value of {@code LEFT_OPEN} is {@code '('}.
+	 */
+	public static final char	LEFT_OPEN				= '(';
+	/**
+	 * The left endpoint is closed and is included in the range.
+	 * <p>
+	 * The value of {@code LEFT_CLOSED} is {@code '['}.
+	 */
+	public static final char	LEFT_CLOSED				= '[';
+	/**
+	 * The right endpoint is open and is excluded from the range.
+	 * <p>
+	 * The value of {@code RIGHT_OPEN} is {@code ')'}.
+	 */
+	public static final char	RIGHT_OPEN				= ')';
+	/**
+	 * The right endpoint is closed and is included in the range.
+	 * <p>
+	 * The value of {@code RIGHT_CLOSED} is {@code ']'}.
+	 */
+	public static final char	RIGHT_CLOSED			= ']';
+
+	private final boolean		leftClosed;
+	private final Version		left;
+	private final Version		right;
+	private final boolean		rightClosed;
+	private final boolean		empty;
+
+	private transient String	versionRangeString /* default to null */;
+	private transient int		hash /* default to 0 */;
+
+	private static final String	LEFT_OPEN_DELIMITER		= "(";
+	private static final String	LEFT_CLOSED_DELIMITER	= "[";
+	private static final String	LEFT_DELIMITERS			= LEFT_CLOSED_DELIMITER + LEFT_OPEN_DELIMITER;
+	private static final String	RIGHT_OPEN_DELIMITER	= ")";
+	private static final String	RIGHT_CLOSED_DELIMITER	= "]";
+	private static final String	RIGHT_DELIMITERS		= RIGHT_OPEN_DELIMITER + RIGHT_CLOSED_DELIMITER;
+	private static final String	ENDPOINT_DELIMITER		= ",";
+
+	/**
+	 * Creates a version range from the specified versions.
+	 * 
+	 * @param leftType Must be either {@link #LEFT_CLOSED} or {@link #LEFT_OPEN}
+	 *        .
+	 * @param leftEndpoint Left endpoint of range. Must not be {@code null}.
+	 * @param rightEndpoint Right endpoint of range. May be {@code null} to
+	 *        indicate the right endpoint is <i>Infinity</i>.
+	 * @param rightType Must be either {@link #RIGHT_CLOSED} or
+	 *        {@link #RIGHT_OPEN}.
+	 * @throws IllegalArgumentException If the arguments are invalid.
+	 */
+	public VersionRange(char leftType, Version leftEndpoint, Version rightEndpoint, char rightType) {
+		if ((leftType != LEFT_CLOSED) && (leftType != LEFT_OPEN)) {
+			throw new IllegalArgumentException("invalid leftType \"" + leftType + "\"");
+		}
+		if ((rightType != RIGHT_OPEN) && (rightType != RIGHT_CLOSED)) {
+			throw new IllegalArgumentException("invalid rightType \"" + rightType + "\"");
+		}
+		if (leftEndpoint == null) {
+			throw new IllegalArgumentException("null leftEndpoint argument");
+		}
+		leftClosed = leftType == LEFT_CLOSED;
+		rightClosed = rightType == RIGHT_CLOSED;
+		left = leftEndpoint;
+		right = rightEndpoint;
+		empty = isEmpty0();
+	}
+
+	/**
+	 * Creates a version range from the specified string.
+	 * 
+	 * <p>
+	 * Version range string grammar:
+	 * 
+	 * <pre>
+	 * range ::= interval | atleast
+	 * interval ::= ( '[' | '(' ) left ',' right ( ']' | ')' )
+	 * left ::= version
+	 * right ::= version
+	 * atleast ::= version
+	 * </pre>
+	 * 
+	 * @param range String representation of the version range. The versions in
+	 *        the range must contain no whitespace. Other whitespace in the
+	 *        range string is ignored.
+	 * @throws IllegalArgumentException If {@code range} is improperly
+	 *         formatted.
+	 */
+	public VersionRange(String range) {
+		boolean closedLeft;
+		boolean closedRight;
+		Version endpointLeft;
+		Version endpointRight;
+
+		try {
+			StringTokenizer st = new StringTokenizer(range, LEFT_DELIMITERS, true);
+			String token = st.nextToken().trim(); // whitespace or left delim
+			if (token.length() == 0) { // leading whitespace
+				token = st.nextToken(); // left delim
+			}
+			closedLeft = LEFT_CLOSED_DELIMITER.equals(token);
+			if (!closedLeft && !LEFT_OPEN_DELIMITER.equals(token)) {
+				// first token is not a delimiter, so it must be "atleast"
+				if (st.hasMoreTokens()) { // there must be no more tokens
+					throw new IllegalArgumentException("invalid range \"" + range + "\": invalid format");
+				}
+				leftClosed = true;
+				rightClosed = false;
+				left = parseVersion(token, range);
+				right = null;
+				empty = false;
+				return;
+			}
+			String version = st.nextToken(ENDPOINT_DELIMITER);
+			endpointLeft = parseVersion(version, range);
+			token = st.nextToken(); // consume comma
+			version = st.nextToken(RIGHT_DELIMITERS);
+			token = st.nextToken(); // right delim
+			closedRight = RIGHT_CLOSED_DELIMITER.equals(token);
+			if (!closedRight && !RIGHT_OPEN_DELIMITER.equals(token)) {
+				throw new IllegalArgumentException("invalid range \"" + range + "\": invalid format");
+			}
+			endpointRight = parseVersion(version, range);
+
+			if (st.hasMoreTokens()) { // any more tokens have to be whitespace
+				token = st.nextToken("").trim();
+				if (token.length() != 0) { // trailing whitespace
+					throw new IllegalArgumentException("invalid range \"" + range + "\": invalid format");
+				}
+			}
+		} catch (NoSuchElementException e) {
+			IllegalArgumentException iae = new IllegalArgumentException("invalid range \"" + range + "\": invalid format");
+			iae.initCause(e);
+			throw iae;
+		}
+
+		leftClosed = closedLeft;
+		rightClosed = closedRight;
+		left = endpointLeft;
+		right = endpointRight;
+		empty = isEmpty0();
+	}
+
+	/**
+	 * Parse version component into a Version.
+	 * 
+	 * @param version version component string
+	 * @param range Complete range string for exception message, if any
+	 * @return Version
+	 */
+	private static Version parseVersion(String version, String range) {
+		try {
+			return Version.parseVersion(version);
+		} catch (IllegalArgumentException e) {
+			IllegalArgumentException iae = new IllegalArgumentException("invalid range \"" + range + "\": " + e.getMessage());
+			iae.initCause(e);
+			throw iae;
+		}
+	}
+
+	/**
+	 * Returns the left endpoint of this version range.
+	 * 
+	 * @return The left endpoint.
+	 */
+	public Version getLeft() {
+		return left;
+	}
+
+	/**
+	 * Returns the right endpoint of this version range.
+	 * 
+	 * @return The right endpoint. May be {@code null} which indicates the right
+	 *         endpoint is <i>Infinity</i>.
+	 */
+	public Version getRight() {
+		return right;
+	}
+
+	/**
+	 * Returns the type of the left endpoint of this version range.
+	 * 
+	 * @return {@link #LEFT_CLOSED} if the left endpoint is closed or
+	 *         {@link #LEFT_OPEN} if the left endpoint is open.
+	 */
+	public char getLeftType() {
+		return leftClosed ? LEFT_CLOSED : LEFT_OPEN;
+	}
+
+	/**
+	 * Returns the type of the right endpoint of this version range.
+	 * 
+	 * @return {@link #RIGHT_CLOSED} if the right endpoint is closed or
+	 *         {@link #RIGHT_OPEN} if the right endpoint is open.
+	 */
+	public char getRightType() {
+		return rightClosed ? RIGHT_CLOSED : RIGHT_OPEN;
+	}
+
+	/**
+	 * Returns whether this version range includes the specified version.
+	 * 
+	 * @param version The version to test for inclusion in this version range.
+	 * @return {@code true} if the specified version is included in this version
+	 *         range; {@code false} otherwise.
+	 */
+	public boolean includes(Version version) {
+		if (empty) {
+			return false;
+		}
+		if (left.compareTo(version) >= (leftClosed ? 1 : 0)) {
+			return false;
+		}
+		if (right == null) {
+			return true;
+		}
+		return right.compareTo(version) >= (rightClosed ? 0 : 1);
+	}
+
+	/**
+	 * Returns the intersection of this version range with the specified version
+	 * ranges.
+	 * 
+	 * @param ranges The version ranges to intersect with this version range.
+	 * @return A version range representing the intersection of this version
+	 *         range and the specified version ranges. If no version ranges are
+	 *         specified, then this version range is returned.
+	 */
+	public VersionRange intersection(VersionRange... ranges) {
+		if ((ranges == null) || (ranges.length == 0)) {
+			return this;
+		}
+		// prime with data from this version range
+		boolean closedLeft = leftClosed;
+		boolean closedRight = rightClosed;
+		Version endpointLeft = left;
+		Version endpointRight = right;
+
+		for (VersionRange range : ranges) {
+			int comparison = endpointLeft.compareTo(range.left);
+			if (comparison == 0) {
+				closedLeft = closedLeft && range.leftClosed;
+			} else {
+				if (comparison < 0) { // move endpointLeft to the right
+					endpointLeft = range.left;
+					closedLeft = range.leftClosed;
+				}
+			}
+			if (range.right != null) {
+				if (endpointRight == null) {
+					endpointRight = range.right;
+					closedRight = range.rightClosed;
+				} else {
+					comparison = endpointRight.compareTo(range.right);
+					if (comparison == 0) {
+						closedRight = closedRight && range.rightClosed;
+					} else {
+						if (comparison > 0) { // move endpointRight to the left
+							endpointRight = range.right;
+							closedRight = range.rightClosed;
+						}
+					}
+				}
+			}
+		}
+
+		return new VersionRange(closedLeft ? LEFT_CLOSED : LEFT_OPEN, endpointLeft, endpointRight, closedRight ? RIGHT_CLOSED : RIGHT_OPEN);
+	}
+
+	/**
+	 * Returns whether this version range is empty. A version range is empty if
+	 * the set of versions defined by the interval is empty.
+	 * 
+	 * @return {@code true} if this version range is empty; {@code false}
+	 *         otherwise.
+	 */
+	public boolean isEmpty() {
+		return empty;
+	}
+
+	/**
+	 * Internal isEmpty behavior.
+	 * 
+	 * @return {@code true} if this version range is empty; {@code false}
+	 *         otherwise.
+	 */
+	private boolean isEmpty0() {
+		if (right == null) { // infinity
+			return false;
+		}
+		int comparison = left.compareTo(right);
+		if (comparison == 0) { // endpoints equal
+			return !leftClosed || !rightClosed;
+		}
+		return comparison > 0; // true if left > right
+	}
+
+	/**
+	 * Returns whether this version range contains only a single version.
+	 * 
+	 * @return {@code true} if this version range contains only a single
+	 *         version; {@code false} otherwise.
+	 */
+	public boolean isExact() {
+		if (empty || (right == null)) {
+			return false;
+		}
+		if (leftClosed) {
+			if (rightClosed) {
+				// [l,r]: exact if l == r
+				return left.equals(right);
+			} else {
+				// [l,r): exact if l++ >= r
+				Version adjacent1 = new Version(left.getMajor(), left.getMinor(), left.getMicro(), left.getQualifier() + "-");
+				return adjacent1.compareTo(right) >= 0;
+			}
+		} else {
+			if (rightClosed) {
+				// (l,r] is equivalent to [l++,r]: exact if l++ == r
+				Version adjacent1 = new Version(left.getMajor(), left.getMinor(), left.getMicro(), left.getQualifier() + "-");
+				return adjacent1.equals(right);
+			} else {
+				// (l,r) is equivalent to [l++,r): exact if (l++)++ >=r
+				Version adjacent2 = new Version(left.getMajor(), left.getMinor(), left.getMicro(), left.getQualifier() + "--");
+				return adjacent2.compareTo(right) >= 0;
+			}
+		}
+	}
+
+	/**
+	 * Returns the string representation of this version range.
+	 * 
+	 * <p>
+	 * The format of the version range string will be a version string if the
+	 * right end point is <i>Infinity</i> ({@code null}) or an interval string.
+	 * 
+	 * @return The string representation of this version range.
+	 */
+	public String toString() {
+		if (versionRangeString != null) {
+			return versionRangeString;
+		}
+		String leftVersion = left.toString();
+		if (right == null) {
+			StringBuffer result = new StringBuffer(leftVersion.length() + 1);
+			result.append(left.toString0());
+			return versionRangeString = result.toString();
+		}
+		String rightVerion = right.toString();
+		StringBuffer result = new StringBuffer(leftVersion.length() + rightVerion.length() + 5);
+		result.append(leftClosed ? LEFT_CLOSED : LEFT_OPEN);
+		result.append(left.toString0());
+		result.append(ENDPOINT_DELIMITER);
+		result.append(right.toString0());
+		result.append(rightClosed ? RIGHT_CLOSED : RIGHT_OPEN);
+		return versionRangeString = result.toString();
+	}
+
+	/**
+	 * Returns a hash code value for the object.
+	 * 
+	 * @return An integer which is a hash code value for this object.
+	 */
+	public int hashCode() {
+		if (hash != 0) {
+			return hash;
+		}
+		if (empty) {
+			return hash = 31;
+		}
+		int h = 31 + (leftClosed ? 7 : 5);
+		h = 31 * h + left.hashCode();
+		if (right != null) {
+			h = 31 * h + right.hashCode();
+			h = 31 * h + (rightClosed ? 7 : 5);
+		}
+		return hash = h;
+	}
+
+	/**
+	 * Compares this {@code VersionRange} object to another object.
+	 * 
+	 * <p>
+	 * A version range is considered to be <b>equal to </b> another version
+	 * range if both the endpoints and their types are equal or if both version
+	 * ranges are {@link #isEmpty() empty}.
+	 * 
+	 * @param object The {@code VersionRange} object to be compared.
+	 * @return {@code true} if {@code object} is a {@code VersionRange} and is
+	 *         equal to this object; {@code false} otherwise.
+	 */
+	public boolean equals(Object object) {
+		if (object == this) { // quicktest
+			return true;
+		}
+		if (!(object instanceof VersionRange)) {
+			return false;
+		}
+		VersionRange other = (VersionRange) object;
+		if (empty && other.empty) {
+			return true;
+		}
+		if (right == null) {
+			return (leftClosed == other.leftClosed) && (other.right == null) && left.equals(other.left);
+		}
+		return (leftClosed == other.leftClosed) && (rightClosed == other.rightClosed) && left.equals(other.left) && right.equals(other.right);
+	}
+
+	/**
+	 * Returns the filter string for this version range using the specified
+	 * attribute name.
+	 * 
+	 * @param attributeName The attribute name to use in the returned filter
+	 *        string.
+	 * @return A filter string for this version range using the specified
+	 *         attribute name.
+	 * @throws IllegalArgumentException If the specified attribute name is not a
+	 *         valid attribute name.
+	 * 
+	 * @see "Core Specification, Filters, for a description of the filter string syntax."
+	 */
+	public String toFilterString(String attributeName) {
+		if (attributeName.length() == 0) {
+			throw new IllegalArgumentException("invalid attributeName \"" + attributeName + "\"");
+		}
+		for (char ch : attributeName.toCharArray()) {
+			if ((ch == '=') || (ch == '>') || (ch == '<') || (ch == '~') || (ch == '(') || (ch == ')')) {
+				throw new IllegalArgumentException("invalid attributeName \"" + attributeName + "\"");
+			}
+		}
+
+		StringBuffer result = new StringBuffer(128);
+		if (right != null) {
+			result.append("(&");
+		}
+		if (leftClosed) {
+			result.append('(');
+			result.append(attributeName);
+			result.append(">=");
+			result.append(left.toString0());
+			result.append(')');
+		} else {
+			result.append("(!(");
+			result.append(attributeName);
+			result.append("<=");
+			result.append(left.toString0());
+			result.append("))");
+		}
+		if (right != null) {
+			if (rightClosed) {
+				result.append('(');
+				result.append(attributeName);
+				result.append("<=");
+				result.append(right.toString0());
+				result.append(')');
+			} else {
+				result.append("(!(");
+				result.append(attributeName);
+				result.append(">=");
+				result.append(right.toString0());
+				result.append("))");
+			}
+			result.append(')');
+		}
+
+		return result.toString();
+	}
+}
diff --git a/src/main/java/org/osgi/framework/hooks/bundle/CollisionHook.java b/src/main/java/org/osgi/framework/hooks/bundle/CollisionHook.java
new file mode 100644
index 0000000..37446ff
--- /dev/null
+++ b/src/main/java/org/osgi/framework/hooks/bundle/CollisionHook.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) OSGi Alliance (2011, 2012). All Rights Reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.framework.hooks.bundle;
+
+import java.util.Collection;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+
+/**
+ * OSGi Framework Bundle Collision Hook Service.
+ * 
+ * <p>
+ * If the framework was launched with the {@link Constants#FRAMEWORK_BSNVERSION
+ * org.osgi.framework.bsnversion} framework launching property set to
+ * {@link Constants#FRAMEWORK_BSNVERSION_MANAGED managed}, then all registered
+ * collision hook services will be called during framework bundle install and
+ * update operations to determine if an install or update operation will result
+ * in a bundle symbolic name and version collision.
+ * 
+ * @ThreadSafe
+ * @version $Id: a1a25ee0432f210a56e911246f477f19edc28bc1 $
+ */
+public interface CollisionHook {
+
+	/**
+	 * Specifies a bundle install operation is being performed.
+	 */
+	int	INSTALLING	= 1;
+
+	/**
+	 * Specifies a bundle update operation is being performed.
+	 */
+	int	UPDATING	= 2;
+
+	/**
+	 * Filter bundle collisions hook method. This method is called during the
+	 * install or update operation. The operation type will be
+	 * {@link #INSTALLING installing} or {@link #UPDATING updating}. Depending
+	 * on the operation type the target bundle and the collision candidate
+	 * collection are the following:
+	 * <ul>
+	 * <li> {@link #INSTALLING installing} - The target is the bundle associated
+	 * with the {@link BundleContext} used to call one of the
+	 * {@link BundleContext#installBundle(String) install} methods. The
+	 * collision candidate collection contains the existing bundles installed
+	 * which have the same symbolic name and version as the bundle being
+	 * installed.
+	 * <li> {@link #UPDATING updating} - The target is the bundle used to call
+	 * one of the {@link Bundle#update() update} methods. The collision
+	 * candidate collection contains the existing bundles installed which have
+	 * the same symbolic name and version as the content the target bundle is
+	 * being updated to.
+	 * </ul>
+	 * This method can filter the collection of collision candidates by removing
+	 * potential collisions. For the specified operation to succeed, the
+	 * collection of collision candidates must be empty after all registered
+	 * collision hook services have been called.
+	 * 
+	 * @param operationType The operation type. Must be the value of
+	 *        {@link #INSTALLING installing} or {@link #UPDATING updating}.
+	 * @param target The target bundle used to determine what collision
+	 *        candidates to filter.
+	 * @param collisionCandidates The collection of collision candidates. The
+	 *        collection supports all the optional {@code Collection} operations
+	 *        except {@code add} and {@code addAll}. Attempting to add to the
+	 *        collection will result in an {@code UnsupportedOperationException}
+	 *        . The collection is not synchronized.
+	 */
+	void filterCollisions(int operationType, Bundle target, Collection<Bundle> collisionCandidates);
+}
diff --git a/src/main/java/org/osgi/framework/hooks/bundle/EventHook.java b/src/main/java/org/osgi/framework/hooks/bundle/EventHook.java
new file mode 100644
index 0000000..e66a20e
--- /dev/null
+++ b/src/main/java/org/osgi/framework/hooks/bundle/EventHook.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) OSGi Alliance (2010, 2012). All Rights Reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.framework.hooks.bundle;
+
+import java.util.Collection;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleEvent;
+
+/**
+ * OSGi Framework Bundle Event Hook Service.
+ * 
+ * <p>
+ * Bundles registering this service will be called during framework lifecycle
+ * (install, start, stop, update, and uninstall bundle) operations.
+ * 
+ * @ThreadSafe
+ * @version $Id: e1471b36491a02bd8598a30d05c889ee58edc760 $
+ */
+public interface EventHook {
+
+	/**
+	 * Bundle event hook method. This method is called prior to bundle event
+	 * delivery when a bundle is installed, resolved, started, stopped,
+	 * unresolved, or uninstalled. This method can filter the bundles which
+	 * receive the event.
+	 * <p>
+	 * This method must be called by the framework one and only one time for
+	 * each bundle event generated, this included bundle events which are
+	 * generated when there are no bundle listeners registered. This method must
+	 * be called on the same thread that is performing the action which
+	 * generated the specified event. The specified collection includes bundle
+	 * contexts with synchronous and asynchronous bundle listeners registered
+	 * with them.
+	 * 
+	 * @param event The bundle event to be delivered
+	 * @param contexts A collection of Bundle Contexts for bundles which have
+	 *        listeners to which the specified event will be delivered. The
+	 *        implementation of this method may remove bundle contexts from the
+	 *        collection to prevent the event from being delivered to the
+	 *        associated bundles. The collection supports all the optional
+	 *        {@code Collection} operations except {@code add} and
+	 *        {@code addAll}. Attempting to add to the collection will result in
+	 *        an {@code UnsupportedOperationException}. The collection is not
+	 *        synchronized.
+	 */
+	void event(BundleEvent event, Collection<BundleContext> contexts);
+}
diff --git a/src/main/java/org/osgi/framework/hooks/bundle/FindHook.java b/src/main/java/org/osgi/framework/hooks/bundle/FindHook.java
new file mode 100644
index 0000000..af556ea
--- /dev/null
+++ b/src/main/java/org/osgi/framework/hooks/bundle/FindHook.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) OSGi Alliance (2011, 2012). All Rights Reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.framework.hooks.bundle;
+
+import java.util.Collection;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+
+/**
+ * OSGi Framework Bundle Context Hook Service.
+ * 
+ * <p>
+ * Bundles registering this service will be called during framework bundle find
+ * (get bundles) operations.
+ * 
+ * @ThreadSafe
+ * @version $Id: ae6bf5fc5cf999ac39dfc195c99ef7e223e3b847 $
+ */
+public interface FindHook {
+	/**
+	 * Find hook method. This method is called for the following:
+	 * <ul>
+	 * <li>Bundle find operations using {@link BundleContext#getBundle(long)}
+	 * and {@link BundleContext#getBundles()} methods. The find method can
+	 * filter the result of the find operation. Note that a find operation using
+	 * the {@link BundleContext#getBundle(String)} method does not cause the
+	 * find method to be called.</li>
+	 * <li>Bundle install operations when an existing bundle is already
+	 * installed at a given location. In this case, the find method is called to
+	 * determine if the context performing the install operation is able to find
+	 * the bundle. If the context cannot find the existing bundle then the
+	 * install operation must fail with a
+	 * {@link BundleException#REJECTED_BY_HOOK} exception.</li>
+	 * </ul>
+	 * 
+	 * @param context The bundle context of the bundle performing the find
+	 *        operation.
+	 * @param bundles A collection of Bundles to be returned as a result of the
+	 *        find operation. The implementation of this method may remove
+	 *        bundles from the collection to prevent the bundles from being
+	 *        returned to the bundle performing the find operation. The
+	 *        collection supports all the optional {@code Collection} operations
+	 *        except {@code add} and {@code addAll}. Attempting to add to the
+	 *        collection will result in an {@code UnsupportedOperationException}
+	 *        . The collection is not synchronized.
+	 */
+	void find(BundleContext context, Collection<Bundle> bundles);
+}
diff --git a/src/main/java/org/osgi/framework/hooks/resolver/ResolverHook.java b/src/main/java/org/osgi/framework/hooks/resolver/ResolverHook.java
new file mode 100644
index 0000000..51c46ec
--- /dev/null
+++ b/src/main/java/org/osgi/framework/hooks/resolver/ResolverHook.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (c) OSGi Alliance (2010, 2012). All Rights Reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.framework.hooks.resolver;
+
+import java.util.Collection;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.namespace.BundleNamespace;
+import org.osgi.framework.namespace.IdentityNamespace;
+import org.osgi.framework.wiring.BundleCapability;
+import org.osgi.framework.wiring.BundleRequirement;
+import org.osgi.framework.wiring.BundleRevision;
+import org.osgi.framework.wiring.FrameworkWiring;
+
+/**
+ * OSGi Framework Resolver Hook instances are obtained from the OSGi
+ * {@link ResolverHookFactory Framework Resolver Hook Factory} service.
+ * 
+ * <p>
+ * A Resolver Hook instance is called by the framework during a resolve process.
+ * A resolver hook may influence the outcome of a resolve process by removing
+ * entries from shrinkable collections that are passed to the hook during a
+ * resolve process. A shrinkable collection is a {@code Collection} that
+ * supports all remove operations. Any other attempts to modify a shrinkable
+ * collection will result in an {@code UnsupportedOperationException} being
+ * thrown.
+ * 
+ * <p>
+ * The following steps outline the way a framework uses the resolver hooks
+ * during a resolve process.
+ * <ol>
+ * <li>Collect a snapshot of registered resolver hook factories that will be
+ * called during the current resolve process. Any hook factories registered
+ * after the snapshot is taken must not be called during the current resolve
+ * process. A resolver hook factory contained in the snapshot may become
+ * unregistered during the resolve process. The framework should handle this and
+ * stop calling the resolver hook instance provided by the unregistered hook
+ * factory and the current resolve process must fail. If possible, an exception
+ * must be thrown to the caller of the API which triggered the resolve process.
+ * In cases where the the caller is not available a framework event of type
+ * error should be fired.</li>
+ * 
+ * <li>For each registered hook factory call the
+ * {@link ResolverHookFactory#begin(Collection)} method to inform the hooks
+ * about a resolve process beginning and to obtain a Resolver Hook instance that
+ * will be used for the duration of the resolve process.</li>
+ * 
+ * <li>Determine the collection of unresolved bundle revisions that may be
+ * considered for resolution during the current resolution process and place
+ * each of the bundle revisions in a shrinkable collection {@code Resolvable}.
+ * For each resolver hook call the {@link #filterResolvable(Collection)} method
+ * with the shrinkable collection {@code Resolvable}.</li>
+ * <li>The shrinkable collection {@code Resolvable} now contains all the
+ * unresolved bundle revisions that may end up as resolved at the end of the
+ * current resolve process. Any other bundle revisions that got removed from the
+ * shrinkable collection {@code Resolvable} must not end up as resolved at the
+ * end of the current resolve process.</li>
+ * <li>For each bundle revision {@code B} left in the shrinkable collection
+ * {@code Resolvable} and any bundle revision {@code B} which is currently
+ * resolved that represents a singleton bundle do the following:
+ * <p/>
+ * Determine the collection of available capabilities that have a namespace of
+ * {@link IdentityNamespace osgi.identity}, are singletons, and have the same
+ * symbolic name as the singleton bundle revision {@code B} and place each of
+ * the matching capabilities into a shrinkable collection {@code Collisions}.
+ * <p/>
+ * Remove the {@link IdentityNamespace osgi.identity} capability provided by
+ * bundle revision {@code B} from shrinkable collection {@code Collisions}. A
+ * singleton bundle cannot collide with itself.
+ * <p/>
+ * For each resolver hook call the
+ * {@link #filterSingletonCollisions(BundleCapability, Collection)} with the
+ * {@link IdentityNamespace osgi.identity} capability provided by bundle
+ * revision {@code B} and the shrinkable collection {@code Collisions}
+ * <p/>
+ * The shrinkable collection {@code Collisions} now contains all singleton
+ * {@link IdentityNamespace osgi.identity} capabilities that can influence the
+ * ability of bundle revision {@code B} to resolve.
+ * <p/>
+ * If the bundle revision {@code B} is already resolved then any resolvable
+ * bundle revision contained in the collection {@code Collisions} is not allowed
+ * to resolve.</li>
+ * <li>During a resolve process a framework is free to attempt to resolve any or
+ * all bundles contained in shrinkable collection {@code Resolvable}. For each
+ * bundle revision {@code B} left in the shrinkable collection
+ * {@code Resolvable} which the framework attempts to resolve the following
+ * steps must be followed:
+ * <p/>
+ * For each requirement {@code R} specified by bundle revision {@code B}
+ * determine the collection of capabilities that satisfy (or match) the
+ * requirement and place each matching capability into a shrinkable collection
+ * {@code Candidates}. A capability is considered to match a particular
+ * requirement if its attributes satisfy a specified requirement and the
+ * requirer bundle has permission to access the capability.
+ * 
+ * <p/>
+ * For each resolver hook call the
+ * {@link #filterMatches(BundleRequirement, Collection)} with the requirement
+ * {@code R} and the shrinkable collection {@code Candidates}.
+ * 
+ * <p/>
+ * The shrinkable collection {@code Candidates} now contains all the
+ * capabilities that may be used to satisfy the requirement {@code R}. Any other
+ * capabilities that got removed from the shrinkable collection
+ * {@code Candidates} must not be used to satisfy requirement {@code R}.</li>
+ * <li>For each resolver hook call the {@link #end()} method to inform the hooks
+ * about a resolve process ending.</li>
+ * </ol>
+ * In all cases, the order in which the resolver hooks are called is the reverse
+ * compareTo ordering of their Service References. That is, the service with the
+ * highest ranking number must be called first. In cases where a shrinkable
+ * collection becomes empty the framework is required to call the remaining
+ * registered hooks.
+ * <p>
+ * Resolver hooks are low level. Implementations of the resolver hook must be
+ * careful not to create an unresolvable state which is very hard for a
+ * developer or a provisioner to diagnose. Resolver hooks also must not be
+ * allowed to start another synchronous resolve process (e.g. by calling
+ * {@link Bundle#start()} or {@link FrameworkWiring#resolveBundles(Collection)}
+ * ). The framework must detect this and throw an {@link IllegalStateException}.
+ * 
+ * @see ResolverHookFactory
+ * @NotThreadSafe
+ * @version $Id: 9d3ef6240aead0952b5a47b793780c1c0589089a $
+ */
+public interface ResolverHook {
+	/**
+	 * Filter resolvable candidates hook method. This method may be called
+	 * multiple times during a single resolve process. This method can filter
+	 * the collection of candidates by removing potential candidates. Removing a
+	 * candidate will prevent the candidate from resolving during the current
+	 * resolve process.
+	 * 
+	 * @param candidates the collection of resolvable candidates available
+	 *        during a resolve process.
+	 */
+	void filterResolvable(Collection<BundleRevision> candidates);
+
+	/**
+	 * Filter singleton collisions hook method. This method is called during the
+	 * resolve process for the specified singleton. The specified singleton
+	 * represents a singleton capability and the specified collection represent
+	 * a collection of singleton capabilities which are considered collision
+	 * candidates. The singleton capability and the collection of collision
+	 * candidates must all use the same namespace.
+	 * <p>
+	 * Currently only capabilities with the namespace of {@link BundleNamespace
+	 * osgi.wiring.bundle} and {@link IdentityNamespace osgi.identity} can be
+	 * singletons. The collision candidates will all have the same namespace, be
+	 * singletons, and have the same symbolic name as the specified singleton
+	 * capability.
+	 * <p>
+	 * In the future, capabilities in other namespaces may support the singleton
+	 * concept. Hook implementations should be prepared to receive calls to this
+	 * method for capabilities in namespaces other than {@link BundleNamespace
+	 * osgi.wiring.bundle} or {@link IdentityNamespace osgi.identity}.
+	 * <p>
+	 * This method can filter the list of collision candidates by removing
+	 * potential collisions. Removing a collision candidate will allow the
+	 * specified singleton to resolve regardless of the resolution state of the
+	 * removed collision candidate.
+	 * 
+	 * @param singleton the singleton involved in a resolve process
+	 * @param collisionCandidates a collection of singleton collision candidates
+	 */
+	void filterSingletonCollisions(BundleCapability singleton, Collection<BundleCapability> collisionCandidates);
+
+	/**
+	 * Filter matches hook method. This method is called during the resolve
+	 * process for the specified requirement. The collection of candidates match
+	 * the specified requirement. This method can filter the collection of
+	 * matching candidates by removing candidates from the collection. Removing
+	 * a candidate will prevent the resolve process from choosing the removed
+	 * candidate to satisfy the requirement.
+	 * <p>
+	 * All of the candidates will have the same namespace and will match the
+	 * specified requirement.
+	 * <p>
+	 * If the Java Runtime Environment supports permissions then the collection
+	 * of candidates will only contain candidates for which the requirer has
+	 * permission to access.
+	 * 
+	 * @param requirement the requirement to filter candidates for
+	 * @param candidates a collection of candidates that match the requirement
+	 */
+	void filterMatches(BundleRequirement requirement, Collection<BundleCapability> candidates);
+
+	/**
+	 * This method is called once at the end of the resolve process. After the
+	 * end method is called the resolve process has ended. The framework must
+	 * not hold onto this resolver hook instance after end has been called.
+	 */
+	void end();
+}
diff --git a/src/main/java/org/osgi/framework/hooks/resolver/ResolverHookFactory.java b/src/main/java/org/osgi/framework/hooks/resolver/ResolverHookFactory.java
new file mode 100644
index 0000000..91ce5f7
--- /dev/null
+++ b/src/main/java/org/osgi/framework/hooks/resolver/ResolverHookFactory.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) OSGi Alliance (2011, 2012). All Rights Reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.framework.hooks.resolver;
+
+import java.util.Collection;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.wiring.BundleRevision;
+import org.osgi.framework.wiring.FrameworkWiring;
+
+/**
+ * OSGi Framework Resolver Hook Factory Service.
+ * 
+ * <p>
+ * Bundles registering this service will be called by the framework during a
+ * bundle resolver process to obtain a {@link ResolverHook resolver hook}
+ * instance which will be used for the duration of a resolve process.
+ * 
+ * @ThreadSafe
+ * @see ResolverHook
+ * @version $Id: e0a2f3ad081c31bbb682fa366c15a3080bf6da2b $
+ */
+public interface ResolverHookFactory {
+	/**
+	 * This method is called by the framework each time a resolve process begins
+	 * to obtain a {@link ResolverHook resolver hook} instance. This resolver
+	 * hook instance will be used for the duration of the resolve process. At
+	 * the end of the resolve process the method {@link ResolverHook#end()} must
+	 * be called by the framework and the framework must not hold any references
+	 * of the resolver hook instance.
+	 * <p>
+	 * The triggers represent the collection of bundles which triggered the
+	 * resolve process. This collection may be empty if the triggers cannot be
+	 * determined by the framework. In most cases the triggers can easily be
+	 * determined. Calling certain methods on {@link Bundle bundle} when a
+	 * bundle is in the {@link Bundle#INSTALLED INSTALLED} state will cause the
+	 * framework to begin a resolve process in order to resolve the bundle. The
+	 * following methods will start a resolve process in this case:
+	 * <ul>
+	 * <li>{@link Bundle#start() start}</li>
+	 * <li>{@link Bundle#loadClass(String) loadClass}</li>
+	 * <li>{@link Bundle#findEntries(String, String, boolean) findEntries}</li>
+	 * <li>{@link Bundle#getResource(String) getResource}</li>
+	 * <li>{@link Bundle#getResources(String) getResources}</li>
+	 * </ul>
+	 * In such cases the collection will contain the single bundle which the
+	 * framework is trying to resolve. Other cases will cause multiple bundles
+	 * to be included in the trigger bundles collection. When
+	 * {@link FrameworkWiring#resolveBundles(Collection) resolveBundles} is
+	 * called the collection of triggers must include all the current bundle
+	 * revisions for bundles passed to resolveBundles which are in the
+	 * {@link Bundle#INSTALLED INSTALLED} state.
+	 * <p>
+	 * When
+	 * {@link FrameworkWiring#refreshBundles(Collection, org.osgi.framework.FrameworkListener...)}
+	 * is called the collection of triggers is determined with the following
+	 * steps:
+	 * <ul>
+	 * <li>If the collection of bundles passed is null then
+	 * {@link FrameworkWiring#getRemovalPendingBundles()} is called to get the
+	 * initial collection of bundles.</li>
+	 * <li>The equivalent of calling
+	 * {@link FrameworkWiring#getDependencyClosure(Collection)} is called with
+	 * the initial collection of bundles to get the dependency closure
+	 * collection of the bundles being refreshed.</li>
+	 * <li>Remove any non-active bundles from the dependency closure collection.
+	 * </li>
+	 * <li>For each bundle remaining in the dependency closure collection get
+	 * the current bundle revision and add it to the collection of triggers.</li>
+	 * </ul>
+	 * <p>
+	 * As described above, a resolve process is typically initiated as a result
+	 * of calling API that causes the framework to attempt to resolve one or
+	 * more bundles. The framework is free to start a resolve process at any
+	 * time for reasons other than calls to framework API. For example, a
+	 * resolve process may be used by the framework for diagnostic purposes and
+	 * result in no bundles actually becoming resolved at the end of the
+	 * process. Resolver hook implementations must be prepared for resolve
+	 * processes that are initiated for other reasons besides calls to framework
+	 * API.
+	 * 
+	 * @param triggers an unmodifiable collection of bundles which triggered the
+	 *        resolve process. This collection may be empty if the collection of
+	 *        trigger bundles cannot be determined.
+	 * @return a resolver hook instance to be used for the duration of the
+	 *         resolve process. A {@code null} value may be returned which
+	 *         indicates this resolver hook factory abstains from the resolve
+	 *         process.
+	 */
+	ResolverHook begin(Collection<BundleRevision> triggers);
+}
diff --git a/src/main/java/org/osgi/framework/hooks/service/EventHook.java b/src/main/java/org/osgi/framework/hooks/service/EventHook.java
new file mode 100644
index 0000000..03a84d4
--- /dev/null
+++ b/src/main/java/org/osgi/framework/hooks/service/EventHook.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) OSGi Alliance (2008, 2012). All Rights Reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.framework.hooks.service;
+
+import java.util.Collection;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceEvent;
+
+/**
+ * OSGi Framework Service Event Hook Service.
+ * 
+ * <p>
+ * Bundles registering this service will be called during framework service
+ * (register, modify, and unregister service) operations.
+ * 
+ * @ThreadSafe
+ * @deprecated As of 1.1. Replaced by {@link EventListenerHook}.
+ * @version $Id: 84757a5f719db4d7671e81a76af2b320404ae0f5 $
+ */
+
+public interface EventHook {
+	/**
+	 * Event hook method. This method is called prior to service event delivery
+	 * when a publishing bundle registers, modifies or unregisters a service.
+	 * This method can filter the bundles which receive the event.
+	 * 
+	 * @param event The service event to be delivered.
+	 * @param contexts A collection of Bundle Contexts for bundles which have
+	 *        listeners to which the specified event will be delivered. The
+	 *        implementation of this method may remove bundle contexts from the
+	 *        collection to prevent the event from being delivered to the
+	 *        associated bundles. The collection supports all the optional
+	 *        {@code Collection} operations except {@code add} and
+	 *        {@code addAll}. Attempting to add to the collection will result in
+	 *        an {@code UnsupportedOperationException}. The collection is not
+	 *        synchronized.
+	 */
+	void event(ServiceEvent event, Collection<BundleContext> contexts);
+}
diff --git a/src/main/java/org/osgi/framework/hooks/service/EventListenerHook.java b/src/main/java/org/osgi/framework/hooks/service/EventListenerHook.java
new file mode 100644
index 0000000..534efc5
--- /dev/null
+++ b/src/main/java/org/osgi/framework/hooks/service/EventListenerHook.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) OSGi Alliance (2010, 2012). All Rights Reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.framework.hooks.service;
+
+import java.util.Collection;
+import java.util.Map;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.hooks.service.ListenerHook.ListenerInfo;
+
+/**
+ * OSGi Framework Service Event Listener Hook Service.
+ * 
+ * <p>
+ * Bundles registering this service will be called during framework service
+ * (register, modify, and unregister service) operations.
+ * 
+ * @ThreadSafe
+ * @since 1.1
+ * @version $Id: b0b99b29206f272ad479fa08ffcd5ef5fda909b8 $
+ */
+
+public interface EventListenerHook {
+	/**
+	 * Event listener hook method. This method is called prior to service event
+	 * delivery when a publishing bundle registers, modifies or unregisters a
+	 * service. This method can filter the listeners which receive the event.
+	 * 
+	 * @param event The service event to be delivered.
+	 * @param listeners A map of Bundle Contexts to a collection of Listener
+	 *        Infos for the bundle's listeners to which the specified event will
+	 *        be delivered. The implementation of this method may remove bundle
+	 *        contexts from the map and listener infos from the collection
+	 *        values to prevent the event from being delivered to the associated
+	 *        listeners. The map supports all the optional {@code Map}
+	 *        operations except {@code put} and {@code putAll}. Attempting to
+	 *        add to the map will result in an
+	 *        {@code UnsupportedOperationException}. The collection values in
+	 *        the map supports all the optional {@code Collection} operations
+	 *        except {@code add} and {@code addAll}. Attempting to add to a
+	 *        collection will result in an {@code UnsupportedOperationException}
+	 *        . The map and the collections are not synchronized.
+	 */
+	void event(ServiceEvent event, Map<BundleContext, Collection<ListenerInfo>> listeners);
+}
diff --git a/src/main/java/org/osgi/framework/hooks/service/FindHook.java b/src/main/java/org/osgi/framework/hooks/service/FindHook.java
new file mode 100644
index 0000000..4cd467c
--- /dev/null
+++ b/src/main/java/org/osgi/framework/hooks/service/FindHook.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) OSGi Alliance (2008, 2012). All Rights Reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.framework.hooks.service;
+
+import java.util.Collection;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * OSGi Framework Service Find Hook Service.
+ * 
+ * <p>
+ * Bundles registering this service will be called during framework service find
+ * (get service references) operations.
+ * 
+ * @ThreadSafe
+ * @version $Id: 45612d6a10a25ca0b40ba695eb8dba21c2c78c24 $
+ */
+
+public interface FindHook {
+	/**
+	 * Find hook method. This method is called during the service find operation
+	 * (for example, {@link BundleContext#getServiceReferences(String, String)}
+	 * ). This method can filter the result of the find operation.
+	 * 
+	 * @param context The bundle context of the bundle performing the find
+	 *        operation.
+	 * @param name The class name of the services to find or {@code null} to
+	 *        find all services.
+	 * @param filter The filter criteria of the services to find or {@code null}
+	 *        for no filter criteria.
+	 * @param allServices {@code true} if the find operation is the result of a
+	 *        call to
+	 *        {@link BundleContext#getAllServiceReferences(String, String)}
+	 * @param references A collection of Service References to be returned as a
+	 *        result of the find operation. The implementation of this method
+	 *        may remove service references from the collection to prevent the
+	 *        references from being returned to the bundle performing the find
+	 *        operation. The collection supports all the optional
+	 *        {@code Collection} operations except {@code add} and
+	 *        {@code addAll}. Attempting to add to the collection will result in
+	 *        an {@code UnsupportedOperationException}. The collection is not
+	 *        synchronized.
+	 */
+	void find(BundleContext context, String name, String filter, boolean allServices, Collection<ServiceReference<?>> references);
+}
diff --git a/src/main/java/org/osgi/framework/hooks/service/ListenerHook.java b/src/main/java/org/osgi/framework/hooks/service/ListenerHook.java
new file mode 100644
index 0000000..ef933c3
--- /dev/null
+++ b/src/main/java/org/osgi/framework/hooks/service/ListenerHook.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) OSGi Alliance (2008, 2012). All Rights Reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.framework.hooks.service;
+
+import java.util.Collection;
+import org.osgi.framework.BundleContext;
+
+/**
+ * OSGi Framework Service Listener Hook Service.
+ * 
+ * <p>
+ * Bundles registering this service will be called during service listener
+ * addition and removal.
+ * 
+ * @ThreadSafe
+ * @version $Id: 94029e2b70119793b3e7d77d6e1d5052d9ee1723 $
+ */
+
+public interface ListenerHook {
+	/**
+	 * Added listeners hook method. This method is called to provide the hook
+	 * implementation with information on newly added service listeners. This
+	 * method will be called as service listeners are added while this hook is
+	 * registered. Also, immediately after registration of this hook, this
+	 * method will be called to provide the current collection of service
+	 * listeners which had been added prior to the hook being registered.
+	 * 
+	 * @param listeners A collection of {@link ListenerInfo}s for newly added
+	 *        service listeners which are now listening to service events.
+	 *        Attempting to add to or remove from the collection will result in
+	 *        an {@code UnsupportedOperationException}. The collection is not
+	 *        synchronized.
+	 */
+	void added(Collection<ListenerInfo> listeners);
+
+	/**
+	 * Removed listeners hook method. This method is called to provide the hook
+	 * implementation with information on newly removed service listeners. This
+	 * method will be called as service listeners are removed while this hook is
+	 * registered.
+	 * 
+	 * @param listeners A collection of {@link ListenerInfo}s for newly removed
+	 *        service listeners which are no longer listening to service events.
+	 *        Attempting to add to or remove from the collection will result in
+	 *        an {@code UnsupportedOperationException}. The collection is not
+	 *        synchronized.
+	 */
+	void removed(Collection<ListenerInfo> listeners);
+
+	/**
+	 * Information about a Service Listener. This interface describes the bundle
+	 * which added the Service Listener and the filter with which it was added.
+	 * 
+	 * @ThreadSafe
+	 * @noimplement
+	 */
+	public interface ListenerInfo {
+		/**
+		 * Return the context of the bundle which added the listener.
+		 * 
+		 * @return The context of the bundle which added the listener.
+		 */
+		BundleContext getBundleContext();
+
+		/**
+		 * Return the filter string with which the listener was added.
+		 * 
+		 * @return The filter string with which the listener was added. This may
+		 *         be {@code null} if the listener was added without a filter.
+		 */
+		String getFilter();
+
+		/**
+		 * Return the state of the listener for this addition and removal life
+		 * cycle. Initially this method will return {@code false} indicating the
+		 * listener has been added but has not been removed. After the listener
+		 * has been removed, this method must always return {@code true}.
+		 * 
+		 * <p>
+		 * There is an extremely rare case in which removed notification to
+		 * {@link ListenerHook}s can be made before added notification if two
+		 * threads are racing to add and remove the same service listener.
+		 * Because {@link ListenerHook}s are called synchronously during service
+		 * listener addition and removal, the Framework cannot guarantee
+		 * in-order delivery of added and removed notification for a given
+		 * service listener. This method can be used to detect this rare
+		 * occurrence.
+		 * 
+		 * @return {@code false} if the listener has not been been removed,
+		 *         {@code true} otherwise.
+		 */
+		boolean isRemoved();
+
+		/**
+		 * Compares this {@code ListenerInfo} to another {@code ListenerInfo}.
+		 * Two {@code ListenerInfo}s are equals if they refer to the same
+		 * listener for a given addition and removal life cycle. If the same
+		 * listener is added again, it must have a different
+		 * {@code ListenerInfo} which is not equal to this {@code ListenerInfo}.
+		 * 
+		 * @param obj The object to compare against this {@code ListenerInfo}.
+		 * @return {@code true} if the other object is a {@code ListenerInfo}
+		 *         object and both objects refer to the same listener for a
+		 *         given addition and removal life cycle.
+		 */
+		boolean equals(Object obj);
+
+		/**
+		 * Returns the hash code for this {@code ListenerInfo}.
+		 * 
+		 * @return The hash code of this {@code ListenerInfo}.
+		 */
+		int hashCode();
+	}
+}
diff --git a/src/main/java/org/osgi/framework/hooks/weaving/WeavingException.java b/src/main/java/org/osgi/framework/hooks/weaving/WeavingException.java
new file mode 100644
index 0000000..8842797
--- /dev/null
+++ b/src/main/java/org/osgi/framework/hooks/weaving/WeavingException.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) OSGi Alliance (2010). All Rights Reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.framework.hooks.weaving;
+
+/**
+ * A weaving exception used to indicate that the class load should be failed but
+ * the weaving hook must not be blacklisted by the framework.
+ * 
+ * <p>
+ * This exception conforms to the general purpose exception chaining mechanism.
+ * 
+ * @version $Id: eb38b85f6ed66ec445fb2f0ee7143df021327a9a $
+ */
+
+public class WeavingException extends RuntimeException {
+	private static final long	serialVersionUID	= 1L;
+
+	/**
+	 * Creates a {@code WeavingException} with the specified message and
+	 * exception cause.
+	 * 
+	 * @param msg The associated message.
+	 * @param cause The cause of this exception.
+	 */
+	public WeavingException(String msg, Throwable cause) {
+		super(msg, cause);
+	}
+
+	/**
+	 * Creates a {@code WeavingException} with the specified message.
+	 * 
+	 * @param msg The message.
+	 */
+	public WeavingException(String msg) {
+		super(msg);
+	}
+}
diff --git a/src/main/java/org/osgi/framework/hooks/weaving/WeavingHook.java b/src/main/java/org/osgi/framework/hooks/weaving/WeavingHook.java
new file mode 100644
index 0000000..be57658
--- /dev/null
+++ b/src/main/java/org/osgi/framework/hooks/weaving/WeavingHook.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) OSGi Alliance (2010). All Rights Reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.framework.hooks.weaving;
+
+/**
+ * OSGi Framework Weaving Hook Service.
+ * 
+ * <p>
+ * Bundles registering this service will be called during framework class
+ * loading operations. Weaving hook services are called when a class is being
+ * loaded by the framework and have an opportunity to transform the class file
+ * bytes that represents the class being loaded. Weaving hooks may also ask the
+ * framework to wire in additional dynamic imports to the bundle.
+ * 
+ * <p>
+ * When a class is being loaded, the framework will create a {@link WovenClass}
+ * object for the class and pass it to each registered weaving hook service for
+ * possible modification. The first weaving hook called will see the original
+ * class file bytes. Subsequently called weaving hooks will see the class file
+ * bytes as modified by previously called weaving hooks.
+ * 
+ * @ThreadSafe
+ * @version $Id: d1985029024baba2db1c56aab1e06ee953fd6365 $
+ */
+
+public interface WeavingHook {
+	/**
+	 * Weaving hook method.
+	 * 
+	 * This method can modify the specified woven class object to weave the
+	 * class being defined.
+	 * 
+	 * <p>
+	 * If this method throws any exception, the framework must log the exception
+	 * and fail the class load in progress. This weaving hook service must be
+	 * blacklisted by the framework and must not be called again. The
+	 * blacklisting of this weaving hook service must expire when this weaving
+	 * hook service is unregistered. However, this method can throw a
+	 * {@link WeavingException} to deliberately fail the class load in progress
+	 * without being blacklisted by the framework.
+	 * 
+	 * @param wovenClass The {@link WovenClass} object that represents the data
+	 *        that will be used to define the class.
+	 * @throws WeavingException If this weaving hook wants to deliberately fail
+	 *         the class load in progress without being blacklisted by the
+	 *         framework
+	 */
+	public void weave(WovenClass wovenClass);
+}
diff --git a/src/main/java/org/osgi/framework/hooks/weaving/WovenClass.java b/src/main/java/org/osgi/framework/hooks/weaving/WovenClass.java
new file mode 100644
index 0000000..697a435
--- /dev/null
+++ b/src/main/java/org/osgi/framework/hooks/weaving/WovenClass.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) OSGi Alliance (2010, 2012). All Rights Reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.framework.hooks.weaving;
+
+import java.security.ProtectionDomain;
+import java.util.List;
+import org.osgi.framework.wiring.BundleWiring;
+
+/**
+ * A class being woven.
+ * 
+ * This object represents a class being woven and is passed to each
+ * {@link WeavingHook} for possible modification. It allows access to the most
+ * recently transformed class file bytes and to any additional packages that
+ * should be added to the bundle as dynamic imports.
+ * 
+ * <p>
+ * After weaving is {@link #isWeavingComplete() complete}, this object becomes
+ * effectively immutable.
+ * 
+ * @NotThreadSafe
+ * @noimplement
+ * @version $Id: 549caef41027c8f0d0fdb4deae756eae6b69d1ee $
+ */
+public interface WovenClass {
+
+	/**
+	 * Returns the class file bytes to be used to define the
+	 * {@link WovenClass#getClassName() named} class.
+	 * 
+	 * <p>
+	 * While weaving is not {@link #isWeavingComplete() complete}, this method
+	 * returns a reference to the class files byte array contained in this
+	 * object. After weaving is {@link #isWeavingComplete() complete}, this
+	 * object becomes effectively immutable and a copy of the class file byte
+	 * array is returned.
+	 * 
+	 * @return The bytes to be used to define the
+	 *         {@link WovenClass#getClassName() named} class.
+	 * @throws SecurityException If the caller does not have
+	 *         {@code AdminPermission[bundle,WEAVE]} and the Java runtime
+	 *         environment supports permissions.
+	 */
+	public byte[] getBytes();
+
+	/**
+	 * Set the class file bytes to be used to define the
+	 * {@link WovenClass#getClassName() named} class. This method must not be
+	 * called outside invocations of the {@link WeavingHook#weave(WovenClass)
+	 * weave} method by the framework.
+	 * 
+	 * <p>
+	 * While weaving is not {@link #isWeavingComplete() complete}, this method
+	 * replaces the reference to the array contained in this object with the
+	 * specified array. After weaving is {@link #isWeavingComplete() complete},
+	 * this object becomes effectively immutable and this method will throw an
+	 * {@link IllegalStateException}.
+	 * 
+	 * @param newBytes The new classfile that will be used to define the
+	 *        {@link WovenClass#getClassName() named} class. The specified array
+	 *        is retained by this object and the caller must not modify the
+	 *        specified array.
+	 * @throws NullPointerException If newBytes is {@code null}.
+	 * @throws IllegalStateException If weaving is {@link #isWeavingComplete()
+	 *         complete}.
+	 * @throws SecurityException If the caller does not have
+	 *         {@code AdminPermission[bundle,WEAVE]} and the Java runtime
+	 *         environment supports permissions.
+	 */
+	public void setBytes(byte[] newBytes);
+
+	/**
+	 * Returns the list of dynamic import package descriptions to add to the
+	 * {@link #getBundleWiring() bundle wiring} for this woven class. Changes
+	 * made to the returned list will be visible to later {@link WeavingHook
+	 * weaving hooks} called with this object. The returned list must not be
+	 * modified outside invocations of the {@link WeavingHook#weave(WovenClass)
+	 * weave} method by the framework.
+	 * 
+	 * <p>
+	 * After weaving is {@link #isWeavingComplete() complete}, this object
+	 * becomes effectively immutable and the returned list will be unmodifiable.
+	 * 
+	 * <p>
+	 * If the Java runtime environment supports permissions, the caller must
+	 * have {@code AdminPermission[bundle,WEAVE]} to modify the returned list.
+	 * 
+	 * @return A list containing zero or more dynamic import package
+	 *         descriptions to add to the bundle wiring for this woven class.
+	 *         This list must throw {@code IllegalArgumentException} if a
+	 *         malformed dynamic import package description is added.
+	 * @see "Core Specification, Dynamic Import Package, for the syntax of a dynamic import package description."
+	 */
+	public List<String> getDynamicImports();
+
+	/**
+	 * Returns whether weaving is complete in this woven class. Weaving is
+	 * complete after the last {@link WeavingHook weaving hook} is called and
+	 * the class is defined.
+	 * 
+	 * <p>
+	 * After weaving is complete, this object becomes effectively immutable.
+	 * 
+	 * @return {@code true} weaving is complete, {@code false} otherwise.
+	 */
+	public boolean isWeavingComplete();
+
+	/**
+	 * Returns the fully qualified name of the class being woven.
+	 * 
+	 * @return The fully qualified name of the class being woven.
+	 */
+	public String getClassName();
+
+	/**
+	 * Returns the protection domain to which the woven class will be assigned
+	 * when it is defined.
+	 * 
+	 * @return The protection domain to which the woven class will be assigned
+	 *         when it is defined, or {@code null} if no protection domain will
+	 *         be assigned.
+	 */
+	public ProtectionDomain getProtectionDomain();
+
+	/**
+	 * Returns the class defined by this woven class. During weaving, this
+	 * method will return {@code null}. Once weaving is
+	 * {@link #isWeavingComplete() complete}, this method will return the class
+	 * object if this woven class was used to define the class.
+	 * 
+	 * @return The class associated with this woven class, or {@code null} if
+	 *         weaving is not complete, the class definition failed or this
+	 *         woven class was not used to define the class.
+	 */
+	public Class<?> getDefinedClass();
+
+	/**
+	 * Returns the bundle wiring whose class loader will define the woven class.
+	 * 
+	 * @return The bundle wiring whose class loader will define the woven class.
+	 */
+	public BundleWiring getBundleWiring();
+}
diff --git a/src/main/java/org/osgi/framework/launch/Framework.java b/src/main/java/org/osgi/framework/launch/Framework.java
new file mode 100644
index 0000000..54d599b
--- /dev/null
+++ b/src/main/java/org/osgi/framework/launch/Framework.java
@@ -0,0 +1,360 @@
+/*
+ * Copyright (c) OSGi Alliance (2008, 2012). All Rights Reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.framework.launch;
+
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Enumeration;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.Constants;
+import org.osgi.framework.FrameworkEvent;
+
+/**
+ * A Framework instance. A Framework is also known as a System Bundle.
+ * 
+ * <p>
+ * Framework instances are created using a {@link FrameworkFactory}. The methods
+ * of this interface can be used to manage and control the created framework
+ * instance.
+ * 
+ * @ThreadSafe
+ * @noimplement
+ * @version $Id: e76240d5de584d1666880d9bc358571a76cbd8fb $
+ */
+public interface Framework extends Bundle {
+
+	/**
+	 * Initialize this Framework. After calling this method, this Framework
+	 * must:
+	 * <ul>
+	 * <li>Have generated a new {@link Constants#FRAMEWORK_UUID framework UUID}.
+	 * </li>
+	 * <li>Be in the {@link #STARTING} state.</li>
+	 * <li>Have a valid Bundle Context.</li>
+	 * <li>Be at start level 0.</li>
+	 * <li>Have event handling enabled.</li>
+	 * <li>Have reified Bundle objects for all installed bundles.</li>
+	 * <li>Have registered any framework services. For example,
+	 * {@code ConditionalPermissionAdmin}.</li>
+	 * <li>Be {@link #adapt(Class) adaptable} to the OSGi defined types to which
+	 * a system bundle can be adapted.</li>
+	 * </ul>
+	 * 
+	 * <p>
+	 * This Framework will not actually be started until {@link #start() start}
+	 * is called.
+	 * 
+	 * <p>
+	 * This method does nothing if called when this Framework is in the
+	 * {@link #STARTING}, {@link #ACTIVE} or {@link #STOPPING} states.
+	 * 
+	 * @throws BundleException If this Framework could not be initialized.
+	 * @throws SecurityException If the Java Runtime Environment supports
+	 *         permissions and the caller does not have the appropriate
+	 *         {@code AdminPermission[this,EXECUTE]} or if there is a security
+	 *         manager already installed and the
+	 *         {@link Constants#FRAMEWORK_SECURITY} configuration property is
+	 *         set.
+	 * 
+	 */
+	void init() throws BundleException;
+
+	/**
+	 * Wait until this Framework has completely stopped. The {@code stop} and
+	 * {@code update} methods on a Framework performs an asynchronous stop of
+	 * the Framework. This method can be used to wait until the asynchronous
+	 * stop of this Framework has completed. This method will only wait if
+	 * called when this Framework is in the {@link #STARTING}, {@link #ACTIVE},
+	 * or {@link #STOPPING} states. Otherwise it will return immediately.
+	 * <p>
+	 * A Framework Event is returned to indicate why this Framework has stopped.
+	 * 
+	 * @param timeout Maximum number of milliseconds to wait until this
+	 *        Framework has completely stopped. A value of zero will wait
+	 *        indefinitely.
+	 * @return A Framework Event indicating the reason this method returned. The
+	 *         following {@code FrameworkEvent} types may be returned by this
+	 *         method.
+	 *         <ul>
+	 *         <li>{@link FrameworkEvent#STOPPED STOPPED} - This Framework has
+	 *         been stopped. </li>
+	 * 
+	 *         <li>{@link FrameworkEvent#STOPPED_UPDATE STOPPED_UPDATE} - This
+	 *         Framework has been updated which has shutdown and will now
+	 *         restart.</li>
+	 * 
+	 *         <li> {@link FrameworkEvent#STOPPED_BOOTCLASSPATH_MODIFIED
+	 *         STOPPED_BOOTCLASSPATH_MODIFIED} - This Framework has been stopped
+	 *         and a bootclasspath extension bundle has been installed or
+	 *         updated. The VM must be restarted in order for the changed boot
+	 *         class path to take effect. </li>
+	 * 
+	 *         <li>{@link FrameworkEvent#ERROR ERROR} - The Framework
+	 *         encountered an error while shutting down or an error has occurred
+	 *         which forced the framework to shutdown. </li>
+	 * 
+	 *         <li> {@link FrameworkEvent#WAIT_TIMEDOUT WAIT_TIMEDOUT} - This
+	 *         method has timed out and returned before this Framework has
+	 *         stopped.</li>
+	 *         </ul>
+	 * @throws InterruptedException If another thread interrupted the current
+	 *         thread before or while the current thread was waiting for this
+	 *         Framework to completely stop. The <i>interrupted status</i> of
+	 *         the current thread is cleared when this exception is thrown.
+	 * @throws IllegalArgumentException If the value of timeout is negative.
+	 */
+	FrameworkEvent waitForStop(long timeout) throws InterruptedException;
+
+	/**
+	 * Start this Framework.
+	 * 
+	 * <p>
+	 * The following steps are taken to start this Framework:
+	 * <ol>
+	 * <li>If this Framework is not in the {@link #STARTING} state,
+	 * {@link #init() initialize} this Framework.</li>
+	 * <li>All installed bundles must be started in accordance with each
+	 * bundle's persistent <i>autostart setting</i>. This means some bundles
+	 * will not be started, some will be started with <i>eager activation</i>
+	 * and some will be started with their <i>declared activation</i> policy.
+	 * The start level of this Framework is moved to the start level specified
+	 * by the {@link Constants#FRAMEWORK_BEGINNING_STARTLEVEL beginning start
+	 * level} framework property, as described in the <i>Start Level
+	 * Specification</i>. If this framework property is not specified, then the
+	 * start level of this Framework is moved to start level one (1). Any
+	 * exceptions that occur during bundle starting must be wrapped in a
+	 * {@link BundleException} and then published as a framework event of type
+	 * {@link FrameworkEvent#ERROR}</li>
+	 * <li>This Framework's state is set to {@link #ACTIVE}.</li>
+	 * <li>A framework event of type {@link FrameworkEvent#STARTED} is fired</li>
+	 * </ol>
+	 * 
+	 * @throws BundleException If this Framework could not be started.
+	 * @throws SecurityException If the caller does not have the appropriate
+	 *         {@code AdminPermission[this,EXECUTE]}, and the Java Runtime
+	 *         Environment supports permissions.
+	 * @see "Start Level Specification"
+	 */
+	void start() throws BundleException;
+
+	/**
+	 * Start this Framework.
+	 * 
+	 * <p>
+	 * Calling this method is the same as calling {@link #start()}. There are no
+	 * start options for the Framework.
+	 * 
+	 * @param options Ignored. There are no start options for the Framework.
+	 * @throws BundleException If this Framework could not be started.
+	 * @throws SecurityException If the caller does not have the appropriate
+	 *         {@code AdminPermission[this,EXECUTE]}, and the Java Runtime
+	 *         Environment supports permissions.
+	 * @see #start()
+	 */
+	void start(int options) throws BundleException;
+
+	/**
+	 * Stop this Framework.
+	 * 
+	 * <p>
+	 * The method returns immediately to the caller after initiating the
+	 * following steps to be taken on another thread.
+	 * <ol>
+	 * <li>This Framework's state is set to {@link #STOPPING}.</li>
+	 * <li>All installed bundles must be stopped without changing each bundle's
+	 * persistent <i>autostart setting</i>. The start level of this Framework is
+	 * moved to start level zero (0), as described in the <i>Start Level
+	 * Specification</i>. Any exceptions that occur during bundle stopping must
+	 * be wrapped in a {@link BundleException} and then published as a framework
+	 * event of type {@link FrameworkEvent#ERROR}</li>
+	 * <li>Unregister all services registered by this Framework.</li>
+	 * <li>Event handling is disabled.</li>
+	 * <li>This Framework's state is set to {@link #RESOLVED}.</li>
+	 * <li>All resources held by this Framework are released. This includes
+	 * threads, bundle class loaders, open files, etc.</li>
+	 * <li>Notify all threads that are waiting at {@link #waitForStop(long)
+	 * waitForStop} that the stop operation has completed.</li>
+	 * </ol>
+	 * <p>
+	 * After being stopped, this Framework may be discarded, initialized or
+	 * started.
+	 * 
+	 * @throws BundleException If stopping this Framework could not be
+	 *         initiated.
+	 * @throws SecurityException If the caller does not have the appropriate
+	 *         {@code AdminPermission[this,EXECUTE]}, and the Java Runtime
+	 *         Environment supports permissions.
+	 * @see "Start Level Specification"
+	 */
+	void stop() throws BundleException;
+
+	/**
+	 * Stop this Framework.
+	 * 
+	 * <p>
+	 * Calling this method is the same as calling {@link #stop()}. There are no
+	 * stop options for the Framework.
+	 * 
+	 * @param options Ignored. There are no stop options for the Framework.
+	 * @throws BundleException If stopping this Framework could not be
+	 *         initiated.
+	 * @throws SecurityException If the caller does not have the appropriate
+	 *         {@code AdminPermission[this,EXECUTE]}, and the Java Runtime
+	 *         Environment supports permissions.
+	 * @see #stop()
+	 */
+	void stop(int options) throws BundleException;
+
+	/**
+	 * The Framework cannot be uninstalled.
+	 * 
+	 * <p>
+	 * This method always throws a BundleException.
+	 * 
+	 * @throws BundleException This Framework cannot be uninstalled.
+	 * @throws SecurityException If the caller does not have the appropriate
+	 *         {@code AdminPermission[this,LIFECYCLE]}, and the Java Runtime
+	 *         Environment supports permissions.
+	 */
+	void uninstall() throws BundleException;
+
+	/**
+	 * Stop and restart this Framework.
+	 * 
+	 * <p>
+	 * The method returns immediately to the caller after initiating the
+	 * following steps to be taken on another thread.
+	 * <ol>
+	 * <li>Perform the steps in the {@link #stop()} method to stop this
+	 * Framework.</li>
+	 * <li>Perform the steps in the {@link #start()} method to start this
+	 * Framework.</li>
+	 * </ol>
+	 * 
+	 * @throws BundleException If stopping and restarting this Framework could
+	 *         not be initiated.
+	 * @throws SecurityException If the caller does not have the appropriate
+	 *         {@code AdminPermission[this,LIFECYCLE]}, and the Java Runtime
+	 *         Environment supports permissions.
+	 */
+	void update() throws BundleException;
+
+	/**
+	 * Stop and restart this Framework.
+	 * 
+	 * <p>
+	 * Calling this method is the same as calling {@link #update()} except that
+	 * any provided InputStream is immediately closed.
+	 * 
+	 * @param in Any provided InputStream is immediately closed before returning
+	 *        from this method and otherwise ignored.
+	 * @throws BundleException If stopping and restarting this Framework could
+	 *         not be initiated.
+	 * @throws SecurityException If the caller does not have the appropriate
+	 *         {@code AdminPermission[this,LIFECYCLE]}, and the Java Runtime
+	 *         Environment supports permissions.
+	 */
+	void update(InputStream in) throws BundleException;
+
+	/**
+	 * Returns the Framework unique identifier. This Framework is assigned the
+	 * unique identifier zero (0) since this Framework is also a System Bundle.
+	 * 
+	 * @return 0.
+	 * @see Bundle#getBundleId()
+	 */
+	long getBundleId();
+
+	/**
+	 * Returns the Framework location identifier. This Framework is assigned the
+	 * unique location "{@code System Bundle}" since this Framework is
+	 * also a System Bundle.
+	 * 
+	 * @return The string "{@code System Bundle}".
+	 * @throws SecurityException If the caller does not have the appropriate
+	 *         {@code AdminPermission[this,METADATA]}, and the Java Runtime
+	 *         Environment supports permissions.
+	 * @see Bundle#getLocation()
+	 * @see Constants#SYSTEM_BUNDLE_LOCATION
+	 */
+	String getLocation();
+
+	/**
+	 * Returns the symbolic name of this Framework. The symbolic name is unique
+	 * for the implementation of the framework. However, the symbolic name
+	 * "{@code system.bundle}" must be recognized as an alias to the
+	 * implementation-defined symbolic name since this Framework is also a
+	 * System Bundle.
+	 * 
+	 * @return The symbolic name of this Framework.
+	 * @see Bundle#getSymbolicName()
+	 * @see Constants#SYSTEM_BUNDLE_SYMBOLICNAME
+	 */
+	String getSymbolicName();
+
+	/**
+	 * Returns {@code null} as a framework implementation does not have a proper
+	 * bundle from which to return entry paths.
+	 * 
+	 * @param path Ignored.
+	 * @return {@code null} as a framework implementation does not have a proper
+	 *         bundle from which to return entry paths.
+	 */
+	Enumeration<String> getEntryPaths(String path);
+
+	/**
+	 * Returns {@code null} as a framework implementation does not have a proper
+	 * bundle from which to return an entry.
+	 * 
+	 * @param path Ignored.
+	 * @return {@code null} as a framework implementation does not have a proper
+	 *         bundle from which to return an entry.
+	 */
+	URL getEntry(String path);
+
+	/**
+	 * Returns {@code null} as a framework implementation does not have a proper
+	 * bundle from which to return entries.
+	 * 
+	 * @param path Ignored.
+	 * @param filePattern Ignored.
+	 * @param recurse Ignored.
+	 * @return {@code null} as a framework implementation does not have a proper
+	 *         bundle from which to return entries.
+	 */
+	Enumeration<URL> findEntries(String path, String filePattern, boolean recurse);
+
+	/**
+	 * Adapt this Framework to the specified type.
+	 * 
+	 * <p>
+	 * Adapting this Framework to the specified type may require certain checks,
+	 * including security checks, to succeed. If a check does not succeed, then
+	 * this Framework cannot be adapted and {@code null} is returned. If this
+	 * Framework is not {@link #init() initialized}, then {@code null} is
+	 * returned if the specified type is one of the OSGi defined types to which
+	 * a system bundle can be adapted.
+	 * 
+	 * @param <A> The type to which this Framework is to be adapted.
+	 * @param type Class object for the type to which this Framework is to be
+	 *        adapted.
+	 * @return The object, of the specified type, to which this Framework has
+	 *         been adapted or {@code null} if this Framework cannot be adapted
+	 */
+	<A> A adapt(Class<A> type);
+}
diff --git a/src/main/java/org/osgi/framework/launch/FrameworkFactory.java b/src/main/java/org/osgi/framework/launch/FrameworkFactory.java
new file mode 100644
index 0000000..bac0183
--- /dev/null
+++ b/src/main/java/org/osgi/framework/launch/FrameworkFactory.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) OSGi Alliance (2009, 2012). All Rights Reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.framework.launch;
+
+import java.util.Map;
+import org.osgi.framework.Bundle;
+
+/**
+ * A factory for creating {@link Framework} instances.
+ * 
+ * <p>
+ * A framework implementation jar must contain the following resource:
+ * 
+ * <pre>
+ * /META-INF/services/org.osgi.framework.launch.FrameworkFactory
+ * </pre>
+ * 
+ * This UTF-8 encoded resource must contain the name of the framework
+ * implementation's FrameworkFactory implementation class. Space and tab
+ * characters, including blank lines, in the resource must be ignored. The
+ * number sign ({@code '#'} \u0023) and all characters following it on each
+ * line are a comment and must be ignored.
+ * 
+ * <p>
+ * Launchers can find the name of the FrameworkFactory implementation class in
+ * the resource and then load and construct a FrameworkFactory object for the
+ * framework implementation. The FrameworkFactory implementation class must have
+ * a public, no-argument constructor. Java™ SE 6 introduced the
+ * {@code ServiceLoader} class which can create a FrameworkFactory instance from
+ * the resource.
+ * 
+ * @ThreadSafe
+ * @noimplement
+ * @version $Id: 1684e14aa98a1f6e1ff3e0f3afa2c55982210f72 $
+ */
+public interface FrameworkFactory {
+
+	/**
+	 * Create a new {@link Framework} instance.
+	 * 
+	 * @param configuration The framework properties to configure the new
+	 *        framework instance. If framework properties are not provided by
+	 *        the configuration argument, the created framework instance must
+	 *        use some reasonable default configuration appropriate for the
+	 *        current VM. For example, the system packages for the current
+	 *        execution environment should be properly exported. The specified
+	 *        configuration argument may be {@code null}. The created framework
+	 *        instance must copy any information needed from the specified
+	 *        configuration argument since the configuration argument can be
+	 *        changed after the framework instance has been created.
+	 * @return A new, configured {@link Framework} instance. The framework
+	 *         instance must be in the {@link Bundle#INSTALLED} state.
+	 * @throws SecurityException If the caller does not have
+	 *         {@code AllPermission}, and the Java Runtime Environment supports
+	 *         permissions.
+	 */
+	Framework newFramework(Map<String, String> configuration);
+}
diff --git a/src/main/java/org/osgi/framework/namespace/AbstractWiringNamespace.java b/src/main/java/org/osgi/framework/namespace/AbstractWiringNamespace.java
new file mode 100644
index 0000000..52563e2
--- /dev/null
+++ b/src/main/java/org/osgi/framework/namespace/AbstractWiringNamespace.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) OSGi Alliance (2012). All Rights Reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.framework.namespace;
+
+import org.osgi.resource.Namespace;
+
+/**
+ * Wiring Capability and Requirement Namespaces base class.
+ * 
+ * <p>
+ * This class is the common class shared by all OSGi defined wiring namespaces.
+ * It defines the names for the common attributes and directives for the OSGi
+ * specified wiring namespaces.
+ * 
+ * <p>
+ * The values associated with these keys are of type {@code String}, unless
+ * otherwise indicated.
+ * 
+ * @Immutable
+ * @version $Id: 383e84df9190757ce6bb6fb722e80a3b7d6b68da $
+ */
+public abstract class AbstractWiringNamespace extends Namespace {
+
+	/**
+	 * The capability directive used to specify the comma separated list of
+	 * mandatory attributes which must be specified in the
+	 * {@link Namespace#REQUIREMENT_FILTER_DIRECTIVE filter} of a requirement in
+	 * order for the capability to match the requirement.
+	 */
+	public final static String	CAPABILITY_MANDATORY_DIRECTIVE		= "mandatory";
+
+	/**
+	 * The capability attribute contains the {@code Version} of the resource
+	 * providing the capability if one is specified or {@code 0.0.0} if not
+	 * specified. The value of this attribute must be of type {@code Version}.
+	 */
+	public static final String	CAPABILITY_BUNDLE_VERSION_ATTRIBUTE	= "bundle-version";
+
+	AbstractWiringNamespace() {
+		// empty
+	}
+}
diff --git a/src/main/java/org/osgi/framework/namespace/BundleNamespace.java b/src/main/java/org/osgi/framework/namespace/BundleNamespace.java
new file mode 100644
index 0000000..84b6700
--- /dev/null
+++ b/src/main/java/org/osgi/framework/namespace/BundleNamespace.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) OSGi Alliance (2012). All Rights Reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.framework.namespace;
+
+import org.osgi.resource.Namespace;
+
+/**
+ * Bundle Capability and Requirement Namespace.
+ * 
+ * <p>
+ * This class defines the names for the attributes and directives for this
+ * namespace. All unspecified capability attributes are of type {@code String}
+ * and are used as arbitrary matching attributes for the capability. The values
+ * associated with the specified directive and attribute keys are of type
+ * {@code String}, unless otherwise indicated.
+ * 
+ * <p>
+ * Unless otherwise noted, all directives specified on the
+ * {@code Bundle-SymbolicName} header are visible in the capability and all
+ * directives specified on the {@code Require-Bundle} header are visible in the
+ * requirement.
+ * 
+ * <ul>
+ * <li>The {@link Namespace#CAPABILITY_USES_DIRECTIVE uses} directive must be
+ * ignored. A {@code uses} directive specified on the
+ * {@code Bundle-SymbolicName} header must be ignored. A {@code uses} directive
+ * must not be present in the capability.</li>
+ * <li>The {@link Namespace#CAPABILITY_EFFECTIVE_DIRECTIVE effective}
+ * {@link Namespace#REQUIREMENT_EFFECTIVE_DIRECTIVE directives} must be ignored.
+ * This namespace is only effective at {@link Namespace#EFFECTIVE_RESOLVE
+ * resolve} time. An {@code effective} directive specified on the
+ * {@code Bundle-SymbolicName} or {@code Require-Bundle} headers must be
+ * ignored. An {@code effective} directive must not be present in a capability
+ * or requirement.</li>
+ * <li>The {@link Namespace#REQUIREMENT_CARDINALITY_DIRECTIVE cardinality}
+ * directive must be ignored. A {@code cardinality} directive specified on the
+ * {@code Require-Bundle} header must be ignored. A {@code cardinality}
+ * directive must not be present in a requirement.</li>
+ * </ul>
+ * 
+ * <p>
+ * A non-fragment resource with the {@link IdentityNamespace#TYPE_BUNDLE
+ * osgi.bundle} type {@link IdentityNamespace#CAPABILITY_TYPE_ATTRIBUTE
+ * identity} provides exactly one<sup>†</sup> bundle capability (that is,
+ * the bundle can be required by another bundle). A fragment resource with the
+ * {@link IdentityNamespace#TYPE_FRAGMENT osgi.fragment} type
+ * {@link IdentityNamespace#CAPABILITY_TYPE_ATTRIBUTE identity} must not declare
+ * a bundle capability. A resource requires zero or more bundle requirements
+ * (that is, required bundles).
+ * <p>
+ * † A resource with no symbolic name must not provide a bundle
+ * capability.
+ * 
+ * @Immutable
+ * @version $Id: 339f1204725aa9d9c2463b1224b2e38e505024e9 $
+ */
+public final class BundleNamespace extends AbstractWiringNamespace {
+
+	/**
+	 * Namespace name for bundle capabilities and requirements.
+	 * 
+	 * <p>
+	 * Also, the capability attribute used to specify the symbolic name of the
+	 * bundle.
+	 */
+	public static final String	BUNDLE_NAMESPACE							= "osgi.wiring.bundle";
+
+	/**
+	 * The capability directive identifying if the resource is a singleton. A
+	 * {@code String} value of "{@code true}" indicates the resource
+	 * is a singleton; any other value or {@code null} indicates the resource is
+	 * not a singleton.
+	 * 
+	 * <p>
+	 * This directive should be examined using the {@link IdentityNamespace
+	 * identity} namespace.
+	 * 
+	 * @see IdentityNamespace#CAPABILITY_SINGLETON_DIRECTIVE
+	 */
+	public static final String	CAPABILITY_SINGLETON_DIRECTIVE				= "singleton";
+
+	/**
+	 * The capability directive identifying if and when a fragment may attach to
+	 * a host bundle.
+	 * 
+	 * <p>
+	 * This directive should be examined using the {@link HostNamespace host}
+	 * namespace.
+	 * 
+	 * @see HostNamespace#CAPABILITY_FRAGMENT_ATTACHMENT_DIRECTIVE
+	 */
+	public static final String	CAPABILITY_FRAGMENT_ATTACHMENT_DIRECTIVE	= "fragment-attachment";
+
+	/**
+	 * The requirement directive used to specify the type of the extension
+	 * fragment.
+	 * 
+	 * <p>
+	 * This directive should be examined using the {@link HostNamespace host}
+	 * namespace.
+	 * 
+	 * @see HostNamespace#REQUIREMENT_EXTENSION_DIRECTIVE
+	 */
+	public final static String	REQUIREMENT_EXTENSION_DIRECTIVE				= "extension";
+
+	/**
+	 * The requirement directive used to specify the visibility type for a
+	 * requirement. The default value is {@link #VISIBILITY_PRIVATE private}.
+	 * 
+	 * @see #VISIBILITY_PRIVATE private
+	 * @see #VISIBILITY_REEXPORT reexport
+	 */
+	public final static String	REQUIREMENT_VISIBILITY_DIRECTIVE			= "visibility";
+
+	/**
+	 * The directive value identifying a private
+	 * {@link #REQUIREMENT_VISIBILITY_DIRECTIVE visibility} type. A private
+	 * visibility type indicates that any {@link PackageNamespace packages} that
+	 * are exported by the required bundle are not made visible on the export
+	 * signature of the requiring bundle. .
+	 * 
+	 * @see #REQUIREMENT_VISIBILITY_DIRECTIVE
+	 */
+	public final static String	VISIBILITY_PRIVATE							= "private";
+
+	/**
+	 * The directive value identifying a reexport
+	 * {@link #REQUIREMENT_VISIBILITY_DIRECTIVE visibility} type. A reexport
+	 * visibility type indicates any {@link PackageNamespace packages} that are
+	 * exported by the required bundle are re-exported by the requiring bundle.
+	 * 
+	 * @see #REQUIREMENT_VISIBILITY_DIRECTIVE
+	 */
+	public final static String	VISIBILITY_REEXPORT							= "reexport";
+
+	private BundleNamespace() {
+		// empty
+	}
+}
diff --git a/src/main/java/org/osgi/framework/namespace/ExecutionEnvironmentNamespace.java b/src/main/java/org/osgi/framework/namespace/ExecutionEnvironmentNamespace.java
new file mode 100644
index 0000000..5328cae
--- /dev/null
+++ b/src/main/java/org/osgi/framework/namespace/ExecutionEnvironmentNamespace.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) OSGi Alliance (2012). All Rights Reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.framework.namespace;
+
+import org.osgi.resource.Namespace;
+
+/**
+ * Execution Environment Capability and Requirement Namespace.
+ * 
+ * <p>
+ * This class defines the names for the attributes and directives for this
+ * namespace. All unspecified capability attributes are of type {@code String}
+ * and are used as arbitrary matching attributes for the capability. The values
+ * associated with the specified directive and attribute keys are of type
+ * {@code String}, unless otherwise indicated.
+ * 
+ * @Immutable
+ * @version $Id: e1c30aac8efacc1b21ab20ffebcc1af30a1054a8 $
+ */
+public final class ExecutionEnvironmentNamespace extends Namespace {
+
+	/**
+	 * Namespace name for execution environment capabilities and requirements.
+	 * 
+	 * <p>
+	 * Also, the capability attribute used to specify the name of the execution
+	 * environment.
+	 */
+	public static final String	EXECUTION_ENVIRONMENT_NAMESPACE	= "osgi.ee";
+
+	/**
+	 * The capability attribute contains the versions of the execution
+	 * environment. The value of this attribute must be of type
+	 * {@code List<Version>}.
+	 */
+	public final static String	CAPABILITY_VERSION_ATTRIBUTE	= "version";
+
+	private ExecutionEnvironmentNamespace() {
+		// empty
+	}
+}
diff --git a/src/main/java/org/osgi/framework/namespace/HostNamespace.java b/src/main/java/org/osgi/framework/namespace/HostNamespace.java
new file mode 100644
index 0000000..5964cf5
--- /dev/null
+++ b/src/main/java/org/osgi/framework/namespace/HostNamespace.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (c) OSGi Alliance (2012). All Rights Reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.framework.namespace;
+
+import org.osgi.resource.Namespace;
+
+/**
+ * Host Capability and Requirement Namespace.
+ * 
+ * <p>
+ * This class defines the names for the attributes and directives for this
+ * namespace. All unspecified capability attributes are of type {@code String}
+ * and are used as arbitrary matching attributes for the capability. The values
+ * associated with the specified directive and attribute keys are of type
+ * {@code String}, unless otherwise indicated.
+ * 
+ * <p>
+ * Unless otherwise noted, all directives specified on the
+ * {@code Bundle-SymbolicName} header are visible in the capability and all
+ * directives specified on the {@code Fragment-Host} header are visible in the
+ * requirement.
+ * 
+ * <ul>
+ * <li>The {@link Namespace#CAPABILITY_USES_DIRECTIVE uses} directive must be
+ * ignored. A {@code uses} directive specified on the
+ * {@code Bundle-SymbolicName} header must be ignored. A {@code uses} directive
+ * must not be present in the capability.</li>
+ * <li>The {@link Namespace#CAPABILITY_EFFECTIVE_DIRECTIVE effective}
+ * {@link Namespace#REQUIREMENT_EFFECTIVE_DIRECTIVE directives} must be ignored.
+ * This namespace is only effective at {@link Namespace#EFFECTIVE_RESOLVE
+ * resolve} time. An {@code effective} directive specified on the
+ * {@code Bundle-SymbolicName} or {@code Fragment-Host} headers must be ignored.
+ * An {@code effective} directive must not be present in a capability or
+ * requirement.</li>
+ * <li>The {@link Namespace#REQUIREMENT_CARDINALITY_DIRECTIVE cardinality}
+ * directive has limited applicability to this namespace. A {@code cardinality}
+ * directive specified on the {@code Fragment-Host} header must be ignored. All
+ * requirements must have the {@code cardinality} directive set to
+ * {@link Namespace#CARDINALITY_MULTIPLE multiple}.</li>
+ * </ul>
+ * 
+ * <p>
+ * A non-fragment resource with the with the
+ * {@link IdentityNamespace#TYPE_BUNDLE osgi.bundle} type
+ * {@link IdentityNamespace#CAPABILITY_TYPE_ATTRIBUTE identity} provides zero or
+ * one<sup>†</sup> host capabilities. A fragment resource with the
+ * {@link IdentityNamespace#TYPE_FRAGMENT osgi.fragment} type
+ * {@link IdentityNamespace#CAPABILITY_TYPE_ATTRIBUTE identity} must not declare
+ * a host capability and must declare exactly one host requirement.
+ * <p>
+ * † A resource with no bundle symbolic name must not provide a host
+ * capability.
+ * 
+ * @Immutable
+ * @version $Id: aa3cc744c7b9c21d908260f456567ab8a6de1430 $
+ */
+public final class HostNamespace extends AbstractWiringNamespace {
+
+	/**
+	 * Namespace name for host capabilities and requirements.
+	 * 
+	 * <p>
+	 * Also, the capability attribute used to specify the symbolic name of the
+	 * host.
+	 * 
+	 */
+	public static final String	HOST_NAMESPACE								= "osgi.wiring.host";
+
+	/**
+	 * The capability directive identifying if the resource is a singleton. A
+	 * {@code String} value of "{@code true}" indicates the resource
+	 * is a singleton; any other value or {@code null} indicates the resource is
+	 * not a singleton.
+	 * 
+	 * <p>
+	 * This directive should be examined using the {@link IdentityNamespace
+	 * identity} namespace.
+	 * 
+	 * @see IdentityNamespace#CAPABILITY_SINGLETON_DIRECTIVE
+	 */
+	public static final String	CAPABILITY_SINGLETON_DIRECTIVE				= "singleton";
+
+	/**
+	 * The capability directive identifying if and when a fragment may attach to
+	 * a host bundle. The default value is {@link #FRAGMENT_ATTACHMENT_ALWAYS
+	 * always}.
+	 * 
+	 * @see #FRAGMENT_ATTACHMENT_ALWAYS
+	 * @see #FRAGMENT_ATTACHMENT_RESOLVETIME
+	 * @see #FRAGMENT_ATTACHMENT_NEVER
+	 */
+	public static final String	CAPABILITY_FRAGMENT_ATTACHMENT_DIRECTIVE	= "fragment-attachment";
+
+	/**
+	 * The directive value indicating that fragments are allowed to attach to
+	 * the host bundle at any time (while the host is resolved or during the
+	 * process of resolving the host bundle).
+	 * 
+	 * @see #CAPABILITY_FRAGMENT_ATTACHMENT_DIRECTIVE
+	 */
+	public static final String	FRAGMENT_ATTACHMENT_ALWAYS					= "always";
+
+	/**
+	 * The directive value indicating that fragments are allowed to attach to
+	 * the host bundle only during the process of resolving the host bundle.
+	 * 
+	 * @see #CAPABILITY_FRAGMENT_ATTACHMENT_DIRECTIVE
+	 */
+	public static final String	FRAGMENT_ATTACHMENT_RESOLVETIME				= "resolve-time";
+
+	/**
+	 * The directive value indicating that no fragments are allowed to attach to
+	 * the host bundle at any time.
+	 * 
+	 * @see #CAPABILITY_FRAGMENT_ATTACHMENT_DIRECTIVE
+	 */
+	public static final String	FRAGMENT_ATTACHMENT_NEVER					= "never";
+
+	/**
+	 * The requirement directive used to specify the type of the extension
+	 * fragment.
+	 * 
+	 * @see #EXTENSION_FRAMEWORK
+	 * @see #EXTENSION_BOOTCLASSPATH
+	 */
+	public final static String	REQUIREMENT_EXTENSION_DIRECTIVE				= "extension";
+
+	/**
+	 * The directive value indicating that the extension fragment is to be
+	 * loaded by the framework's class loader.
+	 * 
+	 * 
+	 * @see #REQUIREMENT_EXTENSION_DIRECTIVE
+	 */
+	public final static String	EXTENSION_FRAMEWORK							= "framework";
+
+	/**
+	 * The directive value indicating that the extension fragment is to be
+	 * loaded by the boot class loader.
+	 * 
+	 * @see #REQUIREMENT_EXTENSION_DIRECTIVE
+	 */
+	public final static String	EXTENSION_BOOTCLASSPATH						= "bootclasspath";
+
+	/**
+	 * The requirement directive used to specify the visibility type for a
+	 * requirement.
+	 * 
+	 * <p>
+	 * This directive should be examined using the {@link BundleNamespace
+	 * bundle} namespace.
+	 * 
+	 * @see BundleNamespace#REQUIREMENT_VISIBILITY_DIRECTIVE
+	 */
+	public final static String	REQUIREMENT_VISIBILITY_DIRECTIVE			= "visibility";
+
+	private HostNamespace() {
+		// empty
+	}
+}
diff --git a/src/main/java/org/osgi/framework/namespace/IdentityNamespace.java b/src/main/java/org/osgi/framework/namespace/IdentityNamespace.java
new file mode 100644
index 0000000..c925e5a
--- /dev/null
+++ b/src/main/java/org/osgi/framework/namespace/IdentityNamespace.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) OSGi Alliance (2012). All Rights Reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.framework.namespace;
+
+import org.osgi.resource.Namespace;
+
+/**
+ * Identity Capability and Requirement Namespace.
+ * 
+ * <p>
+ * This class defines the names for the attributes and directives for this
+ * namespace. All unspecified capability attributes are of type {@code String}
+ * and are used as arbitrary matching attributes for the capability. The values
+ * associated with the specified directive and attribute keys are of type
+ * {@code String}, unless otherwise indicated.
+ * 
+ * <p>
+ * Each resource provides exactly one<sup>†</sup> identity capability that
+ * can be used to identify the resource.
+ * 
+ * <p>
+ * The bundle wiring for the bundle revision provides exactly
+ * one<sup>†</sup> identity capability.
+ * 
+ * <p>
+ * † A resource with no symbolic name must not provide an identity
+ * capability.
+ * 
+ * @Immutable
+ * @version $Id: e34dcaba1f828326a0db13b3d811b2d170ff97a5 $
+ */
+public final class IdentityNamespace extends Namespace {
+
+	/**
+	 * Namespace name for identity capabilities and requirements.
+	 * 
+	 * <p>
+	 * Also, the capability attribute used to specify the symbolic name of the
+	 * resource.
+	 */
+	public static final String	IDENTITY_NAMESPACE					= "osgi.identity";
+
+	/**
+	 * The capability directive identifying if the resource is a singleton. A
+	 * {@code String} value of "true" indicates the resource is a
+	 * singleton; any other value or {@code null} indicates the resource is not
+	 * a singleton.
+	 */
+	public static final String	CAPABILITY_SINGLETON_DIRECTIVE		= "singleton";
+
+	/**
+	 * The capability attribute identifying the {@code Version} of the resource
+	 * if one is specified or {@code 0.0.0} if not specified. The value of this
+	 * attribute must be of type {@code Version}.
+	 */
+	public static final String	CAPABILITY_VERSION_ATTRIBUTE		= "version";
+
+	/**
+	 * The capability attribute identifying the resource type. If the resource
+	 * has no type then the value {@link #TYPE_UNKNOWN unknown} must be used for
+	 * the attribute.
+	 * 
+	 * @see #TYPE_BUNDLE
+	 * @see #TYPE_FRAGMENT
+	 * @see #TYPE_UNKNOWN
+	 */
+	public static final String	CAPABILITY_TYPE_ATTRIBUTE			= "type";
+
+	/**
+	 * The attribute value identifying the resource
+	 * {@link #CAPABILITY_TYPE_ATTRIBUTE type} as an OSGi bundle.
+	 * 
+	 * @see #CAPABILITY_TYPE_ATTRIBUTE
+	 */
+	public static final String	TYPE_BUNDLE							= "osgi.bundle";
+
+	/**
+	 * The attribute value identifying the resource
+	 * {@link #CAPABILITY_TYPE_ATTRIBUTE type} as an OSGi fragment.
+	 * 
+	 * @see #CAPABILITY_TYPE_ATTRIBUTE
+	 */
+	public static final String	TYPE_FRAGMENT						= "osgi.fragment";
+
+	/**
+	 * The attribute value identifying the resource
+	 * {@link #CAPABILITY_TYPE_ATTRIBUTE type} as unknown.
+	 * 
+	 * @see #CAPABILITY_TYPE_ATTRIBUTE
+	 */
+	public static final String	TYPE_UNKNOWN						= "unknown";
+
+	/**
+	 * The capability attribute that contains a human readable copyright notice
+	 * for the resource. See the {@code Bundle-Copyright} manifest header.
+	 */
+	public static final String	CAPABILITY_COPYRIGHT_ATTRIBUTE		= "copyright";
+
+	/**
+	 * The capability attribute that contains a human readable description for
+	 * the resource. See the {@code Bundle-Description} manifest header.
+	 */
+	public static final String	CAPABILITY_DESCRIPTION_ATTRIBUTE	= "description";
+
+	/**
+	 * The capability attribute that contains the URL to documentation for the
+	 * resource. See the {@code Bundle-DocURL} manifest header.
+	 */
+	public static final String	CAPABILITY_DOCUMENTATION_ATTRIBUTE	= "documentation";
+
+	/**
+	 * The capability attribute that contains the URL to the license for the
+	 * resource. See the {@code name} portion of the {@code Bundle-License}
+	 * manifest header.
+	 */
+	public static final String	CAPABILITY_LICENSE_ATTRIBUTE		= "license";
+
+	/**
+	 * The requirement directive that classifies the relationship with another
+	 * resource.
+	 * 
+	 * @see #CLASSIFIER_SOURCES
+	 * @see #CLASSIFIER_JAVADOC
+	 */
+	public static final String	REQUIREMENT_CLASSIFIER_DIRECTIVE	= "classifier";
+
+	/**
+	 * The attribute value identifying the resource
+	 * {@link #REQUIREMENT_CLASSIFIER_DIRECTIVE classifier} as an archive
+	 * containing the source code in the same directory layout as the resource.
+	 * 
+	 * @see #REQUIREMENT_CLASSIFIER_DIRECTIVE
+	 */
+
+	public static final String	CLASSIFIER_SOURCES					= "sources";
+	/**
+	 * The attribute value identifying the resource
+	 * {@link #REQUIREMENT_CLASSIFIER_DIRECTIVE classifier} as an archive
+	 * containing the javadoc in the same directory layout as the resource.
+	 * 
+	 * @see #REQUIREMENT_CLASSIFIER_DIRECTIVE
+	 */
+	public static final String	CLASSIFIER_JAVADOC					= "javadoc";
+
+	private IdentityNamespace() {
+		// empty
+	}
+}
diff --git a/src/main/java/org/osgi/framework/namespace/PackageNamespace.java b/src/main/java/org/osgi/framework/namespace/PackageNamespace.java
new file mode 100644
index 0000000..2d1cbc3
--- /dev/null
+++ b/src/main/java/org/osgi/framework/namespace/PackageNamespace.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) OSGi Alliance (2012). All Rights Reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.framework.namespace;
+
+import org.osgi.resource.Namespace;
+
+/**
+ * Package Capability and Requirement Namespace.
+ * 
+ * <p>
+ * A resource provides zero or more package capabilities (this is, exported
+ * packages) and requires zero or more package requirements (that is, imported
+ * packages).
+ * 
+ * <p>
+ * This class defines the names for the attributes and directives for this
+ * namespace. All unspecified capability attributes are of type {@code String}
+ * and are used as arbitrary matching attributes for the capability. The values
+ * associated with the specified directive and attribute keys are of type
+ * {@code String}, unless otherwise indicated.
+ * 
+ * <p>
+ * Unless otherwise noted, all directives specified on the
+ * {@code Export-Package} header are visible in the capability and all
+ * directives specified on the {@code Import-Package} and
+ * {@code DynamicImport-Package} headers are visible in the requirement.
+ * 
+ * <ul>
+ * <li>The {@link Namespace#CAPABILITY_EFFECTIVE_DIRECTIVE effective}
+ * {@link Namespace#REQUIREMENT_EFFECTIVE_DIRECTIVE directives} must be ignored.
+ * This namespace is only effective at {@link Namespace#EFFECTIVE_RESOLVE
+ * resolve} time. An {@code effective} directive specified on the
+ * {@code Export-Package}, {@code Import-Package} or
+ * {@code DynamicImport-Package} headers must be ignored. An {@code effective}
+ * directive must not be present in a capability or requirement.</li>
+ * <li>The {@link Namespace#REQUIREMENT_CARDINALITY_DIRECTIVE cardinality}
+ * directive has limited applicability to this namespace. A {@code cardinality}
+ * directive specified on the {@code Import-Package} or
+ * {@code DynamicImport-Package} headers must be ignored. Only requirements with
+ * {@link Namespace#REQUIREMENT_RESOLUTION_DIRECTIVE resolution} set to
+ * {@link #RESOLUTION_DYNAMIC dynamic} and the package name contains a wildcard
+ * must have the {@code cardinality} directive set to
+ * {@link Namespace#CARDINALITY_MULTIPLE multiple}. Otherwise, a
+ * {@code cardinality} directive must not be present in a requirement.</li>
+ * </ul>
+ * 
+ * @Immutable
+ * @version $Id: 5adc45bd1ae26120cbff3562c7c8cefc01e38bd3 $
+ */
+public final class PackageNamespace extends AbstractWiringNamespace {
+
+	/**
+	 * Namespace name for package capabilities and requirements.
+	 * 
+	 * <p>
+	 * Also, the capability attribute used to specify the name of the package.
+	 */
+	public static final String	PACKAGE_NAMESPACE							= "osgi.wiring.package";
+
+	/**
+	 * The capability directive used to specify the comma separated list of
+	 * classes which must be allowed to be exported.
+	 */
+	public final static String	CAPABILITY_INCLUDE_DIRECTIVE				= "include";
+
+	/**
+	 * The capability directive used to specify the comma separated list of
+	 * classes which must not be allowed to be exported.
+	 */
+	public final static String	CAPABILITY_EXCLUDE_DIRECTIVE				= "exclude";
+
+	/**
+	 * The capability attribute contains the {@code Version} of the package if
+	 * one is specified or {@code 0.0.0} if not specified. The value of this
+	 * attribute must be of type {@code Version}.
+	 */
+	public final static String	CAPABILITY_VERSION_ATTRIBUTE				= "version";
+
+	/**
+	 * The capability attribute contains the symbolic name of the resource
+	 * providing the package.
+	 */
+	public final static String	CAPABILITY_BUNDLE_SYMBOLICNAME_ATTRIBUTE	= "bundle-symbolic-name";
+
+	/**
+	 * The directive value identifying a dynamic requirement resolution type. A
+	 * dynamic resolution type indicates that the requirement is resolved
+	 * dynamically at runtime (such as a dynamically imported package) and the
+	 * resource will be resolved without the requirement being resolved.
+	 * 
+	 * @see Namespace#REQUIREMENT_RESOLUTION_DIRECTIVE
+	 */
+	public final static String	RESOLUTION_DYNAMIC							= "dynamic";
+
+	private PackageNamespace() {
+		// empty
+	}
+}
diff --git a/src/main/java/org/osgi/framework/startlevel/BundleStartLevel.java b/src/main/java/org/osgi/framework/startlevel/BundleStartLevel.java
new file mode 100644
index 0000000..d22d3ef
--- /dev/null
+++ b/src/main/java/org/osgi/framework/startlevel/BundleStartLevel.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) OSGi Alliance (2010). All Rights Reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.framework.startlevel;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleReference;
+
+/**
+ * Query and modify the start level information for a bundle. The start level
+ * object for a bundle can be obtained by calling {@link Bundle#adapt(Class)
+ * bundle.adapt(BundleStartLevel.class)} on the bundle.
+ * 
+ * <p>
+ * The bundle associated with this BundleStartLevel object can be obtained by
+ * calling {@link BundleReference#getBundle()}.
+ * 
+ * @ThreadSafe
+ * @noimplement
+ * @version $Id: 9a000be191fe3cb4ae82535a30940db0340d5356 $
+ */
+public interface BundleStartLevel extends BundleReference {
+	/**
+	 * Return the assigned start level value for the bundle.
+	 * 
+	 * @return The start level value of the bundle.
+	 * @see #setStartLevel(int)
+	 * @throws IllegalStateException If the bundle has been uninstalled.
+	 */
+	int getStartLevel();
+
+	/**
+	 * Assign a start level value to the bundle.
+	 * 
+	 * <p>
+	 * The bundle will be assigned the specified start level. The start level
+	 * value assigned to the bundle will be persistently recorded by the
+	 * Framework.
+	 * <p>
+	 * If the new start level for the bundle is lower than or equal to the
+	 * active start level of the Framework and the bundle's autostart setting
+	 * indicates this bundle must be started, the Framework will start the
+	 * bundle as described in the {@link Bundle#start(int)} method using the
+	 * {@link Bundle#START_TRANSIENT} option. The
+	 * {@link Bundle#START_ACTIVATION_POLICY} option must also be used if
+	 * {@link #isActivationPolicyUsed()} returns {@code true}. The actual
+	 * starting of the bundle must occur asynchronously.
+	 * <p>
+	 * If the new start level for the bundle is higher than the active start
+	 * level of the Framework, the Framework will stop the bundle as described
+	 * in the {@link Bundle#stop(int)} method using the
+	 * {@link Bundle#STOP_TRANSIENT} option. The actual stopping of the bundle
+	 * must occur asynchronously.
+	 * 
+	 * @param startlevel The new start level for the bundle.
+	 * @throws IllegalArgumentException If the specified start level is less
+	 *         than or equal to zero, or if the bundle is the system bundle.
+	 * @throws IllegalStateException If the bundle has been uninstalled.
+	 * @throws SecurityException If the caller does not have
+	 *         {@code AdminPermission[bundle,EXECUTE]} and the Java runtime
+	 *         environment supports permissions.
+	 */
+	void setStartLevel(int startlevel);
+
+	/**
+	 * Returns whether the bundle's autostart setting indicates it must be
+	 * started.
+	 * <p>
+	 * The autostart setting of a bundle indicates whether the bundle is to be
+	 * started when its start level is reached.
+	 * 
+	 * @return {@code true} if the autostart setting of the bundle indicates it
+	 *         is to be started. {@code false} otherwise.
+	 * @throws IllegalStateException If this bundle has been uninstalled.
+	 * @see Bundle#START_TRANSIENT
+	 */
+	boolean isPersistentlyStarted();
+
+	/**
+	 * Returns whether the bundle's autostart setting indicates that the
+	 * activation policy declared in the bundle manifest must be used.
+	 * <p>
+	 * The autostart setting of a bundle indicates whether the bundle's declared
+	 * activation policy is to be used when the bundle is started.
+	 * 
+	 * @return {@code true} if the bundle's autostart setting indicates the
+	 *         activation policy declared in the manifest must be used.
+	 *         {@code false} if the bundle must be eagerly activated.
+	 * @throws IllegalStateException If the bundle has been uninstalled.
+	 * @see Bundle#START_ACTIVATION_POLICY
+	 */
+	boolean isActivationPolicyUsed();
+}
diff --git a/src/main/java/org/osgi/framework/startlevel/FrameworkStartLevel.java b/src/main/java/org/osgi/framework/startlevel/FrameworkStartLevel.java
new file mode 100644
index 0000000..11a8049
--- /dev/null
+++ b/src/main/java/org/osgi/framework/startlevel/FrameworkStartLevel.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) OSGi Alliance (2002, 2011). All Rights Reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.framework.startlevel;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleReference;
+import org.osgi.framework.FrameworkListener;
+
+/**
+ * Query and modify the start level information for the framework. The start
+ * level object for the framework can be obtained by calling
+ * {@link Bundle#adapt(Class) bundle.adapt(FrameworkStartLevel.class)} on the
+ * system bundle. Only the system bundle can be adapted to a FrameworkStartLevel
+ * object.
+ * 
+ * <p>
+ * The system bundle associated with this FrameworkStartLevel object can be
+ * obtained by calling {@link BundleReference#getBundle()}.
+ * 
+ * @ThreadSafe
+ * @noimplement
+ * @version $Id: 12c6f60842df994c7de2cc3cfd02f791b95fc35b $
+ */
+public interface FrameworkStartLevel extends BundleReference {
+	/**
+	 * 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.
+	 */
+	int getStartLevel();
+
+	/**
+	 * Modify the active start level of the Framework and notify when complete.
+	 * 
+	 * <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. The specified {@code FrameworkListener}
+	 * s are notified, in the order specified, when the start level change is
+	 * complete. When the start level change completes normally, each specified
+	 * {@code FrameworkListener} will be called with a Framework event of type
+	 * {@code FrameworkEvent.STARTLEVEL_CHANGED}. If the start level change does
+	 * not complete normally, each specified {@code FrameworkListener} will be
+	 * called with a Framework event of type {@code FrameworkEvent.ERROR}.
+	 * 
+	 * <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.
+	 * 
+	 * 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 whose autostart setting
+	 * indicate they must be started. They are started as described in the
+	 * {@link Bundle#start(int)} method using the {@link Bundle#START_TRANSIENT}
+	 * option. The {@link Bundle#START_ACTIVATION_POLICY} option must also be
+	 * used if {@link BundleStartLevel#isActivationPolicyUsed()} returns
+	 * {@code true} for the bundle.
+	 * </ol>
+	 * When this process completes after the specified start level is reached,
+	 * the Framework will fire a Framework event of type
+	 * {@code FrameworkEvent.STARTLEVEL_CHANGED} 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.
+	 * 
+	 * 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 as described in the
+	 * {@link Bundle#stop(int)} method using the {@link Bundle#STOP_TRANSIENT}
+	 * option.
+	 * <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 fire a Framework event of type
+	 * {@code FrameworkEvent.STARTLEVEL_CHANGED} 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 fire a
+	 * Framework event of type {@code FrameworkEvent.STARTLEVEL_CHANGED} to
+	 * announce it has finished moving to the specified start level. This event
+	 * may arrive before this method returns.
+	 * 
+	 * @param startlevel The requested start level for the Framework.
+	 * @param listeners Zero or more listeners to be notified when the start
+	 *        level change has been completed. The specified listeners do not
+	 *        need to be otherwise registered with the framework. If a specified
+	 *        listener is already registered with the framework, it will be
+	 *        notified twice.
+	 * @throws IllegalArgumentException If the specified start level is less
+	 *         than or equal to zero.
+	 * @throws SecurityException If the caller does not have
+	 *         {@code AdminPermission[System Bundle,STARTLEVEL]} and the Java
+	 *         runtime environment supports permissions.
+	 */
+	void setStartLevel(int startlevel, FrameworkListener... listeners);
+
+	/**
+	 * 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(int)
+	 */
+	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}, 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>
+	 * This 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
+	 *         {@code AdminPermission[System Bundle,STARTLEVEL]} and the Java
+	 *         runtime environment supports permissions.
+	 */
+	void setInitialBundleStartLevel(int startlevel);
+}
diff --git a/src/main/java/org/osgi/framework/wiring/BundleCapability.java b/src/main/java/org/osgi/framework/wiring/BundleCapability.java
new file mode 100644
index 0000000..19d7a67
--- /dev/null
+++ b/src/main/java/org/osgi/framework/wiring/BundleCapability.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) OSGi Alliance (2010, 2012). All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.framework.wiring;
+
+import java.util.Map;
+import org.osgi.framework.namespace.AbstractWiringNamespace;
+import org.osgi.resource.Capability;
+
+/**
+ * A capability that has been declared from a {@link BundleRevision bundle
+ * revision}.
+ * 
+ * @ThreadSafe
+ * @noimplement
+ * @version $Id: 39086f7e6086c2b3d83fbcb976a011cf69483ad8 $
+ */
+public interface BundleCapability extends Capability {
+
+	/**
+	 * Returns the bundle revision declaring this capability.
+	 * 
+	 * @return The bundle revision declaring this capability.
+	 */
+	BundleRevision getRevision();
+
+	/**
+	 * Returns the namespace of this capability.
+	 * 
+	 * @return The namespace of this capability.
+	 */
+	String getNamespace();
+
+	/**
+	 * Returns the directives of this capability.
+	 * 
+	 * <p>
+	 * All capability directives not specified by the
+	 * {@link AbstractWiringNamespace wiring namespaces} have no specified
+	 * semantics and are considered extra user defined information.
+	 * 
+	 * @return An unmodifiable map of directive names to directive values for
+	 *         this capability, or an empty map if this capability has no
+	 *         directives.
+	 */
+	Map<String, String> getDirectives();
+
+	/**
+	 * Returns the attributes of this capability.
+	 * 
+	 * @return An unmodifiable map of attribute names to attribute values for
+	 *         this capability, or an empty map if this capability has no
+	 *         attributes.
+	 */
+	Map<String, Object> getAttributes();
+
+	/**
+	 * Returns the resource declaring this capability.
+	 * 
+	 * <p>
+	 * This method returns the same value as {@link #getRevision()}.
+	 * 
+	 * @return The resource declaring this capability.
+	 * @since 1.1
+	 */
+	BundleRevision getResource();
+}
diff --git a/src/main/java/org/osgi/framework/wiring/BundleRequirement.java b/src/main/java/org/osgi/framework/wiring/BundleRequirement.java
new file mode 100644
index 0000000..bb26c5d
--- /dev/null
+++ b/src/main/java/org/osgi/framework/wiring/BundleRequirement.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) OSGi Alliance (2010, 2012). All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.framework.wiring;
+
+import java.util.Map;
+import org.osgi.framework.namespace.AbstractWiringNamespace;
+import org.osgi.resource.Requirement;
+
+/**
+ * A requirement that has been declared from a {@link BundleRevision bundle
+ * revision}.
+ * 
+ * @ThreadSafe
+ * @noimplement
+ * @version $Id: 212ffb64f724d982db86ff7e49ed64ea530e670a $
+ */
+public interface BundleRequirement extends Requirement {
+	/**
+	 * Returns the bundle revision declaring this requirement.
+	 * 
+	 * @return The bundle revision declaring this requirement.
+	 */
+	BundleRevision getRevision();
+
+	/**
+	 * Returns whether the specified capability matches this requirement.
+	 * 
+	 * @param capability The capability to match to this requirement.
+	 * @return {@code true} if the specified capability has the same
+	 *         {@link #getNamespace() namespace} as this requirement and the
+	 *         filter for this requirement matches the
+	 *         {@link BundleCapability#getAttributes() attributes of the
+	 *         specified capability}; {@code false} otherwise.
+	 */
+	boolean matches(BundleCapability capability);
+
+	/**
+	 * Returns the namespace of this requirement.
+	 * 
+	 * @return The namespace of this requirement.
+	 */
+	String getNamespace();
+
+	/**
+	 * Returns the directives of this requirement.
+	 * 
+	 * <p>
+	 * All requirement directives not specified by the
+	 * {@link AbstractWiringNamespace wiring namespaces} have no specified
+	 * semantics and are considered extra user defined information.
+	 * 
+	 * @return An unmodifiable map of directive names to directive values for
+	 *         this requirement, or an empty map if this requirement has no
+	 *         directives.
+	 */
+	Map<String, String> getDirectives();
+
+	/**
+	 * Returns the attributes of this requirement.
+	 * 
+	 * <p>
+	 * Requirement attributes have no specified semantics and are considered
+	 * extra user defined information.
+	 * 
+	 * @return An unmodifiable map of attribute names to attribute values for
+	 *         this requirement, or an empty map if this requirement has no
+	 *         attributes.
+	 */
+	Map<String, Object> getAttributes();
+
+	/**
+	 * Returns the resource declaring this requirement.
+	 * 
+	 * <p>
+	 * This method returns the same value as {@link #getRevision()}.
+	 * 
+	 * @return The resource declaring this requirement. This can be {@code null}
+	 *         if this requirement is synthesized.
+	 * @since 1.1
+	 */
+	BundleRevision getResource();
+}
diff --git a/src/main/java/org/osgi/framework/wiring/BundleRevision.java b/src/main/java/org/osgi/framework/wiring/BundleRevision.java
new file mode 100644
index 0000000..1ba9365
--- /dev/null
+++ b/src/main/java/org/osgi/framework/wiring/BundleRevision.java
@@ -0,0 +1,304 @@
+/*
+ * Copyright (c) OSGi Alliance (2010, 2012). All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.framework.wiring;
+
+import java.util.List;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleReference;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Version;
+import org.osgi.framework.namespace.BundleNamespace;
+import org.osgi.framework.namespace.HostNamespace;
+import org.osgi.framework.namespace.PackageNamespace;
+import org.osgi.resource.Capability;
+import org.osgi.resource.Requirement;
+import org.osgi.resource.Resource;
+
+/**
+ * Bundle Revision. When a bundle is installed and each time a bundle is
+ * updated, a new bundle revision of the bundle is created. Since a bundle
+ * update can change the entries in a bundle, different bundle wirings for the
+ * same bundle can be associated with different bundle revisions.
+ * 
+ * <p>
+ * For a bundle that has not been uninstalled, the most recent bundle revision
+ * is defined to be the current bundle revision. A bundle in the UNINSTALLED
+ * state does not have a current revision. The current bundle revision for a
+ * bundle can be obtained by calling {@link Bundle#adapt(Class) bundle.adapt}
+ * (BundleRevision.class). Since a bundle in the UNINSTALLED state does not have
+ * a current revision, adapting such a bundle returns {@code null}.
+ * 
+ * <p>
+ * The framework defines namespaces for {@link PackageNamespace package},
+ * {@link BundleNamespace bundle} and {@link HostNamespace host} capabilities
+ * and requirements. These namespaces are defined only to express wiring
+ * information by the framework. They must not be used in
+ * {@link Constants#PROVIDE_CAPABILITY Provide-Capability} and
+ * {@link Constants#REQUIRE_CAPABILITY Require-Capability} manifest headers.
+ * 
+ * @ThreadSafe
+ * @noimplement
+ * @version $Id: e68e01a670f0ae9d6eb736414f875c8b216ed1bc $
+ */
+public interface BundleRevision extends BundleReference, Resource {
+	/**
+	 * Returns the symbolic name for this bundle revision.
+	 * 
+	 * @return The symbolic name for this bundle revision.
+	 * @see Bundle#getSymbolicName()
+	 */
+	String getSymbolicName();
+
+	/**
+	 * Returns the version for this bundle revision.
+	 * 
+	 * @return The version for this bundle revision, or
+	 *         {@link Version#emptyVersion} if this bundle revision has no
+	 *         version information.
+	 * @see Bundle#getVersion()
+	 */
+	Version getVersion();
+
+	/**
+	 * Returns the capabilities declared by this bundle revision.
+	 * 
+	 * @param namespace The namespace of the declared capabilities to return or
+	 *        {@code null} to return the declared capabilities from all
+	 *        namespaces.
+	 * @return An unmodifiable list containing the declared
+	 *         {@link BundleCapability}s from the specified namespace. The
+	 *         returned list will be empty if this bundle revision declares no
+	 *         capabilities in the specified namespace. The list contains the
+	 *         declared capabilities in the order they are specified in the
+	 *         manifest.
+	 */
+	List<BundleCapability> getDeclaredCapabilities(String namespace);
+
+	/**
+	 * Returns the requirements declared by this bundle revision.
+	 * 
+	 * @param namespace The namespace of the declared requirements to return or
+	 *        {@code null} to return the declared requirements from all
+	 *        namespaces.
+	 * @return An unmodifiable list containing the declared
+	 *         {@link BundleRequirement}s from the specified namespace. The
+	 *         returned list will be empty if this bundle revision declares no
+	 *         requirements in the specified namespace. The list contains the
+	 *         declared requirements in the order they are specified in the
+	 *         manifest.
+	 */
+	List<BundleRequirement> getDeclaredRequirements(String namespace);
+
+	/**
+	 * Namespace for package capabilities and requirements.
+	 * 
+	 * <p>
+	 * The name of the package is stored in the capability attribute of the same
+	 * name as this namespace (osgi.wiring.package). The other directives and
+	 * attributes of the package, from the {@link Constants#EXPORT_PACKAGE
+	 * Export-Package} manifest header, can be found in the cabability's
+	 * {@link BundleCapability#getDirectives() directives} and
+	 * {@link BundleCapability#getAttributes() attributes}. The
+	 * {@link Constants#VERSION_ATTRIBUTE version} capability attribute must
+	 * contain the {@link Version} of the package if one is specified or
+	 * {@link Version#emptyVersion} if not specified. The
+	 * {@link Constants#BUNDLE_SYMBOLICNAME_ATTRIBUTE bundle-symbolic-name}
+	 * capability attribute must contain the
+	 * {@link BundleRevision#getSymbolicName() symbolic name} of the provider if
+	 * one is specified. The {@link Constants#BUNDLE_VERSION_ATTRIBUTE
+	 * bundle-version} capability attribute must contain the
+	 * {@link BundleRevision#getVersion() version} of the provider if one is
+	 * specified or {@link Version#emptyVersion} if not specified.
+	 * 
+	 * <p>
+	 * The package capabilities provided by the system bundle, that is the
+	 * bundle with id zero, must include the package specified by the
+	 * {@link Constants#FRAMEWORK_SYSTEMPACKAGES} and
+	 * {@link Constants#FRAMEWORK_SYSTEMPACKAGES_EXTRA} framework properties as
+	 * well as any other package exported by the framework implementation.
+	 * 
+	 * <p>
+	 * A bundle revision {@link BundleRevision#getDeclaredCapabilities(String)
+	 * declares} zero or more package capabilities (this is, exported packages)
+	 * and {@link BundleRevision#getDeclaredRequirements(String) declares} zero
+	 * or more package requirements.
+	 * <p>
+	 * A bundle wiring {@link BundleWiring#getCapabilities(String) provides}
+	 * zero or more resolved package capabilities (that is, exported packages)
+	 * and {@link BundleWiring#getRequiredWires(String) requires} zero or more
+	 * resolved package requirements (that is, imported packages). The number of
+	 * package wires required by a bundle wiring may change as the bundle wiring
+	 * may dynamically import additional packages.
+	 * 
+	 * @see PackageNamespace
+	 */
+	String	PACKAGE_NAMESPACE	= PackageNamespace.PACKAGE_NAMESPACE;
+
+	/**
+	 * Namespace for bundle capabilities and requirements.
+	 * 
+	 * <p>
+	 * The bundle symbolic name of the bundle is stored in the capability
+	 * attribute of the same name as this namespace (osgi.wiring.bundle). The
+	 * other directives and attributes of the bundle, from the
+	 * {@link Constants#BUNDLE_SYMBOLICNAME Bundle-SymbolicName} manifest
+	 * header, can be found in the cabability's
+	 * {@link BundleCapability#getDirectives() directives} and
+	 * {@link BundleCapability#getAttributes() attributes}. The
+	 * {@link Constants#BUNDLE_VERSION_ATTRIBUTE bundle-version} capability
+	 * attribute must contain the {@link Version} of the bundle from the
+	 * {@link Constants#BUNDLE_VERSION Bundle-Version} manifest header if one is
+	 * specified or {@link Version#emptyVersion} if not specified.
+	 * 
+	 * <p>
+	 * A non-fragment revision
+	 * {@link BundleRevision#getDeclaredCapabilities(String) declares} exactly
+	 * one<sup>†</sup> bundle capability (that is, the bundle can be
+	 * required by another bundle). A fragment revision must not declare a
+	 * bundle capability.
+	 * 
+	 * <p>
+	 * A bundle wiring for a non-fragment revision
+	 * {@link BundleWiring#getCapabilities(String) provides} exactly
+	 * one<sup>†</sup> bundle capability (that is, the bundle can be
+	 * required by another bundle) and
+	 * {@link BundleWiring#getRequiredWires(String) requires} zero or more
+	 * bundle capabilities (that is, requires other bundles).
+	 * 
+	 * <p>
+	 * † A bundle with no bundle symbolic name (that is, a bundle with
+	 * {@link Constants#BUNDLE_MANIFESTVERSION Bundle-ManifestVersion}
+	 * {@literal <} 2) must not provide a bundle capability.
+	 * 
+	 * @see BundleNamespace
+	 */
+	String	BUNDLE_NAMESPACE	= BundleNamespace.BUNDLE_NAMESPACE;
+
+	/**
+	 * Namespace for host capabilities and requirements.
+	 * 
+	 * <p>
+	 * The bundle symbolic name of the bundle is stored in the capability
+	 * attribute of the same name as this namespace (osgi.wiring.host). The
+	 * other directives and attributes of the bundle, from the
+	 * {@link Constants#BUNDLE_SYMBOLICNAME Bundle-SymbolicName} manifest
+	 * header, can be found in the cabability's
+	 * {@link BundleCapability#getDirectives() directives} and
+	 * {@link BundleCapability#getAttributes() attributes}. The
+	 * {@link Constants#BUNDLE_VERSION_ATTRIBUTE bundle-version} capability
+	 * attribute must contain the {@link Version} of the bundle from the
+	 * {@link Constants#BUNDLE_VERSION Bundle-Version} manifest header if one is
+	 * specified or {@link Version#emptyVersion} if not specified.
+	 * 
+	 * <p>
+	 * A non-fragment revision
+	 * {@link BundleRevision#getDeclaredCapabilities(String) declares} zero or
+	 * one<sup>†</sup> host capability if the bundle
+	 * {@link Constants#FRAGMENT_ATTACHMENT_DIRECTIVE allows fragments to be
+	 * attached}. A fragment revision must
+	 * {@link BundleRevision#getDeclaredRequirements(String) declare} exactly
+	 * one host requirement.
+	 * 
+	 * <p>
+	 * A bundle wiring for a non-fragment revision
+	 * {@link BundleWiring#getCapabilities(String) provides} zero or
+	 * one<sup>†</sup> host capability if the bundle
+	 * {@link Constants#FRAGMENT_ATTACHMENT_DIRECTIVE allows fragments to be
+	 * attached}. A bundle wiring for a fragment revision
+	 * {@link BundleWiring#getRequiredWires(String) requires} a host capability
+	 * for each host to which it is attached.
+	 * 
+	 * <p>
+	 * † A bundle with no bundle symbolic name (that is, a bundle with
+	 * {@link Constants#BUNDLE_MANIFESTVERSION Bundle-ManifestVersion}
+	 * {@literal <} 2) must not provide a host capability.
+	 * 
+	 * @see HostNamespace
+	 */
+	String	HOST_NAMESPACE		= HostNamespace.HOST_NAMESPACE;
+
+	/**
+	 * Returns the special types of this bundle revision. The bundle revision
+	 * type values are:
+	 * <ul>
+	 * <li>{@link #TYPE_FRAGMENT}
+	 * </ul>
+	 * 
+	 * A bundle revision may be more than one type at a time. A type code is
+	 * used to identify the bundle revision type for future extendability.
+	 * 
+	 * <p>
+	 * If this bundle revision is not one or more of the defined types then 0 is
+	 * returned.
+	 * 
+	 * @return The special types of this bundle revision. The type values are
+	 *         ORed together.
+	 */
+	int getTypes();
+
+	/**
+	 * Bundle revision type indicating the bundle revision is a fragment.
+	 * 
+	 * @see #getTypes()
+	 */
+	int	TYPE_FRAGMENT	= 0x00000001;
+
+	/**
+	 * Returns the bundle wiring which is using this bundle revision.
+	 * 
+	 * @return The bundle wiring which is using this bundle revision or
+	 *         {@code null} if no bundle wiring is using this bundle revision.
+	 * @see BundleWiring#getRevision()
+	 */
+	BundleWiring getWiring();
+
+	/**
+	 * Returns the capabilities declared by this resource.
+	 * 
+	 * <p>
+	 * This method returns the same value as
+	 * {@link #getDeclaredCapabilities(String)}.
+	 * 
+	 * @param namespace The namespace of the declared capabilities to return or
+	 *        {@code null} to return the declared capabilities from all
+	 *        namespaces.
+	 * @return An unmodifiable list containing the declared {@link Capability}s
+	 *         from the specified namespace. The returned list will be empty if
+	 *         this resource declares no capabilities in the specified
+	 *         namespace.
+	 * @since 1.1
+	 */
+	List<Capability> getCapabilities(String namespace);
+
+	/**
+	 * Returns the requirements declared by this bundle resource.
+	 * 
+	 * <p>
+	 * This method returns the same value as
+	 * {@link #getDeclaredRequirements(String)}.
+	 * 
+	 * @param namespace The namespace of the declared requirements to return or
+	 *        {@code null} to return the declared requirements from all
+	 *        namespaces.
+	 * @return An unmodifiable list containing the declared {@link Requirement}
+	 *         s from the specified namespace. The returned list will be empty
+	 *         if this resource declares no requirements in the specified
+	 *         namespace.
+	 * @since 1.1
+	 */
+	List<Requirement> getRequirements(String namespace);
+}
diff --git a/src/main/java/org/osgi/framework/wiring/BundleRevisions.java b/src/main/java/org/osgi/framework/wiring/BundleRevisions.java
new file mode 100644
index 0000000..f0d03ff
--- /dev/null
+++ b/src/main/java/org/osgi/framework/wiring/BundleRevisions.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) OSGi Alliance (2011, 2012). All Rights Reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.framework.wiring;
+
+import java.util.List;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleReference;
+
+/**
+ * The {@link BundleRevision bundle revisions} of a bundle. When a bundle is
+ * installed and each time a bundle is updated, a new bundle revision of the
+ * bundle is created. For a bundle that has not been uninstalled, the most
+ * recent bundle revision is defined to be the current bundle revision. A bundle
+ * in the UNINSTALLED state does not have a current revision. An in use bundle
+ * revision is associated with an {@link BundleWiring#isInUse() in use}
+ * {@link BundleWiring}. The current bundle revision, if there is one, and all
+ * in use bundle revisions are returned.
+ * 
+ * <p>
+ * The bundle revisions for a bundle can be obtained by calling
+ * {@link Bundle#adapt(Class) bundle.adapt}({@link BundleRevisions}.class).
+ * {@link #getRevisions()} on the bundle.
+ * 
+ * @ThreadSafe
+ * @noimplement
+ * @version $Id: 8423242078417873faf0f8979e153e3c1f3a0e4b $
+ */
+public interface BundleRevisions extends BundleReference {
+	/**
+	 * Return the bundle revisions for the {@link BundleReference#getBundle()
+	 * referenced} bundle.
+	 * 
+	 * <p>
+	 * The result is a list containing the current bundle revision, if there is
+	 * one, and all in use bundle revisions. The list may also contain
+	 * intermediate bundle revisions which are not in use.
+	 * 
+	 * <p>
+	 * The list is ordered in reverse chronological order such that the first
+	 * item is the most recent bundle revision and last item is the oldest
+	 * bundle revision.
+	 * 
+	 * <p>
+	 * Generally the list will have at least one bundle revision for the bundle:
+	 * the current bundle revision. However, for an uninstalled bundle with no
+	 * in use bundle revisions, the list may be empty.
+	 * 
+	 * @return A list containing a snapshot of the {@link BundleRevision}s for
+	 *         the referenced bundle.
+	 */
+	List<BundleRevision> getRevisions();
+}
diff --git a/src/main/java/org/osgi/framework/wiring/BundleWire.java b/src/main/java/org/osgi/framework/wiring/BundleWire.java
new file mode 100644
index 0000000..d14829a
--- /dev/null
+++ b/src/main/java/org/osgi/framework/wiring/BundleWire.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) OSGi Alliance (2011, 2012). All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.framework.wiring;
+
+import org.osgi.resource.Wire;
+
+/**
+ * A wire connecting a {@link BundleCapability} to a {@link BundleRequirement}.
+ * 
+ * @ThreadSafe
+ * @noimplement
+ * @version $Id: 02e7cd6ec0fa9fdb73f782a6890984d5d4e7ca21 $
+ */
+public interface BundleWire extends Wire {
+	/**
+	 * Returns the {@link BundleCapability} for this wire.
+	 * 
+	 * @return The {@link BundleCapability} for this wire.
+	 */
+	BundleCapability getCapability();
+
+	/**
+	 * Return the {@link BundleRequirement} for this wire.
+	 * 
+	 * @return The {@link BundleRequirement} for this wire.
+	 */
+	BundleRequirement getRequirement();
+
+	/**
+	 * Returns the bundle wiring {@link BundleWiring#getProvidedWires(String)
+	 * providing} the {@link #getCapability() capability}.
+	 * 
+	 * <p>
+	 * The bundle revision referenced by the returned bundle wiring may differ
+	 * from the bundle revision referenced by the {@link #getCapability()
+	 * capability}.
+	 * 
+	 * @return The bundle wiring providing the capability. If the bundle wiring
+	 *         providing the capability is not {@link BundleWiring#isInUse() in
+	 *         use}, {@code null} will be returned.
+	 */
+	BundleWiring getProviderWiring();
+
+	/**
+	 * Returns the bundle wiring who
+	 * {@link BundleWiring#getRequiredWires(String) requires} the
+	 * {@link #getCapability() capability}.
+	 * 
+	 * <p>
+	 * The bundle revision referenced by the returned bundle wiring may differ
+	 * from the bundle revision referenced by the {@link #getRequirement()
+	 * requirement}.
+	 * 
+	 * @return The bundle wiring whose requirement is wired to the capability.
+	 *         If the bundle wiring requiring the capability is not
+	 *         {@link BundleWiring#isInUse() in use}, {@code null} will be
+	 *         returned.
+	 */
+	BundleWiring getRequirerWiring();
+
+	/**
+	 * Returns the resource providing the {@link #getCapability() capability}.
+	 * 
+	 * <p>
+	 * The returned resource may differ from the resource referenced by the
+	 * {@link #getCapability() capability}.
+	 * 
+	 * <p>
+	 * This method returns the same value as {@link #getProviderWiring()}.
+	 * {@link BundleWiring#getRevision() getRevision()}.
+	 * 
+	 * @return The resource providing the capability.
+	 * @since 1.1
+	 */
+	BundleRevision getProvider();
+
+	/**
+	 * Returns the resource who {@link #getRequirement() requires} the
+	 * {@link #getCapability() capability}.
+	 * 
+	 * <p>
+	 * The returned resource may differ from the resource referenced by the
+	 * {@link #getRequirement() requirement}.
+	 * 
+	 * <p>
+	 * This method returns the same value as {@link #getRequirerWiring()}.
+	 * {@link BundleWiring#getRevision() getRevision()}.
+	 * 
+	 * @return The resource who requires the capability.
+	 * @since 1.1
+	 */
+	BundleRevision getRequirer();
+
+}
diff --git a/src/main/java/org/osgi/framework/wiring/BundleWiring.java b/src/main/java/org/osgi/framework/wiring/BundleWiring.java
new file mode 100644
index 0000000..b234c38
--- /dev/null
+++ b/src/main/java/org/osgi/framework/wiring/BundleWiring.java
@@ -0,0 +1,502 @@
+/*
+ * Copyright (c) OSGi Alliance (2010, 2012). All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.framework.wiring;
+
+import java.net.URL;
+import java.util.Collection;
+import java.util.List;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleReference;
+import org.osgi.framework.namespace.IdentityNamespace;
+import org.osgi.framework.namespace.PackageNamespace;
+import org.osgi.resource.Capability;
+import org.osgi.resource.Namespace;
+import org.osgi.resource.Requirement;
+import org.osgi.resource.Wire;
+import org.osgi.resource.Wiring;
+
+/**
+ * A wiring for a bundle. Each time a bundle is resolved, a new bundle wiring
+ * for the bundle is created. A bundle wiring is associated with a bundle
+ * revision and represents the dependencies with other bundle wirings.
+ * 
+ * <p>
+ * The bundle wiring for a bundle is the {@link #isCurrent() current} bundle
+ * wiring if it is the most recent bundle wiring for the current bundle
+ * revision. A bundle wiring is {@link #isInUse() in use} if it is the current
+ * bundle wiring or if some other in use bundle wiring is dependent upon it. For
+ * example, another bundle wiring is wired to a capability provided by the
+ * bundle wiring. An in use bundle wiring for a non-fragment bundle has a class
+ * loader. All bundles with non-current, in use bundle wirings are considered
+ * removal pending. Once a bundle wiring is no longer in use, it is considered
+ * stale and is discarded by the framework.
+ * 
+ * <p>
+ * The current bundle wiring for a bundle can be obtained by calling
+ * {@link Bundle#adapt(Class) bundle.adapt}(BundleWiring.class). A bundle in the
+ * INSTALLED or UNINSTALLED state does not have a current wiring, adapting such
+ * a bundle returns {@code null}.
+ * 
+ * @ThreadSafe
+ * @noimplement
+ * @version $Id: a3b3fd7ad7d289a5bfc6e4e02c875bc42a34df89 $
+ */
+public interface BundleWiring extends BundleReference, Wiring {
+	/**
+	 * Returns {@code true} if this bundle wiring is the current bundle wiring.
+	 * The bundle wiring for a bundle is the current bundle wiring if it is the
+	 * most recent bundle wiring for the current bundle revision. All bundles
+	 * with non-current, in use bundle wirings are considered
+	 * {@link FrameworkWiring#getRemovalPendingBundles() removal pending}.
+	 * 
+	 * @return {@code true} if this bundle wiring is the current bundle wiring;
+	 *         {@code false} otherwise.
+	 */
+	boolean isCurrent();
+
+	/**
+	 * Returns {@code true} if this bundle wiring is in use. A bundle wiring is
+	 * in use if it is the {@link #isCurrent() current} wiring or if some other
+	 * in use bundle wiring is dependent upon it. Once a bundle wiring is no
+	 * longer in use, it is considered stale and is discarded by the framework.
+	 * 
+	 * @return {@code true} if this bundle wiring is in use; {@code false}
+	 *         otherwise.
+	 */
+	boolean isInUse();
+
+	/**
+	 * Returns the capabilities provided by this bundle wiring.
+	 * 
+	 * <p>
+	 * Only capabilities considered by the resolver are returned. For example,
+	 * capabilities with {@link Namespace#CAPABILITY_EFFECTIVE_DIRECTIVE
+	 * effective} directive not equal to {@link Namespace#EFFECTIVE_RESOLVE
+	 * resolve} are not returned.
+	 * 
+	 * <p>
+	 * A capability may not be required by any bundle wiring and thus there may
+	 * be no {@link #getProvidedWires(String) wires} for the capability.
+	 * 
+	 * <p>
+	 * A bundle wiring for a non-fragment revision provides a subset of the
+	 * declared capabilities from the bundle revision and all attached fragment
+	 * revisions<sup>†</sup>. Not all declared capabilities may be
+	 * provided since some may be discarded. For example, if a package is
+	 * declared to be both exported and imported, only one is selected and the
+	 * other is discarded.
+	 * <p>
+	 * A bundle wiring for a fragment revision with a symbolic name must provide
+	 * exactly one {@link IdentityNamespace identity} capability.
+	 * <p>
+	 * † The {@link IdentityNamespace identity} capability provided by
+	 * attached fragment revisions must not be included in the capabilities of
+	 * the host bundle wiring.
+	 * 
+	 * @param namespace The namespace of the capabilities to return or
+	 *        {@code null} to return the capabilities from all namespaces.
+	 * @return A list containing a snapshot of the {@link BundleCapability}s, or
+	 *         an empty list if this bundle wiring provides no capabilities in
+	 *         the specified namespace. If this bundle wiring is not
+	 *         {@link #isInUse() in use}, {@code null} will be returned. For a
+	 *         given namespace, the list contains the wires in the order the
+	 *         capabilities were specified in the manifests of the
+	 *         {@link #getRevision() bundle revision} and the attached
+	 *         fragments<sup>†</sup> of this bundle wiring. There is no
+	 *         ordering defined between capabilities in different namespaces.
+	 */
+	List<BundleCapability> getCapabilities(String namespace);
+
+	/**
+	 * Returns the requirements of this bundle wiring.
+	 * 
+	 * <p>
+	 * Only requirements considered by the resolver are returned. For example,
+	 * requirements with {@link Namespace#REQUIREMENT_EFFECTIVE_DIRECTIVE
+	 * effective} directive not equal to {@link Namespace#EFFECTIVE_RESOLVE
+	 * resolve} are not returned.
+	 * 
+	 * <p>
+	 * A bundle wiring for a non-fragment revision has a subset of the declared
+	 * requirements from the bundle revision and all attached fragment
+	 * revisions. Not all declared requirements may be present since some may be
+	 * discarded. For example, if a package is declared to be optionally
+	 * imported and is not actually imported, the requirement must be discarded.
+	 * 
+	 * @param namespace The namespace of the requirements to return or
+	 *        {@code null} to return the requirements from all namespaces.
+	 * @return A list containing a snapshot of the {@link BundleRequirement}s,
+	 *         or an empty list if this bundle wiring uses no requirements in
+	 *         the specified namespace. If this bundle wiring is not
+	 *         {@link #isInUse() in use}, {@code null} will be returned. For a
+	 *         given namespace, the list contains the wires in the order the
+	 *         requirements were specified in the manifests of the
+	 *         {@link #getRevision() bundle revision} and the attached fragments
+	 *         of this bundle wiring. There is no ordering defined between
+	 *         requirements in different namespaces.
+	 */
+	List<BundleRequirement> getRequirements(String namespace);
+
+	/**
+	 * Returns the {@link BundleWire}s to the provided {@link BundleCapability
+	 * capabilities} of this bundle wiring.
+	 * 
+	 * @param namespace The namespace of the capabilities for which to return
+	 *        wires or {@code null} to return the wires for the capabilities in
+	 *        all namespaces.
+	 * @return A list containing a snapshot of the {@link BundleWire}s for the
+	 *         {@link BundleCapability capabilities} of this bundle wiring, or
+	 *         an empty list if this bundle wiring has no capabilities in the
+	 *         specified namespace. If this bundle wiring is not
+	 *         {@link #isInUse() in use}, {@code null} will be returned. For a
+	 *         given namespace, the list contains the wires in the order the
+	 *         capabilities were specified in the manifests of the
+	 *         {@link #getRevision() bundle revision} and the attached fragments
+	 *         of this bundle wiring. There is no ordering defined between
+	 *         capabilities in different namespaces.
+	 */
+	List<BundleWire> getProvidedWires(String namespace);
+
+	/**
+	 * Returns the {@link BundleWire}s to the {@link BundleRequirement
+	 * requirements} in use by this bundle wiring.
+	 * 
+	 * <p>
+	 * This method may return different results if this bundle wiring adds wires
+	 * to more requirements. For example, dynamically importing a package will
+	 * establish a new wire to the dynamically imported package.
+	 * 
+	 * @param namespace The namespace of the requirements for which to return
+	 *        wires or {@code null} to return the wires for the requirements in
+	 *        all namespaces.
+	 * @return A list containing a snapshot of the {@link BundleWire}s for the
+	 *         {@link BundleRequirement requirements} of this bundle wiring, or
+	 *         an empty list if this bundle wiring has no requirements in the
+	 *         specified namespace. If this bundle wiring is not
+	 *         {@link #isInUse() in use}, {@code null} will be returned. For a
+	 *         given namespace, the list contains the wires in the order the
+	 *         requirements were specified in the manifests of the
+	 *         {@link #getRevision() bundle revision} and the attached fragments
+	 *         of this bundle wiring. There is no ordering defined between
+	 *         requirements in different namespaces.
+	 */
+	List<BundleWire> getRequiredWires(String namespace);
+
+	/**
+	 * Returns the bundle revision for the bundle in this bundle wiring. Since a
+	 * bundle update can change the entries in a bundle, different bundle
+	 * wirings for the same bundle can have different bundle revisions.
+	 * 
+	 * <p>
+	 * The bundle object {@link BundleReference#getBundle() referenced} by the
+	 * returned {@code BundleRevision} may return different information than the
+	 * returned {@code BundleRevision} since the returned {@code BundleRevision}
+	 * may refer to an older revision of the bundle.
+	 * 
+	 * @return The bundle revision for this bundle wiring.
+	 * @see BundleRevision#getWiring()
+	 */
+	BundleRevision getRevision();
+
+	/**
+	 * Returns the class loader for this bundle wiring. Since a bundle refresh
+	 * creates a new bundle wiring for a bundle, different bundle wirings for
+	 * the same bundle will have different class loaders.
+	 * 
+	 * @return The class loader for this bundle wiring. If this bundle wiring is
+	 *         not {@link #isInUse() in use} or this bundle wiring is for a
+	 *         fragment revision, {@code null} will be returned.
+	 * @throws SecurityException If the caller does not have the appropriate
+	 *         {@code RuntimePermission("getClassLoader")}, and the Java Runtime
+	 *         Environment supports permissions.
+	 */
+	ClassLoader getClassLoader();
+
+	/**
+	 * Returns entries in this bundle wiring's {@link #getRevision() bundle
+	 * revision} and its attached fragment revisions. This bundle wiring's class
+	 * loader is not used to search for entries. Only the contents of this
+	 * bundle wiring's bundle revision and its attached fragment revisions are
+	 * searched for the specified entries.
+	 * 
+	 * <p>
+	 * This method takes into account that the "contents" of this
+	 * bundle wiring can have attached fragments. This "bundle space"
+	 * is not a namespace with unique members; the same entry name can be
+	 * present multiple times. This method therefore returns a list of URL
+	 * objects. These URLs can come from different JARs but have the same path
+	 * name. This method can either return only entries in the specified path or
+	 * recurse into subdirectories returning entries in the directory tree
+	 * beginning at the specified path.
+	 * 
+	 * <p>
+	 * URLs for directory entries must have their path end with "/".
+	 * <p>
+	 * Note: Jar and zip files are not required to include directory entries.
+	 * URLs to directory entries will not be returned if the bundle contents do
+	 * not contain directory entries.
+	 * 
+	 * @param path The path name in which to look. The path is always relative
+	 *        to the root of this bundle wiring and may begin with
+	 *        "/". A path value of "/" indicates the root of
+	 *        this bundle wiring.
+	 * @param filePattern The file name pattern for selecting entries in the
+	 *        specified path. The pattern is only matched against the last
+	 *        element of the entry path. If the entry is a directory then the
+	 *        trailing "/" is not used for pattern matching. Substring
+	 *        matching is supported, as specified in the Filter specification,
+	 *        using the wildcard character ("*"). If {@code null} is
+	 *        specified, this is equivalent to "*" and matches all
+	 *        files.
+	 * @param options The options for listing resource names. See
+	 *        {@link #FINDENTRIES_RECURSE}. The method must ignore unrecognized
+	 *        options.
+	 * @return An unmodifiable list of URL objects for each matching entry, or
+	 *         an empty list if no matching entry could be found, if this bundle
+	 *         wiring is for a fragment revision or if the caller does not have
+	 *         the appropriate {@code AdminPermission[bundle,RESOURCE]} and the
+	 *         Java Runtime Environment supports permissions. The list is
+	 *         ordered such that entries from the {@link #getRevision() bundle
+	 *         revision} are returned first followed by the entries from
+	 *         attached fragment revisions in attachment order. If this bundle
+	 *         wiring is not {@link #isInUse() in use}, {@code null} must be
+	 *         returned.
+	 * @see Bundle#findEntries(String, String, boolean)
+	 */
+	List<URL> findEntries(String path, String filePattern, int options);
+
+	/**
+	 * The find entries operation must recurse into subdirectories.
+	 * 
+	 * <p>
+	 * This bit may be set when calling
+	 * {@link #findEntries(String, String, int)} to specify the result must
+	 * include the matching entries from the specified path and its
+	 * subdirectories. If this bit is not set, then the result must only include
+	 * matching entries from the specified path.
+	 * 
+	 * @see #findEntries(String, String, int)
+	 */
+	int	FINDENTRIES_RECURSE	= 0x00000001;
+
+	/**
+	 * Returns the names of resources visible to this bundle wiring's
+	 * {@link #getClassLoader() class loader}. The returned names can be used to
+	 * access the resources via this bundle wiring's class loader.
+	 * 
+	 * <ul>
+	 * <li>Only the resource names for resources in bundle wirings will be
+	 * returned. The names of resources visible to a bundle wiring's parent
+	 * class loader, such as the bootstrap class loader, must not be included in
+	 * the result.
+	 * <li>Only established wires will be examined for resources. This method
+	 * must not cause new wires for dynamic imports to be established.
+	 * </ul>
+	 * 
+	 * @param path The path name in which to look. The path is always relative
+	 *        to the root of this bundle wiring's class loader and may begin
+	 *        with "/". A path value of "/" indicates the
+	 *        root of this bundle wiring's class loader.
+	 * @param filePattern The file name pattern for selecting resource names in
+	 *        the specified path. The pattern is only matched against the last
+	 *        element of the resource path. If the resource is a directory then
+	 *        the trailing "/" is not used for pattern matching.
+	 *        Substring matching is supported, as specified in the Filter
+	 *        specification, using the wildcard character ("*"). If
+	 *        {@code null} is specified, this is equivalent to "*" and
+	 *        matches all files.
+	 * @param options The options for listing resource names. See
+	 *        {@link #LISTRESOURCES_LOCAL} and {@link #LISTRESOURCES_RECURSE}.
+	 *        This method must ignore unrecognized options.
+	 * @return An unmodifiable collection of resource names for each matching
+	 *         resource, or an empty collection if no matching resource could be
+	 *         found, if this bundle wiring is for a fragment revision or if the
+	 *         caller does not have the appropriate
+	 *         {@code AdminPermission[bundle,RESOURCE]} and the Java Runtime
+	 *         Environment supports permissions. The collection is unordered and
+	 *         must contain no duplicate resource names. If this bundle wiring
+	 *         is not {@link #isInUse() in use}, {@code null} must be returned.
+	 */
+	Collection<String> listResources(String path, String filePattern, int options);
+
+	/**
+	 * The list resource names operation must recurse into subdirectories.
+	 * 
+	 * <p>
+	 * This bit may be set when calling
+	 * {@link #listResources(String, String, int)} to specify the result must
+	 * include the names of matching resources from the specified path and its
+	 * subdirectories. If this bit is not set, then the result must only include
+	 * names of matching resources from the specified path.
+	 * 
+	 * @see #listResources(String, String, int)
+	 */
+	int	LISTRESOURCES_RECURSE	= 0x00000001;
+
+	/**
+	 * The list resource names operation must limit the result to the names of
+	 * matching resources contained in this bundle wiring's
+	 * {@link #getRevision() bundle revision} and its attached fragment
+	 * revisions. The result must not include resource names for resources in
+	 * {@link PackageNamespace package} names which are
+	 * {@link #getRequiredWires(String) imported} by this wiring.
+	 * 
+	 * <p>
+	 * This bit may be set when calling
+	 * {@link #listResources(String, String, int)} to specify the result must
+	 * only include the names of matching resources contained in this bundle
+	 * wiring's bundle revision and its attached fragment revisions. If this bit
+	 * is not set, then the result must include the names of matching resources
+	 * reachable from this bundle wiring's class loader which may include the
+	 * names of matching resources contained in imported packages and required
+	 * bundles.
+	 * 
+	 * @see #listResources(String, String, int)
+	 */
+	int	LISTRESOURCES_LOCAL		= 0x00000002;
+
+	/**
+	 * Returns the capabilities provided by this wiring.
+	 * 
+	 * <p>
+	 * Only capabilities considered by the resolver are returned. For example,
+	 * capabilities with {@link Namespace#CAPABILITY_EFFECTIVE_DIRECTIVE
+	 * effective} directive not equal to {@link Namespace#EFFECTIVE_RESOLVE
+	 * resolve} are not returned.
+	 * 
+	 * <p>
+	 * A capability may not be required by any wiring and thus there may be no
+	 * {@link #getProvidedResourceWires(String) wires} for the capability.
+	 * 
+	 * <p>
+	 * A wiring for a non-fragment resource provides a subset of the declared
+	 * capabilities from the resource and all attached fragment
+	 * resources<sup>†</sup>. Not all declared capabilities may be
+	 * provided since some may be discarded. For example, if a package is
+	 * declared to be both exported and imported, only one is selected and the
+	 * other is discarded.
+	 * <p>
+	 * A wiring for a fragment resource with a symbolic name must provide
+	 * exactly one {@code osgi.identity} capability.
+	 * <p>
+	 * † The {@code osgi.identity} capability provided by attached
+	 * fragment resource must not be included in the capabilities of the host
+	 * wiring.
+	 * 
+	 * <p>
+	 * This method returns the same value as {@link #getCapabilities(String)}.
+	 * 
+	 * @param namespace The namespace of the capabilities to return or
+	 *        {@code null} to return the capabilities from all namespaces.
+	 * @return A list containing a snapshot of the {@link Capability}s, or an
+	 *         empty list if this wiring provides no capabilities in the
+	 *         specified namespace. For a given namespace, the list contains the
+	 *         wires in the order the capabilities were specified in the
+	 *         manifests of the {@link #getResource() resource} and the attached
+	 *         fragment resources<sup>†</sup> of this wiring. There is no
+	 *         ordering defined between capabilities in different namespaces.
+	 * @since 1.1
+	 */
+	List<Capability> getResourceCapabilities(String namespace);
+
+	/**
+	 * Returns the requirements of this wiring.
+	 * 
+	 * <p>
+	 * Only requirements considered by the resolver are returned. For example,
+	 * requirements with {@link Namespace#REQUIREMENT_EFFECTIVE_DIRECTIVE
+	 * effective} directive not equal to {@link Namespace#EFFECTIVE_RESOLVE
+	 * resolve} are not returned.
+	 * 
+	 * <p>
+	 * A wiring for a non-fragment resource has a subset of the declared
+	 * requirements from the resource and all attached fragment resources. Not
+	 * all declared requirements may be present since some may be discarded. For
+	 * example, if a package is declared to be optionally imported and is not
+	 * actually imported, the requirement must be discarded.
+	 * 
+	 * <p>
+	 * This method returns the same value as {@link #getRequirements(String)}.
+	 * 
+	 * @param namespace The namespace of the requirements to return or
+	 *        {@code null} to return the requirements from all namespaces.
+	 * @return A list containing a snapshot of the {@link Requirement}s, or an
+	 *         empty list if this wiring uses no requirements in the specified
+	 *         namespace. For a given namespace, the list contains the wires in
+	 *         the order the requirements were specified in the manifests of the
+	 *         {@link #getResource() resource} and the attached fragment
+	 *         resources of this wiring. There is no ordering defined between
+	 *         requirements in different namespaces.
+	 * @since 1.1
+	 */
+	List<Requirement> getResourceRequirements(String namespace);
+
+	/**
+	 * Returns the {@link Wire}s to the provided {@link Capability capabilities}
+	 * of this wiring.
+	 * 
+	 * <p>
+	 * This method returns the same value as {@link #getProvidedWires(String)}.
+	 * 
+	 * @param namespace The namespace of the capabilities for which to return
+	 *        wires or {@code null} to return the wires for the capabilities in
+	 *        all namespaces.
+	 * @return A list containing a snapshot of the {@link Wire}s for the
+	 *         {@link Capability capabilities} of this wiring, or an empty list
+	 *         if this wiring has no capabilities in the specified namespace.
+	 *         For a given namespace, the list contains the wires in the order
+	 *         the capabilities were specified in the manifests of the
+	 *         {@link #getResource() resource} and the attached fragment
+	 *         resources of this wiring. There is no ordering defined between
+	 *         capabilities in different namespaces.
+	 * @since 1.1
+	 */
+	List<Wire> getProvidedResourceWires(String namespace);
+
+	/**
+	 * Returns the {@link Wire}s to the {@link Requirement requirements} in use
+	 * by this wiring.
+	 * 
+	 * <p>
+	 * This method returns the same value as {@link #getRequiredWires(String)}.
+	 * 
+	 * @param namespace The namespace of the requirements for which to return
+	 *        wires or {@code null} to return the wires for the requirements in
+	 *        all namespaces.
+	 * @return A list containing a snapshot of the {@link Wire}s for the
+	 *         {@link Requirement requirements} of this wiring, or an empty list
+	 *         if this wiring has no requirements in the specified namespace.
+	 *         For a given namespace, the list contains the wires in the order
+	 *         the requirements were specified in the manifests of the
+	 *         {@link #getResource() resource} and the attached fragment
+	 *         resources of this wiring. There is no ordering defined between
+	 *         requirements in different namespaces.
+	 * @since 1.1
+	 */
+	List<Wire> getRequiredResourceWires(String namespace);
+
+	/**
+	 * Returns the resource associated with this wiring.
+	 * 
+	 * <p>
+	 * This method returns the same value as {@link #getRevision()}.
+	 * 
+	 * @return The resource associated with this wiring.
+	 * @since 1.1
+	 */
+	BundleRevision getResource();
+}
diff --git a/src/main/java/org/osgi/framework/wiring/FrameworkWiring.java b/src/main/java/org/osgi/framework/wiring/FrameworkWiring.java
new file mode 100644
index 0000000..ce9952c
--- /dev/null
+++ b/src/main/java/org/osgi/framework/wiring/FrameworkWiring.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (c) OSGi Alliance (2001, 2012). All Rights Reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.framework.wiring;
+
+import java.util.Collection;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleReference;
+import org.osgi.framework.FrameworkListener;
+
+/**
+ * Query and modify wiring information for the framework. The framework wiring
+ * object for the framework can be obtained by calling
+ * {@link Bundle#adapt(Class) bundle.adapt(FrameworkWiring.class)} on the system
+ * bundle. Only the system bundle can be adapted to a FrameworkWiring object.
+ * 
+ * <p>
+ * The system bundle associated with this FrameworkWiring object can be obtained
+ * by calling {@link BundleReference#getBundle()}.
+ * 
+ * @ThreadSafe
+ * @noimplement
+ * @version $Id: bff4cdf85c632e2946e18c1640a86e80c069dd37 $
+ */
+public interface FrameworkWiring extends BundleReference {
+	/**
+	 * Refreshes the specified bundles. This forces the update (replacement) or
+	 * removal of packages exported by the specified bundles.
+	 * 
+	 * <p>
+	 * The technique by which the framework refreshes bundles may vary among
+	 * different framework implementations. A permissible implementation is to
+	 * stop and restart the framework.
+	 * 
+	 * <p>
+	 * This method returns to the caller immediately and then performs the
+	 * following steps on a separate thread:
+	 * 
+	 * <ol>
+	 * <li>Compute the {@link #getDependencyClosure(Collection) dependency
+	 * closure} of the specified bundles. If no bundles are specified, compute
+	 * the dependency closure of the {@link #getRemovalPendingBundles() removal
+	 * pending} bundles.
+	 * 
+	 * <li>Each bundle in the dependency closure that is in the {@code ACTIVE}
+	 * state will be stopped as described in the {@code Bundle.stop} method.
+	 * 
+	 * <li>Each bundle in the dependency closure that is in the {@code RESOLVED}
+	 * state is unresolved and thus moved to the {@code INSTALLED} state. The
+	 * effect of this step is that bundles in the dependency closure are no
+	 * longer {@code RESOLVED}.
+	 * 
+	 * <li>Each bundle in the dependency closure that is in the
+	 * {@code UNINSTALLED} state is removed from the dependency closure and is
+	 * now completely removed from the Framework.
+	 * 
+	 * <li>Each bundle in the dependency closure that was in the {@code ACTIVE}
+	 * state prior to Step 2 is started as described in the {@code Bundle.start}
+	 * 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 bundles satisfying the dependencies have been
+	 * installed in the Framework.
+	 * </ol>
+	 * 
+	 * <p>
+	 * For any exceptions that are thrown during any of these steps, a framework
+	 * event of type {@code FrameworkEvent.ERROR} is fired 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. All framework events fired by this method
+	 * are also delivered to the specified {@code FrameworkListener}s in the
+	 * order they are specified.
+	 * 
+	 * <p>
+	 * When this process completes after the bundles are refreshed, the
+	 * Framework will fire a Framework event of type
+	 * {@code FrameworkEvent.PACKAGES_REFRESHED} to announce it has completed
+	 * the bundle refresh. The specified {@code FrameworkListener}s are notified
+	 * in the order specified. Each specified {@code FrameworkListener} will be
+	 * called with a Framework event of type
+	 * {@code FrameworkEvent.PACKAGES_REFRESHED}.
+	 * 
+	 * @param bundles The bundles to be refreshed, or {@code null} to refresh
+	 *        the {@link #getRemovalPendingBundles() removal pending} bundles.
+	 * @param listeners Zero or more listeners to be notified when the bundle
+	 *        refresh has been completed. The specified listeners do not need to
+	 *        be otherwise registered with the framework. If a specified
+	 *        listener is already registered with the framework, it will be
+	 *        notified twice.
+	 * @throws IllegalArgumentException If the specified {@code Bundle}s were
+	 *         not created by the same framework instance associated with this
+	 *         FrameworkWiring.
+	 * @throws SecurityException If the caller does not have
+	 *         {@code AdminPermission[System Bundle,RESOLVE]} and the Java
+	 *         runtime environment supports permissions.
+	 */
+	void refreshBundles(Collection<Bundle> bundles, FrameworkListener... listeners);
+
+	/**
+	 * Resolves 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 no bundles are 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} to resolve all
+	 *        unresolved bundles installed in the Framework.
+	 * @return {@code true} if all specified bundles are resolved; {@code false}
+	 *         otherwise.
+	 * @throws IllegalArgumentException If the specified {@code Bundle}s were
+	 *         not created by the same framework instance associated with this
+	 *         FrameworkWiring.
+	 * @throws SecurityException If the caller does not have
+	 *         {@code AdminPermission[System Bundle,RESOLVE]} and the Java
+	 *         runtime environment supports permissions.
+	 */
+	boolean resolveBundles(Collection<Bundle> bundles);
+
+	/**
+	 * Returns the bundles that have {@link BundleWiring#isCurrent()
+	 * non-current}, {@link BundleWiring#isInUse() in use} bundle wirings. This
+	 * is typically the bundles which have been updated or uninstalled since the
+	 * last call to {@link #refreshBundles(Collection, FrameworkListener...)}.
+	 * 
+	 * @return A collection containing a snapshot of the {@code Bundle}s which
+	 *         have non-current, in use {@code BundleWiring}s, or an empty
+	 *         collection if there are no such bundles.
+	 */
+	Collection<Bundle> getRemovalPendingBundles();
+
+	/**
+	 * Returns the dependency closure for the specified bundles.
+	 * 
+	 * <p>
+	 * A graph of bundles is computed starting with the specified bundles. The
+	 * graph is expanded by adding any bundle that is either wired to a package
+	 * that is currently exported by a bundle in the graph or requires a bundle
+	 * in the graph. The graph is fully constructed when there is no bundle
+	 * outside the graph that is wired to a bundle in the graph. The graph may
+	 * contain {@code UNINSTALLED} bundles that are
+	 * {@link #getRemovalPendingBundles() removal pending}.
+	 * 
+	 * @param bundles The initial bundles for which to generate the dependency
+	 *        closure.
+	 * @return A collection containing a snapshot of the dependency closure of
+	 *         the specified bundles, or an empty collection if there were no
+	 *         specified bundles.
+	 * @throws IllegalArgumentException If the specified {@code Bundle}s were
+	 *         not created by the same framework instance associated with this
+	 *         FrameworkWiring.
+	 */
+	Collection<Bundle> getDependencyClosure(Collection<Bundle> bundles);
+}
diff --git a/src/main/java/org/osgi/resource/Capability.java b/src/main/java/org/osgi/resource/Capability.java
new file mode 100644
index 0000000..bda0ce6
--- /dev/null
+++ b/src/main/java/org/osgi/resource/Capability.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) OSGi Alliance (2011, 2012). All Rights Reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.resource;
+
+import java.util.Map;
+
+/**
+ * A capability that has been declared from a {@link Resource}.
+ * 
+ * <p>
+ * Instances of this type must be <i>effectively immutable</i>. That is, for a
+ * given instance of this interface, the methods defined by this interface must
+ * always return the same result.
+ * 
+ * @ThreadSafe
+ * @version $Id: 5f40514f7bf45f6dce59651e8812b0922580e77e $
+ */
+public interface Capability {
+
+	/**
+	 * Returns the namespace of this capability.
+	 * 
+	 * @return The namespace of this capability.
+	 */
+	String getNamespace();
+
+	/**
+	 * Returns the directives of this capability.
+	 * 
+	 * @return An unmodifiable map of directive names to directive values for
+	 *         this capability, or an empty map if this capability has no
+	 *         directives.
+	 */
+	Map<String, String> getDirectives();
+
+	/**
+	 * Returns the attributes of this capability.
+	 * 
+	 * @return An unmodifiable map of attribute names to attribute values for
+	 *         this capability, or an empty map if this capability has no
+	 *         attributes.
+	 */
+	Map<String, Object> getAttributes();
+
+	/**
+	 * Returns the resource declaring this capability.
+	 * 
+	 * @return The resource declaring this capability.
+	 */
+	Resource getResource();
+
+	/**
+	 * Compares this {@code Capability} to another {@code Capability}.
+	 * 
+	 * <p>
+	 * This {@code Capability} is equal to another {@code Capability} if they
+	 * have the same namespace, directives and attributes and are declared by
+	 * the same resource.
+	 * 
+	 * @param obj The object to compare against this {@code Capability}.
+	 * @return {@code true} if this {@code Capability} is equal to the other
+	 *         object; {@code false} otherwise.
+	 */
+	boolean equals(Object obj);
+
+	/**
+	 * Returns the hashCode of this {@code Capability}.
+	 * 
+	 * @return The hashCode of this {@code Capability}.
+	 */
+	int hashCode();
+}
diff --git a/src/main/java/org/osgi/resource/Namespace.java b/src/main/java/org/osgi/resource/Namespace.java
new file mode 100644
index 0000000..abda616
--- /dev/null
+++ b/src/main/java/org/osgi/resource/Namespace.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) OSGi Alliance (2012). All Rights Reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.resource;
+
+/**
+ * Capability and Requirement Namespaces base class.
+ * 
+ * <p>
+ * This class is the common class shared by all OSGi defined namespaces. It
+ * defines the names for the common attributes and directives for the OSGi
+ * specified namespaces.
+ * 
+ * <p>
+ * The OSGi Alliance reserves the right to extend the set of directives and
+ * attributes which have specified semantics for all of the specified
+ * namespaces.
+ * 
+ * <p>
+ * The values associated with these keys are of type {@code String}, unless
+ * otherwise indicated.
+ * 
+ * @Immutable
+ * @version $Id: 43c9ff5cea19546d71c4703db71a2b5070a3f2fa $
+ */
+public abstract class Namespace {
+
+	/**
+	 * The capability directive used to specify the comma separated list of
+	 * package names used by a capability.
+	 */
+	public final static String	CAPABILITY_USES_DIRECTIVE			= "uses";
+
+	/**
+	 * The capability directive used to specify the effective time for the
+	 * capability. The default value is {@link #EFFECTIVE_RESOLVE resolve}.
+	 * 
+	 * @see #EFFECTIVE_RESOLVE resolve
+	 * @see #EFFECTIVE_ACTIVE active
+	 */
+	public final static String	CAPABILITY_EFFECTIVE_DIRECTIVE		= "effective";
+
+	/**
+	 * The requirement directive used to specify a capability filter. This
+	 * filter is used to match against a capability's attributes.
+	 */
+	public final static String	REQUIREMENT_FILTER_DIRECTIVE		= "filter";
+
+	/**
+	 * The requirement directive used to specify the resolution type for a
+	 * requirement. The default value is {@link #RESOLUTION_MANDATORY mandatory}
+	 * .
+	 * 
+	 * @see #RESOLUTION_MANDATORY mandatory
+	 * @see #RESOLUTION_OPTIONAL optional
+	 */
+	public final static String	REQUIREMENT_RESOLUTION_DIRECTIVE	= "resolution";
+
+	/**
+	 * The directive value identifying a mandatory requirement resolution type.
+	 * A mandatory resolution type indicates that the requirement must be
+	 * resolved when the resource is resolved. If such a requirement cannot be
+	 * resolved, the resource fails to resolve.
+	 * 
+	 * @see #REQUIREMENT_RESOLUTION_DIRECTIVE
+	 */
+	public final static String	RESOLUTION_MANDATORY				= "mandatory";
+
+	/**
+	 * The directive value identifying an optional requirement resolution type.
+	 * An optional resolution type indicates that the requirement is optional
+	 * and the resource may be resolved without the requirement being resolved.
+	 * 
+	 * @see #REQUIREMENT_RESOLUTION_DIRECTIVE
+	 */
+	public final static String	RESOLUTION_OPTIONAL					= "optional";
+
+	/**
+	 * The requirement directive used to specify the effective time for the
+	 * requirement. The default value is {@link #EFFECTIVE_RESOLVE resolve}.
+	 * 
+	 * @see #EFFECTIVE_RESOLVE resolve
+	 * @see #EFFECTIVE_ACTIVE active
+	 */
+	public final static String	REQUIREMENT_EFFECTIVE_DIRECTIVE		= "effective";
+
+	/**
+	 * The directive value identifying a {@link #CAPABILITY_EFFECTIVE_DIRECTIVE
+	 * capability} or {@link #REQUIREMENT_EFFECTIVE_DIRECTIVE requirement} that
+	 * is effective at resolve time. Capabilities and requirements with an
+	 * effective time of resolve are the only capabilities which are processed
+	 * while resolving a resource.
+	 * 
+	 * @see #REQUIREMENT_EFFECTIVE_DIRECTIVE
+	 * @see #CAPABILITY_EFFECTIVE_DIRECTIVE
+	 */
+	public final static String	EFFECTIVE_RESOLVE					= "resolve";
+
+	/**
+	 * The directive value identifying a {@link #CAPABILITY_EFFECTIVE_DIRECTIVE
+	 * capability} or {@link #REQUIREMENT_EFFECTIVE_DIRECTIVE requirement} that
+	 * is effective at active time. Capabilities and requirements with an
+	 * effective time of active are ignored while resolving a resource.
+	 * 
+	 * @see #REQUIREMENT_EFFECTIVE_DIRECTIVE
+	 * @see #CAPABILITY_EFFECTIVE_DIRECTIVE
+	 */
+	public final static String	EFFECTIVE_ACTIVE					= "active";
+
+	/**
+	 * The requirement directive used to specify the cardinality for a
+	 * requirement. The default value is {@link #CARDINALITY_SINGLE single}.
+	 * 
+	 * @see #CARDINALITY_MULTIPLE multiple
+	 * @see #CARDINALITY_SINGLE single
+	 */
+	public final static String	REQUIREMENT_CARDINALITY_DIRECTIVE	= "cardinality";
+
+	/**
+	 * The directive value identifying a multiple
+	 * {@link #REQUIREMENT_CARDINALITY_DIRECTIVE cardinality} type.
+	 * 
+	 * @see #REQUIREMENT_CARDINALITY_DIRECTIVE
+	 */
+	public final static String	CARDINALITY_MULTIPLE				= "multiple";
+
+	/**
+	 * The directive value identifying a
+	 * {@link #REQUIREMENT_CARDINALITY_DIRECTIVE cardinality} type of single.
+	 * 
+	 * @see #REQUIREMENT_CARDINALITY_DIRECTIVE
+	 */
+	public final static String	CARDINALITY_SINGLE					= "single";
+
+	/**
+	 * Protected constructor for Namespace sub-types.
+	 */
+	protected Namespace() {
+		// empty
+	}
+}
diff --git a/src/main/java/org/osgi/resource/Requirement.java b/src/main/java/org/osgi/resource/Requirement.java
new file mode 100644
index 0000000..cf931bf
--- /dev/null
+++ b/src/main/java/org/osgi/resource/Requirement.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) OSGi Alliance (2011, 2012). All Rights Reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.resource;
+
+import java.util.Map;
+
+/**
+ * A requirement that has been declared from a {@link Resource} .
+ * 
+ * <p>
+ * Instances of this type must be <i>effectively immutable</i>. That is, for a
+ * given instance of this interface, the methods defined by this interface must
+ * always return the same result.
+ * 
+ * @ThreadSafe
+ * @version $Id: 212b26179910f98fd2c59c3e1e7dd0d086f42b5d $
+ */
+public interface Requirement {
+	/**
+	 * Returns the namespace of this requirement.
+	 * 
+	 * @return The namespace of this requirement.
+	 */
+	String getNamespace();
+
+	/**
+	 * Returns the directives of this requirement.
+	 * 
+	 * @return An unmodifiable map of directive names to directive values for
+	 *         this requirement, or an empty map if this requirement has no
+	 *         directives.
+	 */
+	Map<String, String> getDirectives();
+
+	/**
+	 * Returns the attributes of this requirement.
+	 * 
+	 * <p>
+	 * Requirement attributes have no specified semantics and are considered
+	 * extra user defined information.
+	 * 
+	 * @return An unmodifiable map of attribute names to attribute values for
+	 *         this requirement, or an empty map if this requirement has no
+	 *         attributes.
+	 */
+	Map<String, Object> getAttributes();
+
+	/**
+	 * Returns the resource declaring this requirement.
+	 * 
+	 * @return The resource declaring this requirement. This can be {@code null}
+	 *         if this requirement is synthesized.
+	 */
+	Resource getResource();
+
+	/**
+	 * Compares this {@code Requirement} to another {@code Requirement}.
+	 * 
+	 * <p>
+	 * This {@code Requirement} is equal to another {@code Requirement} if they
+	 * have the same namespace, directives and attributes and are declared by
+	 * the same resource.
+	 * 
+	 * @param obj The object to compare against this {@code Requirement}.
+	 * @return {@code true} if this {@code Requirement} is equal to the other
+	 *         object; {@code false} otherwise.
+	 */
+	boolean equals(Object obj);
+
+	/**
+	 * Returns the hashCode of this {@code Requirement}.
+	 * 
+	 * @return The hashCode of this {@code Requirement}.
+	 */
+	int hashCode();
+}
diff --git a/src/main/java/org/osgi/resource/Resource.java b/src/main/java/org/osgi/resource/Resource.java
new file mode 100644
index 0000000..455d891
--- /dev/null
+++ b/src/main/java/org/osgi/resource/Resource.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) OSGi Alliance (2011, 2012). All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.resource;
+
+import java.util.List;
+
+/**
+ * A resource is the representation of a uniquely identified and typed data. A
+ * resource declares requirements that need to be satisfied by capabilities
+ * before it can provide its capabilities.
+ * 
+ * <p>
+ * Instances of this type must be <i>effectively immutable</i>. That is, for a
+ * given instance of this interface, the methods defined by this interface must
+ * always return the same result.
+ * 
+ * @ThreadSafe
+ * @version $Id: 40958d5777ee269d27d58e9f646a4c91bcc6daa4 $
+ */
+public interface Resource {
+	/**
+	 * Returns the capabilities declared by this resource.
+	 * 
+	 * @param namespace The namespace of the declared capabilities to return or
+	 *        {@code null} to return the declared capabilities from all
+	 *        namespaces.
+	 * @return An unmodifiable list containing the declared {@link Capability}s
+	 *         from the specified namespace. The returned list will be empty if
+	 *         this resource declares no capabilities in the specified
+	 *         namespace.
+	 */
+	List<Capability> getCapabilities(String namespace);
+
+	/**
+	 * Returns the requirements declared by this bundle resource.
+	 * 
+	 * @param namespace The namespace of the declared requirements to return or
+	 *        {@code null} to return the declared requirements from all
+	 *        namespaces.
+	 * @return An unmodifiable list containing the declared {@link Requirement}
+	 *         s from the specified namespace. The returned list will be empty
+	 *         if this resource declares no requirements in the specified
+	 *         namespace.
+	 */
+	List<Requirement> getRequirements(String namespace);
+
+	/**
+	 * Compares this {@code Resource} to another {@code Resource}.
+	 * 
+	 * <p>
+	 * This {@code Resource} is equal to another {@code Resource} if both have
+	 * the same content and come from the same location. Location may be defined
+	 * as the bundle location if the resource is an installed bundle or the
+	 * repository location if the resource is in a repository.
+	 * 
+	 * @param obj The object to compare against this {@code Resource}.
+	 * @return {@code true} if this {@code Resource} is equal to the other
+	 *         object; {@code false} otherwise.
+	 */
+	boolean equals(Object obj);
+
+	/**
+	 * Returns the hashCode of this {@code Resource}.
+	 * 
+	 * @return The hashCode of this {@code Resource}.
+	 */
+	int hashCode();
+}
diff --git a/src/main/java/org/osgi/resource/Wire.java b/src/main/java/org/osgi/resource/Wire.java
new file mode 100644
index 0000000..18feab8
--- /dev/null
+++ b/src/main/java/org/osgi/resource/Wire.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) OSGi Alliance (2011, 2012). All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.resource;
+
+/**
+ * A wire connecting a {@link Capability} to a {@link Requirement}.
+ * 
+ * <p>
+ * Instances of this type must be <i>effectively immutable</i>. That is, for a
+ * given instance of this interface, the methods defined by this interface must
+ * always return the same result.
+ * 
+ * @ThreadSafe
+ * @version $Id: d7ca9a5d3e8dd2277f8243a750e40fbcf79185bd $
+ */
+public interface Wire {
+	/**
+	 * Returns the {@link Capability} for this wire.
+	 * 
+	 * @return The {@link Capability} for this wire.
+	 */
+	Capability getCapability();
+
+	/**
+	 * Returns the {@link Requirement} for this wire.
+	 * 
+	 * @return The {@link Requirement} for this wire.
+	 */
+	Requirement getRequirement();
+
+	/**
+	 * Returns the resource providing the {@link #getCapability() capability}.
+	 * 
+	 * <p>
+	 * The returned resource may differ from the resource referenced by the
+	 * {@link #getCapability() capability}.
+	 * 
+	 * @return The resource providing the capability.
+	 */
+	Resource getProvider();
+
+	/**
+	 * Returns the resource who {@link #getRequirement() requires} the
+	 * {@link #getCapability() capability}.
+	 * 
+	 * <p>
+	 * The returned resource may differ from the resource referenced by the
+	 * {@link #getRequirement() requirement}.
+	 * 
+	 * @return The resource who requires the capability.
+	 */
+	Resource getRequirer();
+
+	/**
+	 * Compares this {@code Wire} to another {@code Wire}.
+	 * 
+	 * <p>
+	 * This {@code Wire} is equal to another {@code Wire} if they have the same
+	 * capability, requirement, provider and requirer.
+	 * 
+	 * @param obj The object to compare against this {@code Wire}.
+	 * @return {@code true} if this {@code Wire} is equal to the other object;
+	 *         {@code false} otherwise.
+	 */
+	boolean equals(Object obj);
+
+	/**
+	 * Returns the hashCode of this {@code Wire}.
+	 * 
+	 * @return The hashCode of this {@code Wire}.
+	 */
+	int hashCode();
+}
diff --git a/src/main/java/org/osgi/resource/Wiring.java b/src/main/java/org/osgi/resource/Wiring.java
new file mode 100644
index 0000000..230fef0
--- /dev/null
+++ b/src/main/java/org/osgi/resource/Wiring.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) OSGi Alliance (2011, 2012). All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.resource;
+
+import java.util.List;
+
+/**
+ * A wiring for a resource. A wiring is associated with a resource and
+ * represents the dependencies with other wirings.
+ * 
+ * <p>
+ * Instances of this type must be <i>effectively immutable</i>. That is, for a
+ * given instance of this interface, the methods defined by this interface must
+ * always return the same result.
+ * 
+ * @ThreadSafe
+ * @version $Id: b65dec3887cfa1d5731e860db558a01503c0f47d $
+ */
+public interface Wiring {
+	/**
+	 * Returns the capabilities provided by this wiring.
+	 * 
+	 * <p>
+	 * Only capabilities considered by the resolver are returned. For example,
+	 * capabilities with {@link Namespace#CAPABILITY_EFFECTIVE_DIRECTIVE
+	 * effective} directive not equal to {@link Namespace#EFFECTIVE_RESOLVE
+	 * resolve} are not returned.
+	 * 
+	 * <p>
+	 * A capability may not be required by any wiring and thus there may be no
+	 * {@link #getProvidedResourceWires(String) wires} for the capability.
+	 * 
+	 * <p>
+	 * A wiring for a non-fragment resource provides a subset of the declared
+	 * capabilities from the resource and all attached fragment
+	 * resources<sup>†</sup>. Not all declared capabilities may be
+	 * provided since some may be discarded. For example, if a package is
+	 * declared to be both exported and imported, only one is selected and the
+	 * other is discarded.
+	 * <p>
+	 * A wiring for a fragment resource with a symbolic name must provide
+	 * exactly one {@code osgi.identity} capability.
+	 * <p>
+	 * † The {@code osgi.identity} capability provided by attached
+	 * fragment resource must not be included in the capabilities of the host
+	 * wiring.
+	 * 
+	 * @param namespace The namespace of the capabilities to return or
+	 *        {@code null} to return the capabilities from all namespaces.
+	 * @return A list containing a snapshot of the {@link Capability}s, or an
+	 *         empty list if this wiring provides no capabilities in the
+	 *         specified namespace. For a given namespace, the list contains the
+	 *         wires in the order the capabilities were specified in the
+	 *         manifests of the {@link #getResource() resource} and the attached
+	 *         fragment resources<sup>†</sup> of this wiring. There is no
+	 *         ordering defined between capabilities in different namespaces.
+	 */
+	List<Capability> getResourceCapabilities(String namespace);
+
+	/**
+	 * Returns the requirements of this wiring.
+	 * 
+	 * <p>
+	 * Only requirements considered by the resolver are returned. For example,
+	 * requirements with {@link Namespace#REQUIREMENT_EFFECTIVE_DIRECTIVE
+	 * effective} directive not equal to {@link Namespace#EFFECTIVE_RESOLVE
+	 * resolve} are not returned.
+	 * 
+	 * <p>
+	 * A wiring for a non-fragment resource has a subset of the declared
+	 * requirements from the resource and all attached fragment resources. Not
+	 * all declared requirements may be present since some may be discarded. For
+	 * example, if a package is declared to be optionally imported and is not
+	 * actually imported, the requirement must be discarded.
+	 * 
+	 * @param namespace The namespace of the requirements to return or
+	 *        {@code null} to return the requirements from all namespaces.
+	 * @return A list containing a snapshot of the {@link Requirement}s, or an
+	 *         empty list if this wiring uses no requirements in the specified
+	 *         namespace. For a given namespace, the list contains the wires in
+	 *         the order the requirements were specified in the manifests of the
+	 *         {@link #getResource() resource} and the attached fragment
+	 *         resources of this wiring. There is no ordering defined between
+	 *         requirements in different namespaces.
+	 */
+	List<Requirement> getResourceRequirements(String namespace);
+
+	/**
+	 * Returns the {@link Wire}s to the provided {@link Capability capabilities}
+	 * of this wiring.
+	 * 
+	 * @param namespace The namespace of the capabilities for which to return
+	 *        wires or {@code null} to return the wires for the capabilities in
+	 *        all namespaces.
+	 * @return A list containing a snapshot of the {@link Wire}s for the
+	 *         {@link Capability capabilities} of this wiring, or an empty list
+	 *         if this wiring has no capabilities in the specified namespace.
+	 *         For a given namespace, the list contains the wires in the order
+	 *         the capabilities were specified in the manifests of the
+	 *         {@link #getResource() resource} and the attached fragment
+	 *         resources of this wiring. There is no ordering defined between
+	 *         capabilities in different namespaces.
+	 */
+	List<Wire> getProvidedResourceWires(String namespace);
+
+	/**
+	 * Returns the {@link Wire}s to the {@link Requirement requirements} in use
+	 * by this wiring.
+	 * 
+	 * @param namespace The namespace of the requirements for which to return
+	 *        wires or {@code null} to return the wires for the requirements in
+	 *        all namespaces.
+	 * @return A list containing a snapshot of the {@link Wire}s for the
+	 *         {@link Requirement requirements} of this wiring, or an empty list
+	 *         if this wiring has no requirements in the specified namespace.
+	 *         For a given namespace, the list contains the wires in the order
+	 *         the requirements were specified in the manifests of the
+	 *         {@link #getResource() resource} and the attached fragment
+	 *         resources of this wiring. There is no ordering defined between
+	 *         requirements in different namespaces.
+	 */
+	List<Wire> getRequiredResourceWires(String namespace);
+
+	/**
+	 * Returns the resource associated with this wiring.
+	 * 
+	 * @return The resource associated with this wiring.
+	 */
+	Resource getResource();
+}
diff --git a/src/main/java/org/osgi/service/packageadmin/ExportedPackage.java b/src/main/java/org/osgi/service/packageadmin/ExportedPackage.java
new file mode 100644
index 0000000..4dd3a3a
--- /dev/null
+++ b/src/main/java/org/osgi/service/packageadmin/ExportedPackage.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) OSGi Alliance (2001, 2011). All Rights Reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.service.packageadmin;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.Version;
+
+/**
+ * An exported package.
+ * 
+ * Objects implementing this interface are created by the Package Admin service.
+ * 
+ * <p>
+ * The term <i>exported package</i> refers to a package that has been exported
+ * from a resolved bundle. This package may or may not be currently wired to
+ * other bundles.
+ * 
+ * <p>
+ * The information about an exported package provided by this object may change.
+ * An {@code ExportedPackage} object becomes stale if the package it
+ * references has been updated or removed as a result of calling
+ * {@code PackageAdmin.refreshPackages()}.
+ * 
+ * If this object becomes stale, its {@code getName()} and
+ * {@code getVersion()} methods continue to return their original values,
+ * {@code isRemovalPending()} returns {@code true}, and
+ * {@code getExportingBundle()} and {@code getImportingBundles()}
+ * return {@code null}.
+ * 
+ * @ThreadSafe
+ * @noimplement
+ * @deprecated The PackageAdmin service has been replaced by the
+ *             <code>org.osgi.framework.wiring</code> package.
+ * @version $Id: 22ce5e8e388107b04edba3aea2f3036b8026798d $
+ */
+public interface ExportedPackage {
+	/**
+	 * Returns the name of the package associated with this exported package.
+	 * 
+	 * @return The name of this exported package.
+	 */
+	public String getName();
+
+	/**
+	 * Returns the bundle exporting the package associated with this exported
+	 * package.
+	 * 
+	 * @return The exporting bundle, or {@code null} if this
+	 *         {@code ExportedPackage} object has become stale.
+	 */
+	public Bundle getExportingBundle();
+
+	/**
+	 * Returns the resolved bundles that are currently wired to this exported
+	 * package.
+	 * 
+	 * <p>
+	 * Bundles which require the exporting bundle associated with this exported
+	 * package are considered to be wired to this exported package are included
+	 * in the returned array. See {@link RequiredBundle#getRequiringBundles()}.
+	 * 
+	 * @return The array of resolved bundles currently wired to this exported
+	 *         package, or {@code null} if this
+	 *         {@code ExportedPackage} object has become stale. The array
+	 *         will be empty if no bundles are wired to this exported package.
+	 */
+	public Bundle[] getImportingBundles();
+
+	/**
+	 * Returns the version of this exported package.
+	 * 
+	 * @return The version of this exported package, or {@code null} if no
+	 *         version information is available.
+	 * @deprecated As of 1.2, replaced by {@link #getVersion()}.
+	 */
+	public String getSpecificationVersion();
+
+	/**
+	 * Returns the version of this exported package.
+	 * 
+	 * @return The version of this exported package, or
+	 *         {@link Version#emptyVersion} if no version information is
+	 *         available.
+	 * @since 1.2
+	 */
+	public Version getVersion();
+
+	/**
+	 * Returns {@code true} if the package associated with this
+	 * {@code ExportedPackage} object has been exported by a bundle that
+	 * has been updated or uninstalled.
+	 * 
+	 * @return {@code true} if the associated package is being exported
+	 *         by a bundle that has been updated or uninstalled, or if this
+	 *         {@code ExportedPackage} object has become stale;
+	 *         {@code false} otherwise.
+	 */
+	public boolean isRemovalPending();
+}
diff --git a/src/main/java/org/osgi/service/packageadmin/PackageAdmin.java b/src/main/java/org/osgi/service/packageadmin/PackageAdmin.java
new file mode 100644
index 0000000..2c57890
--- /dev/null
+++ b/src/main/java/org/osgi/service/packageadmin/PackageAdmin.java
@@ -0,0 +1,298 @@
+/*
+ * Copyright (c) OSGi Alliance (2001, 2010). All Rights Reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.service.packageadmin;
+
+import org.osgi.framework.Bundle;
+
+/**
+ * Framework service which allows bundle programmers to inspect the package
+ * wiring state of bundles in the Framework as well as other functions related
+ * to the class loader network among bundles.
+ * 
+ * <p>
+ * If present, there will only be a single instance of this service registered
+ * with the Framework.
+ * 
+ * @ThreadSafe
+ * @noimplement
+ * @version $Id: a268c3bdc986080fa16bdb2f56ba1d3800d030dd $
+ * @deprecated This service has been replaced by the
+ *             <code>org.osgi.framework.wiring</code> package.
+ * @see org.osgi.service.packageadmin.ExportedPackage
+ * @see org.osgi.service.packageadmin.RequiredBundle
+ */
+public interface PackageAdmin {
+	/**
+	 * Gets the exported packages for the specified bundle.
+	 * 
+	 * @param bundle The bundle whose exported packages are to be returned, or
+	 *        {@code null} if all exported packages 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 known to be
+	 *        exported by the system bundle. This will include the package
+	 *        specified by the {@code org.osgi.framework.system.packages}
+	 *        system property as well as any other package exported by the
+	 *        framework implementation.
+	 * 
+	 * @return An array of exported packages, or {@code null} if the
+	 *         specified bundle has no exported packages.
+	 * @throws IllegalArgumentException If the specified {@code Bundle} was
+	 *         not created by the same framework instance that registered this
+	 *         {@code PackageAdmin} service.
+	 */
+	public ExportedPackage[] getExportedPackages(Bundle bundle);
+
+	/**
+	 * Gets the exported packages for the specified package name.
+	 * 
+	 * @param name The name of the exported packages to be returned.
+	 * 
+	 * @return An array of the exported packages, or {@code null} if no
+	 *         exported packages with the specified name exists.
+	 * @since 1.2
+	 */
+	public ExportedPackage[] getExportedPackages(String name);
+
+	/**
+	 * Gets the exported package for the specified package name.
+	 * 
+	 * <p>
+	 * If there are multiple exported packages with specified name, the exported
+	 * package with the highest version will be returned.
+	 * 
+	 * @param name The name of the exported package to be returned.
+	 * 
+	 * @return The exported package, or {@code null} if no exported
+	 *         package with the specified name exists.
+	 * @see #getExportedPackages(String)
+	 */
+	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 on a separate 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 bundle
+	 * updated or uninstalled since the last call to this method. Add to the
+	 * graph any bundle that is wired to 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 is wired to a bundle in the graph. The
+	 * graph may contain {@code UNINSTALLED} bundles that are currently
+	 * still exporting packages.
+	 * 
+	 * <li>Each bundle in the graph that is in the {@code ACTIVE} state
+	 * will be stopped as described in the {@code Bundle.stop} method.
+	 * 
+	 * <li>Each bundle in the graph that is in the {@code RESOLVED} state
+	 * is unresolved and thus moved to the {@code INSTALLED} state. The
+	 * effect of this step is that bundles in the graph are no longer
+	 * {@code RESOLVED}.
+	 * 
+	 * <li>Each bundle in the graph that is in the {@code UNINSTALLED}
+	 * 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} state
+	 * prior to Step 2 is started as described in the {@code Bundle.start}
+	 * 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} is fired.
+	 * </ol>
+	 * 
+	 * <p>
+	 * For any exceptions that are thrown during any of these steps, a
+	 * {@code FrameworkEvent} of type {@code ERROR} is fired
+	 * 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} for all bundles updated or
+	 *        uninstalled since the last call to this method.
+	 * @throws SecurityException If the caller does not have
+	 *         {@code AdminPermission[System Bundle,RESOLVE]} and the Java
+	 *         runtime environment supports permissions.
+	 * @throws IllegalArgumentException If the specified {@code Bundle}s
+	 *         were not created by the same framework instance that registered
+	 *         this {@code PackageAdmin} service.
+	 */
+	public void refreshPackages(Bundle[] bundles);
+
+	/**
+	 * 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} 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} to resolve all
+	 *        unresolved bundles installed in the Framework.
+	 * @return {@code true} if all specified bundles are resolved;
+	 * @throws SecurityException If the caller does not have
+	 *         {@code AdminPermission[System Bundle,RESOLVE]} and the Java
+	 *         runtime environment supports permissions.
+	 * @throws IllegalArgumentException If the specified {@code Bundle}s
+	 *         were not created by the same framework instance that registered
+	 *         this {@code PackageAdmin} service.
+	 * @since 1.2
+	 */
+	public boolean resolveBundles(Bundle[] bundles);
+
+	/**
+	 * Returns an array of required bundles having the specified symbolic name.
+	 * 
+	 * <p>
+	 * If {@code null} is specified, then all required bundles will be
+	 * returned.
+	 * 
+	 * @param symbolicName The bundle symbolic name or {@code null} for
+	 *        all required bundles.
+	 * @return An array of required bundles or {@code null} if no
+	 *         required bundles exist for the specified symbolic name.
+	 * @since 1.2
+	 */
+	public RequiredBundle[] getRequiredBundles(String symbolicName);
+
+	/**
+	 * Returns the bundles with the specified symbolic name whose bundle version
+	 * is within the specified version range. If no bundles are installed that
+	 * have the specified symbolic name, then {@code null} is returned.
+	 * If a version range is specified, then only the bundles that have the
+	 * specified symbolic name and whose bundle versions 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} 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} 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} is returned.
+	 * If no fragments are attached to the specified bundle then
+	 * {@code null} is returned.
+	 * <p>
+	 * This method does not attempt to resolve the specified bundle. If the
+	 * specified bundle is not resolved then {@code null} is returned.
+	 * 
+	 * @param bundle The bundle whose attached fragment bundles are to be
+	 *        returned.
+	 * @return An array of fragment bundles or {@code null} if the bundle
+	 *         does not have any attached fragment bundles or the bundle is not
+	 *         resolved.
+	 * @throws IllegalArgumentException If the specified {@code Bundle} was
+	 *         not created by the same framework instance that registered this
+	 *         {@code PackageAdmin} service.
+	 * @since 1.2
+	 */
+	public Bundle[] getFragments(Bundle bundle);
+
+	/**
+	 * Returns the host bundles to which the specified fragment bundle is
+	 * attached.
+	 * 
+	 * @param bundle The fragment bundle whose host bundles are to be returned.
+	 * @return An array containing the host bundles to which the specified
+	 *         fragment is attached or {@code null} if the specified bundle
+	 *         is not a fragment or is not attached to any host bundles.
+	 * @throws IllegalArgumentException If the specified {@code Bundle} was
+	 *         not created by the same framework instance that registered this
+	 *         {@code PackageAdmin} service.
+	 * @since 1.2
+	 */
+	public Bundle[] getHosts(Bundle bundle);
+
+	/**
+	 * Returns the bundle from which the specified class is loaded. The class
+	 * loader of the returned bundle must have been used to load the specified
+	 * class. If the class was not loaded by a bundle class loader then
+	 * {@code null} is returned.
+	 * 
+	 * @param clazz The class object from which to locate the bundle.
+	 * @return The bundle from which the specified class is loaded or
+	 *         {@code null} if the class was not loaded by a bundle class
+	 *         loader created by the same framework instance that registered
+	 *         this {@code PackageAdmin} service.
+	 * @since 1.2
+	 */
+	public Bundle getBundle(Class clazz);
+
+	/**
+	 * Bundle type indicating the bundle is a fragment bundle.
+	 * 
+	 * <p>
+	 * The value of {@code BUNDLE_TYPE_FRAGMENT} 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.
+	 * 
+	 * @param bundle The bundle for which to return the special type.
+	 * @return The special type of the bundle.
+	 * @throws IllegalArgumentException If the specified {@code Bundle} was
+	 *         not created by the same framework instance that registered this
+	 *         {@code PackageAdmin} service.
+	 * @since 1.2
+	 */
+	public int getBundleType(Bundle bundle);
+}
diff --git a/src/main/java/org/osgi/service/packageadmin/RequiredBundle.java b/src/main/java/org/osgi/service/packageadmin/RequiredBundle.java
new file mode 100644
index 0000000..34c0b08
--- /dev/null
+++ b/src/main/java/org/osgi/service/packageadmin/RequiredBundle.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) OSGi Alliance (2004, 2010). All Rights Reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.service.packageadmin;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.Version;
+
+/**
+ * A required bundle.
+ * 
+ * Objects implementing this interface are created by the Package Admin service.
+ * 
+ * <p>
+ * The term <i>required bundle</i> refers to a resolved bundle that has a bundle
+ * symbolic name and is not a fragment. That is, a bundle that may be required
+ * by other bundles. This bundle may or may not be currently required by other
+ * bundles.
+ * 
+ * <p>
+ * The information about a required bundle provided by this object may change. A
+ * {@code RequiredBundle} object becomes stale if an exported package of
+ * the bundle it references has been updated or removed as a result of calling
+ * {@code PackageAdmin.refreshPackages()}).
+ * 
+ * If this object becomes stale, its {@code getSymbolicName()} and
+ * {@code getVersion()} methods continue to return their original values,
+ * {@code isRemovalPending()} returns true, and {@code getBundle()}
+ * and {@code getRequiringBundles()} return {@code null}.
+ * 
+ * @since 1.2
+ * @ThreadSafe
+ * @noimplement
+ * @deprecated The PackageAdmin service has been replaced by the
+ *             <code>org.osgi.framework.wiring</code> package.
+ * @version $Id: 1606b0422cae6769b7eedc2d565df61841da1e22 $
+ */
+public interface RequiredBundle {
+	/**
+	 * Returns the symbolic name of this required bundle.
+	 * 
+	 * @return The symbolic name of this required bundle.
+	 */
+	public String getSymbolicName();
+
+	/**
+	 * Returns the bundle associated with this required bundle.
+	 * 
+	 * @return The bundle, or {@code null} if this
+	 *         {@code RequiredBundle} object has become stale.
+	 */
+	public Bundle getBundle();
+
+	/**
+	 * Returns the bundles that currently require this required bundle.
+	 * 
+	 * <p>
+	 * If this required bundle is required and then 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 bundles currently requiring this required bundle, or
+	 *         {@code null} if this {@code RequiredBundle} object
+	 *         has become stale. The array will be empty if no bundles require
+	 *         this required package.
+	 */
+	public Bundle[] getRequiringBundles();
+
+	/**
+	 * Returns the version of this required bundle.
+	 * 
+	 * @return The version of this required bundle, or
+	 *         {@link Version#emptyVersion} if no version information is
+	 *         available.
+	 */
+	public Version getVersion();
+
+	/**
+	 * Returns {@code true} if the bundle associated with this
+	 * {@code RequiredBundle} object has been updated or uninstalled.
+	 * 
+	 * @return {@code true} if the required bundle has been updated or
+	 *         uninstalled, or if the {@code RequiredBundle} object has
+	 *         become stale; {@code false} otherwise.
+	 */
+	public boolean isRemovalPending();
+}
diff --git a/src/main/java/org/osgi/service/startlevel/StartLevel.java b/src/main/java/org/osgi/service/startlevel/StartLevel.java
new file mode 100644
index 0000000..b4b8816
--- /dev/null
+++ b/src/main/java/org/osgi/service/startlevel/StartLevel.java
@@ -0,0 +1,276 @@
+/*
+ * Copyright (c) OSGi Alliance (2002, 2011). All Rights Reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+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}. In addition {@code AdminPermission}
+ * 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 whose autostart setting
+ * indicates the bundle should be started are started as described in the
+ * {@code Bundle.start} method. 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 fire a Framework event of type
+ * {@code FrameworkEvent.STARTED} to announce it has completed its
+ * launch.
+ * 
+ * <p>
+ * Within a start level, bundles may be started in an order defined by the
+ * Framework implementation. This may be something like ascending
+ * {@code Bundle.getBundleId} order or an order based upon dependencies
+ * between bundles. A similar but reversed order may be used when stopping
+ * bundles within a start level.
+ * 
+ * <p>
+ * The StartLevel service can be used by management bundles to alter the active
+ * start level of the framework.
+ * 
+ * @ThreadSafe
+ * @noimplement
+ * @version $Id: ec0295bdf246c0258261374b3ac0e4aef11f7315 $
+ * @deprecated This service has been replaced by the
+ *             <code>org.osgi.framework.startlevel</code> package.
+ */
+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 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.
+	 * 
+	 * 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 whose autostart
+	 * setting indicate they must be started. They are started as described in
+	 * the {@link Bundle#start(int)} method using the
+	 * {@link Bundle#START_TRANSIENT} option. The
+	 * {@link Bundle#START_ACTIVATION_POLICY} option must also be used if
+	 * {@link #isBundleActivationPolicyUsed(Bundle)} returns {@code true}
+	 * for the bundle.
+	 * </ol>
+	 * When this process completes after the specified start level is reached,
+	 * the Framework will fire a Framework event of type
+	 * {@code FrameworkEvent.STARTLEVEL_CHANGED} 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.
+	 * 
+	 * 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 as described in the
+	 * {@link Bundle#stop(int)} method using the {@link Bundle#STOP_TRANSIENT}
+	 * option.
+	 * <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 fire a Framework event of type
+	 * {@code FrameworkEvent.STARTLEVEL_CHANGED} 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 fire a
+	 * Framework event of type {@code FrameworkEvent.STARTLEVEL_CHANGED}
+	 * to announce it has finished moving to the specified start level. This
+	 * event may arrive before 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
+	 *         {@code AdminPermission[System Bundle,STARTLEVEL]} and the
+	 *         Java runtime environment supports permissions.
+	 */
+	public 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.
+	 * @throws java.lang.IllegalArgumentException If the specified bundle has
+	 *         been uninstalled or if the specified bundle was not created by
+	 *         the same framework instance that registered this
+	 *         {@code StartLevel} service.
+	 */
+	public 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.
+	 * <p>
+	 * If the new start level for the bundle is lower than or equal to the
+	 * active start level of the Framework and the bundle's autostart setting
+	 * indicates the bundle must be started, the Framework will start the
+	 * specified bundle as described in the {@link Bundle#start(int)} method
+	 * using the {@link Bundle#START_TRANSIENT} option. The
+	 * {@link Bundle#START_ACTIVATION_POLICY} option must also be used if
+	 * {@link #isBundleActivationPolicyUsed(Bundle)} returns {@code true}
+	 * for the bundle. The actual starting of this bundle must occur
+	 * asynchronously.
+	 * <p>
+	 * 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 {@link Bundle#stop(int)} method using the
+	 * {@link Bundle#STOP_TRANSIENT} option. 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 if the specified bundle is the system bundle,
+	 *         or if the specified bundle was not created by the same framework
+	 *         instance that registered this {@code StartLevel} service.
+	 * @throws SecurityException If the caller does not have
+	 *         {@code AdminPermission[bundle,EXECUTE]} and the Java runtime
+	 *         environment supports permissions.
+	 */
+	public 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(int)
+	 */
+	public 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},
+	 * 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>
+	 * This 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
+	 *         {@code AdminPermission[System Bundle,STARTLEVEL]} and the
+	 *         Java runtime environment supports permissions.
+	 */
+	public void setInitialBundleStartLevel(int startlevel);
+
+	/**
+	 * Returns whether the specified bundle's autostart setting indicates the
+	 * bundle must be started.
+	 * <p>
+	 * The autostart setting of a bundle indicates whether the bundle is to be
+	 * started when its start level is reached.
+	 * 
+	 * @param bundle The bundle whose autostart setting is to be examined.
+	 * @return {@code true} if the autostart setting of the bundle
+	 *         indicates the bundle is to be started. {@code false}
+	 *         otherwise.
+	 * @throws java.lang.IllegalArgumentException If the specified bundle has
+	 *         been uninstalled or if the specified bundle was not created by
+	 *         the same framework instance that registered this
+	 *         {@code StartLevel} service.
+	 * @see Bundle#START_TRANSIENT
+	 */
+	public boolean isBundlePersistentlyStarted(Bundle bundle);
+
+	/**
+	 * Returns whether the specified bundle's autostart setting indicates that
+	 * the activation policy declared in the bundle's manifest must be used.
+	 * <p>
+	 * The autostart setting of a bundle indicates whether the bundle's declared
+	 * activation policy is to be used when the bundle is started.
+	 * 
+	 * @param bundle The bundle whose autostart setting is to be examined.
+	 * @return {@code true} if the bundle's autostart setting indicates the
+	 *         activation policy declared in the manifest must be used.
+	 *         {@code false} if the bundle must be eagerly activated.
+	 * @throws java.lang.IllegalArgumentException If the specified bundle has
+	 *         been uninstalled or if the specified bundle was not created by
+	 *         the same framework instance that registered this
+	 *         {@code StartLevel} service.
+	 * @since 1.1
+	 * @see Bundle#START_ACTIVATION_POLICY
+	 */
+	public boolean isBundleActivationPolicyUsed(Bundle bundle);
+}
diff --git a/src/main/java/org/osgi/service/url/AbstractURLStreamHandlerService.java b/src/main/java/org/osgi/service/url/AbstractURLStreamHandlerService.java
new file mode 100644
index 0000000..679583c
--- /dev/null
+++ b/src/main/java/org/osgi/service/url/AbstractURLStreamHandlerService.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) OSGi Alliance (2002, 2012). All Rights Reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.service.url;
+
+import java.net.*;
+
+/**
+ * Abstract implementation of the {@code URLStreamHandlerService} interface. All
+ * the methods simply invoke the corresponding methods on
+ * {@code java.net.URLStreamHandler} except for {@code parseURL} and
+ * {@code setURL}, which use the {@code URLStreamHandlerSetter} parameter.
+ * Subclasses of this abstract class should not need to override the
+ * {@code setURL} and {@code parseURL(URLStreamHandlerSetter,...)} methods.
+ * 
+ * @ThreadSafe
+ * @version $Id: b86572a4f13b7bb4a343ac4d6b6fb3487e01bd31 $
+ */
+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} object passed to the parseURL method.
+	 */
+	protected volatile URLStreamHandlerSetter	realHandler;
+
+	/**
+	 * Parse a URL using the {@code URLStreamHandlerSetter} object. This method
+	 * sets the {@code realHandler} field with the specified
+	 * {@code URLStreamHandlerSetter} object and then calls
+	 * {@code parseURL(URL,String,int,int)}.
+	 * 
+	 * @param realHandler The object on which the {@code setURL} 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}.
+	 * 
+	 * @see "java.net.URLStreamHandler.toExternalForm"
+	 */
+	public String toExternalForm(URL u) {
+		return super.toExternalForm(u);
+	}
+
+	/**
+	 * This method calls {@code super.equals(URL,URL)}.
+	 * 
+	 * @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}.
+	 * 
+	 * @see "java.net.URLStreamHandler.getDefaultPort"
+	 */
+	public int getDefaultPort() {
+		return super.getDefaultPort();
+	}
+
+	/**
+	 * This method calls {@code super.getHostAddress}.
+	 * 
+	 * @see "java.net.URLStreamHandler.getHostAddress"
+	 */
+	public InetAddress getHostAddress(URL u) {
+		return super.getHostAddress(u);
+	}
+
+	/**
+	 * This method calls {@code super.hashCode(URL)}.
+	 * 
+	 * @see "java.net.URLStreamHandler.hashCode(URL)"
+	 */
+	public int hashCode(URL u) {
+		return super.hashCode(u);
+	}
+
+	/**
+	 * This method calls {@code super.hostsEqual}.
+	 * 
+	 * @see "java.net.URLStreamHandler.hostsEqual"
+	 */
+	public boolean hostsEqual(URL u1, URL u2) {
+		return super.hostsEqual(u1, u2);
+	}
+
+	/**
+	 * This method calls {@code super.sameFile}.
+	 * 
+	 * @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)}.
+	 * 
+	 * @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)}
+	 * .
+	 * 
+	 * @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);
+	}
+}
diff --git a/src/main/java/org/osgi/service/url/URLConstants.java b/src/main/java/org/osgi/service/url/URLConstants.java
new file mode 100644
index 0000000..493f352
--- /dev/null
+++ b/src/main/java/org/osgi/service/url/URLConstants.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) OSGi Alliance (2002, 2012). All Rights Reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.service.url;
+
+/**
+ * Defines standard names for property keys associated with
+ * {@link URLStreamHandlerService} and {@code java.net.ContentHandler} services.
+ * 
+ * <p>
+ * The values associated with these keys are of type {@code java.lang.String[]}
+ * or {@code java.lang.String}, unless otherwise indicated.
+ * 
+ * @noimplement
+ * @version $Id: ac2b9670972d6e41d989c51067219ff7be459831 $
+ */
+public interface URLConstants {
+	/**
+	 * Service property naming the protocols serviced by a
+	 * URLStreamHandlerService. The property's value is a protocol name or 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 a MIME type or an array
+	 * of MIME types.
+	 */
+	public static final String	URL_CONTENT_MIMETYPE	= "url.content.mimetype";
+}
diff --git a/src/main/java/org/osgi/service/url/URLStreamHandlerService.java b/src/main/java/org/osgi/service/url/URLStreamHandlerService.java
new file mode 100644
index 0000000..e6dd098
--- /dev/null
+++ b/src/main/java/org/osgi/service/url/URLStreamHandlerService.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) OSGi Alliance (2002, 2012). All Rights Reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.service.url;
+
+import java.net.*;
+
+/**
+ * Service interface with public versions of the protected
+ * {@code java.net.URLStreamHandler} methods.
+ * <p>
+ * The important differences between this interface and the
+ * {@code URLStreamHandler} class are that the {@code setURL} method is absent
+ * and the {@code parseURL} method takes a {@link URLStreamHandlerSetter} object
+ * as the first argument. Classes implementing this interface must call the
+ * {@code setURL} method on the {@code URLStreamHandlerSetter} object received
+ * in the {@code parseURL} method instead of {@code URLStreamHandler.setURL} to
+ * avoid a {@code SecurityException}.
+ * 
+ * @see AbstractURLStreamHandlerService
+ * 
+ * @ThreadSafe
+ * @version $Id: 4a453f61b9acdc6449df389b2a0538d0ccb33ed2 $
+ */
+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} proxy,
+	 * instead of {@code java.net.URLStreamHandler.parseURL}, passing a
+	 * {@code URLStreamHandlerSetter} object.
+	 * 
+	 * @param realHandler The object on which {@code setURL} 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);
+}
diff --git a/src/main/java/org/osgi/service/url/URLStreamHandlerSetter.java b/src/main/java/org/osgi/service/url/URLStreamHandlerSetter.java
new file mode 100644
index 0000000..90feae6
--- /dev/null
+++ b/src/main/java/org/osgi/service/url/URLStreamHandlerSetter.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) OSGi Alliance (2002, 2012). All Rights Reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.service.url;
+
+import java.net.URL;
+
+/**
+ * Interface used by {@code URLStreamHandlerService} objects to call the
+ * {@code setURL} method on the proxy {@code URLStreamHandler} object.
+ * 
+ * <p>
+ * Objects of this type are passed to the
+ * {@link URLStreamHandlerService#parseURL(URLStreamHandlerSetter, URL, String, int, int)}
+ * method. Invoking the {@code setURL} method on the
+ * {@code URLStreamHandlerSetter} object will invoke the {@code setURL} method
+ * on the proxy {@code URLStreamHandler} object that is actually registered with
+ * {@code java.net.URL} for the protocol.
+ * 
+ * @ThreadSafe
+ * @version $Id: 90f25e3961fea2150cfd31117a2237304f1518f9 $
+ */
+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);
+}
diff --git a/src/main/java/org/osgi/util/tracker/AbstractTracked.java b/src/main/java/org/osgi/util/tracker/AbstractTracked.java
new file mode 100644
index 0000000..f3ddddc
--- /dev/null
+++ b/src/main/java/org/osgi/util/tracker/AbstractTracked.java
@@ -0,0 +1,466 @@
+/*
+ * Copyright (c) OSGi Alliance (2007, 2012). All Rights Reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.util.tracker;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Abstract class to track items. If a Tracker is reused (closed then reopened),
+ * then a new AbstractTracked object is used. This class acts a map of tracked
+ * item -> customized object. Subclasses of this class will act as the listener
+ * object for the tracker. This class is used to synchronize access to the
+ * tracked items. This is not a public class. It is only for use by the
+ * implementation of the Tracker class.
+ * 
+ * @param <S> The tracked item. It is the key.
+ * @param <T> The value mapped to the tracked item.
+ * @param <R> The reason the tracked item is being tracked or untracked.
+ * @ThreadSafe
+ * @version $Id: 16340086b98d308c2d12f13bcd87fc6467a5a367 $
+ * @since 1.4
+ */
+abstract class AbstractTracked<S, T, R> {
+	/* set this to true to compile in debug messages */
+	static final boolean		DEBUG	= false;
+
+	/**
+	 * Map of tracked items to customized objects.
+	 * 
+	 * @GuardedBy this
+	 */
+	private final Map<S, T>		tracked;
+
+	/**
+	 * Modification count. This field is initialized to zero and incremented by
+	 * modified.
+	 * 
+	 * @GuardedBy this
+	 */
+	private int					trackingCount;
+
+	/**
+	 * List of items in the process of being added. This is used to deal with
+	 * nesting of events. Since events may be synchronously delivered, events
+	 * can be nested. For example, when processing the adding of a service and
+	 * the customizer causes the service to be unregistered, notification to the
+	 * nested call to untrack that the service was unregistered can be made to
+	 * the track method.
+	 * 
+	 * Since the ArrayList implementation is not synchronized, all access to
+	 * this list must be protected by the same synchronized object for
+	 * thread-safety.
+	 * 
+	 * @GuardedBy this
+	 */
+	private final List<S>		adding;
+
+	/**
+	 * true if the tracked object is closed.
+	 * 
+	 * This field is volatile because it is set by one thread and read by
+	 * another.
+	 */
+	volatile boolean			closed;
+
+	/**
+	 * Initial list of items for the tracker. This is used to correctly process
+	 * the initial items which could be modified before they are tracked. This
+	 * is necessary since the initial set of tracked items are not "announced"
+	 * by events and therefore the event which makes the item untracked could be
+	 * delivered before we track the item.
+	 * 
+	 * An item must not be in both the initial and adding lists at the same
+	 * time. An item must be moved from the initial list to the adding list
+	 * "atomically" before we begin tracking it.
+	 * 
+	 * Since the LinkedList implementation is not synchronized, all access to
+	 * this list must be protected by the same synchronized object for
+	 * thread-safety.
+	 * 
+	 * @GuardedBy this
+	 */
+	private final LinkedList<S>	initial;
+
+	/**
+	 * AbstractTracked constructor.
+	 */
+	AbstractTracked() {
+		tracked = new HashMap<S, T>();
+		trackingCount = 0;
+		adding = new ArrayList<S>(6);
+		initial = new LinkedList<S>();
+		closed = false;
+	}
+
+	/**
+	 * Set initial list of items into tracker before events begin to be
+	 * received.
+	 * 
+	 * This method must be called from Tracker's open method while synchronized
+	 * on this object in the same synchronized block as the add listener call.
+	 * 
+	 * @param list The initial list of items to be tracked. {@code null} entries
+	 *        in the list are ignored.
+	 * @GuardedBy this
+	 */
+	void setInitial(S[] list) {
+		if (list == null) {
+			return;
+		}
+		for (S item : list) {
+			if (item == null) {
+				continue;
+			}
+			if (DEBUG) {
+				System.out.println("AbstractTracked.setInitial: " + item); //$NON-NLS-1$
+			}
+			initial.add(item);
+		}
+	}
+
+	/**
+	 * Track the initial list of items. This is called after events can begin to
+	 * be received.
+	 * 
+	 * This method must be called from Tracker's open method while not
+	 * synchronized on this object after the add listener call.
+	 * 
+	 */
+	void trackInitial() {
+		while (true) {
+			S item;
+			synchronized (this) {
+				if (closed || (initial.size() == 0)) {
+					/*
+					 * if there are no more initial items
+					 */
+					return; /* we are done */
+				}
+				/*
+				 * move the first item from the initial list to the adding list
+				 * within this synchronized block.
+				 */
+				item = initial.removeFirst();
+				if (tracked.get(item) != null) {
+					/* if we are already tracking this item */
+					if (DEBUG) {
+						System.out.println("AbstractTracked.trackInitial[already tracked]: " + item); //$NON-NLS-1$
+					}
+					continue; /* skip this item */
+				}
+				if (adding.contains(item)) {
+					/*
+					 * if this item is already in the process of being added.
+					 */
+					if (DEBUG) {
+						System.out.println("AbstractTracked.trackInitial[already adding]: " + item); //$NON-NLS-1$
+					}
+					continue; /* skip this item */
+				}
+				adding.add(item);
+			}
+			if (DEBUG) {
+				System.out.println("AbstractTracked.trackInitial: " + item); //$NON-NLS-1$
+			}
+			trackAdding(item, null); /*
+									 * Begin tracking it. We call trackAdding
+									 * since we have already put the item in the
+									 * adding list.
+									 */
+		}
+	}
+
+	/**
+	 * Called by the owning Tracker object when it is closed.
+	 */
+	void close() {
+		closed = true;
+	}
+
+	/**
+	 * Begin to track an item.
+	 * 
+	 * @param item Item to be tracked.
+	 * @param related Action related object.
+	 */
+	void track(final S item, final R related) {
+		final T object;
+		synchronized (this) {
+			if (closed) {
+				return;
+			}
+			object = tracked.get(item);
+			if (object == null) { /* we are not tracking the item */
+				if (adding.contains(item)) {
+					/* if this item is already in the process of being added. */
+					if (DEBUG) {
+						System.out.println("AbstractTracked.track[already adding]: " + item); //$NON-NLS-1$
+					}
+					return;
+				}
+				adding.add(item); /* mark this item is being added */
+			} else { /* we are currently tracking this item */
+				if (DEBUG) {
+					System.out.println("AbstractTracked.track[modified]: " + item); //$NON-NLS-1$
+				}
+				modified(); /* increment modification count */
+			}
+		}
+
+		if (object == null) { /* we are not tracking the item */
+			trackAdding(item, related);
+		} else {
+			/* Call customizer outside of synchronized region */
+			customizerModified(item, related, object);
+			/*
+			 * If the customizer throws an unchecked exception, it is safe to
+			 * let it propagate
+			 */
+		}
+	}
+
+	/**
+	 * Common logic to add an item to the tracker used by track and
+	 * trackInitial. The specified item must have been placed in the adding list
+	 * before calling this method.
+	 * 
+	 * @param item Item to be tracked.
+	 * @param related Action related object.
+	 */
+	private void trackAdding(final S item, final R related) {
+		if (DEBUG) {
+			System.out.println("AbstractTracked.trackAdding: " + item); //$NON-NLS-1$
+		}
+		T object = null;
+		boolean becameUntracked = false;
+		/* Call customizer outside of synchronized region */
+		try {
+			object = customizerAdding(item, related);
+			/*
+			 * If the customizer throws an unchecked exception, it will
+			 * propagate after the finally
+			 */
+		} finally {
+			synchronized (this) {
+				if (adding.remove(item) && !closed) {
+					/*
+					 * if the item was not untracked during the customizer
+					 * callback
+					 */
+					if (object != null) {
+						tracked.put(item, object);
+						modified(); /* increment modification count */
+						notifyAll(); /* notify any waiters */
+					}
+				} else {
+					becameUntracked = true;
+				}
+			}
+		}
+		/*
+		 * The item became untracked during the customizer callback.
+		 */
+		if (becameUntracked && (object != null)) {
+			if (DEBUG) {
+				System.out.println("AbstractTracked.trackAdding[removed]: " + item); //$NON-NLS-1$
+			}
+			/* Call customizer outside of synchronized region */
+			customizerRemoved(item, related, object);
+			/*
+			 * If the customizer throws an unchecked exception, it is safe to
+			 * let it propagate
+			 */
+		}
+	}
+
+	/**
+	 * Discontinue tracking the item.
+	 * 
+	 * @param item Item to be untracked.
+	 * @param related Action related object.
+	 */
+	void untrack(final S item, final R related) {
+		final T object;
+		synchronized (this) {
+			if (initial.remove(item)) { /*
+										 * if this item is already in the list
+										 * of initial references to process
+										 */
+				if (DEBUG) {
+					System.out.println("AbstractTracked.untrack[removed from initial]: " + item); //$NON-NLS-1$
+				}
+				return; /*
+						 * we have removed it from the list and it will not be
+						 * processed
+						 */
+			}
+
+			if (adding.remove(item)) { /*
+										 * if the item is in the process of
+										 * being added
+										 */
+				if (DEBUG) {
+					System.out.println("AbstractTracked.untrack[being added]: " + item); //$NON-NLS-1$
+				}
+				return; /*
+						 * in case the item is untracked while in the process of
+						 * adding
+						 */
+			}
+			object = tracked.remove(item); /*
+											 * must remove from tracker before
+											 * calling customizer callback
+											 */
+			if (object == null) { /* are we actually tracking the item */
+				return;
+			}
+			modified(); /* increment modification count */
+		}
+		if (DEBUG) {
+			System.out.println("AbstractTracked.untrack[removed]: " + item); //$NON-NLS-1$
+		}
+		/* Call customizer outside of synchronized region */
+		customizerRemoved(item, related, object);
+		/*
+		 * If the customizer throws an unchecked exception, it is safe to let it
+		 * propagate
+		 */
+	}
+
+	/**
+	 * Returns the number of tracked items.
+	 * 
+	 * @return The number of tracked items.
+	 * 
+	 * @GuardedBy this
+	 */
+	int size() {
+		return tracked.size();
+	}
+
+	/**
+	 * Returns if the tracker is empty.
+	 * 
+	 * @return Whether the tracker is empty.
+	 * 
+	 * @GuardedBy this
+	 * @since 1.5
+	 */
+	boolean isEmpty() {
+		return tracked.isEmpty();
+	}
+
+	/**
+	 * Return the customized object for the specified item
+	 * 
+	 * @param item The item to lookup in the map
+	 * @return The customized object for the specified item.
+	 * 
+	 * @GuardedBy this
+	 */
+	T getCustomizedObject(final S item) {
+		return tracked.get(item);
+	}
+
+	/**
+	 * Copy the tracked items into an array.
+	 * 
+	 * @param list An array to contain the tracked items.
+	 * @return The specified list if it is large enough to hold the tracked
+	 *         items or a new array large enough to hold the tracked items.
+	 * @GuardedBy this
+	 */
+	S[] copyKeys(final S[] list) {
+		return tracked.keySet().toArray(list);
+	}
+
+	/**
+	 * Increment the modification count. If this method is overridden, the
+	 * overriding method MUST call this method to increment the tracking count.
+	 * 
+	 * @GuardedBy this
+	 */
+	void modified() {
+		trackingCount++;
+	}
+
+	/**
+	 * Returns the tracking count for this {@code ServiceTracker} object.
+	 * 
+	 * The tracking count is initialized to 0 when this object is opened. Every
+	 * time an item is added, modified or removed from this object the tracking
+	 * count is incremented.
+	 * 
+	 * @GuardedBy this
+	 * @return The tracking count for this object.
+	 */
+	int getTrackingCount() {
+		return trackingCount;
+	}
+
+	/**
+	 * Copy the tracked items and associated values into the specified map.
+	 * 
+	 * @param <M> Type of {@code Map} to hold the tracked items and associated
+	 *        values.
+	 * @param map The map into which to copy the tracked items and associated
+	 *        values. This map must not be a user provided map so that user code
+	 *        is not executed while synchronized on this.
+	 * @return The specified map.
+	 * @GuardedBy this
+	 * @since 1.5
+	 */
+	<M extends Map<? super S, ? super T>> M copyEntries(final M map) {
+		map.putAll(tracked);
+		return map;
+	}
+
+	/**
+	 * Call the specific customizer adding method. This method must not be
+	 * called while synchronized on this object.
+	 * 
+	 * @param item Item to be tracked.
+	 * @param related Action related object.
+	 * @return Customized object for the tracked item or {@code null} if the
+	 *         item is not to be tracked.
+	 */
+	abstract T customizerAdding(final S item, final R related);
+
+	/**
+	 * Call the specific customizer modified method. This method must not be
+	 * called while synchronized on this object.
+	 * 
+	 * @param item Tracked item.
+	 * @param related Action related object.
+	 * @param object Customized object for the tracked item.
+	 */
+	abstract void customizerModified(final S item, final R related, final T object);
+
+	/**
+	 * Call the specific customizer removed method. This method must not be
+	 * called while synchronized on this object.
+	 * 
+	 * @param item Tracked item.
+	 * @param related Action related object.
+	 * @param object Customized object for the tracked item.
+	 */
+	abstract void customizerRemoved(final S item, final R related, final T object);
+}
diff --git a/src/main/java/org/osgi/util/tracker/BundleTracker.java b/src/main/java/org/osgi/util/tracker/BundleTracker.java
new file mode 100644
index 0000000..7e1bb53
--- /dev/null
+++ b/src/main/java/org/osgi/util/tracker/BundleTracker.java
@@ -0,0 +1,494 @@
+/*
+ * Copyright (c) OSGi Alliance (2007, 2012). All Rights Reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.util.tracker;
+
+import java.util.HashMap;
+import java.util.Map;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.SynchronousBundleListener;
+
+/**
+ * The {@code BundleTracker} class simplifies tracking bundles much like the
+ * {@code ServiceTracker} simplifies tracking services.
+ * <p>
+ * A {@code BundleTracker} is constructed with state criteria and a
+ * {@code BundleTrackerCustomizer} object. A {@code BundleTracker} can use the
+ * {@code BundleTrackerCustomizer} to select which bundles are tracked and to
+ * create a customized object to be tracked with the bundle. The
+ * {@code BundleTracker} can then be opened to begin tracking all bundles whose
+ * state matches the specified state criteria.
+ * <p>
+ * The {@code getBundles} method can be called to get the {@code Bundle} objects
+ * of the bundles being tracked. The {@code getObject} method can be called to
+ * get the customized object for a tracked bundle.
+ * <p>
+ * The {@code BundleTracker} class is thread-safe. It does not call a
+ * {@code BundleTrackerCustomizer} while holding any locks.
+ * {@code BundleTrackerCustomizer} implementations must also be thread-safe.
+ * 
+ * @param <T> The type of the tracked object.
+ * @ThreadSafe
+ * @version $Id: f21db4fe54284d4810bd9b5fa2528957804e3a21 $
+ * @since 1.4
+ */
+public class BundleTracker<T> implements BundleTrackerCustomizer<T> {
+	/* set this to true to compile in debug messages */
+	static final boolean				DEBUG	= false;
+
+	/**
+	 * The Bundle Context used by this {@code BundleTracker}.
+	 */
+	protected final BundleContext		context;
+
+	/**
+	 * The {@code BundleTrackerCustomizer} object for this tracker.
+	 */
+	final BundleTrackerCustomizer<T>	customizer;
+
+	/**
+	 * Tracked bundles: {@code Bundle} object -> customized Object and
+	 * {@code BundleListener} object
+	 */
+	private volatile Tracked			tracked;
+
+	/**
+	 * Accessor method for the current Tracked object. This method is only
+	 * intended to be used by the unsynchronized methods which do not modify the
+	 * tracked field.
+	 * 
+	 * @return The current Tracked object.
+	 */
+	private Tracked tracked() {
+		return tracked;
+	}
+
+	/**
+	 * State mask for bundles being tracked. This field contains the ORed values
+	 * of the bundle states being tracked.
+	 */
+	final int	mask;
+
+	/**
+	 * Create a {@code BundleTracker} for bundles whose state is present in the
+	 * specified state mask.
+	 * 
+	 * <p>
+	 * Bundles whose state is present on the specified state mask will be
+	 * tracked by this {@code BundleTracker}.
+	 * 
+	 * @param context The {@code BundleContext} against which the tracking is
+	 *        done.
+	 * @param stateMask The bit mask of the {@code OR}ing of the bundle states
+	 *        to be tracked.
+	 * @param customizer The customizer object to call when bundles are added,
+	 *        modified, or removed in this {@code BundleTracker}. If customizer
+	 *        is {@code null}, then this {@code BundleTracker} will be used as
+	 *        the {@code BundleTrackerCustomizer} and this {@code BundleTracker}
+	 *        will call the {@code BundleTrackerCustomizer} methods on itself.
+	 * @see Bundle#getState()
+	 */
+	public BundleTracker(BundleContext context, int stateMask, BundleTrackerCustomizer<T> customizer) {
+		this.context = context;
+		this.mask = stateMask;
+		this.customizer = (customizer == null) ? this : customizer;
+	}
+
+	/**
+	 * Open this {@code BundleTracker} and begin tracking bundles.
+	 * 
+	 * <p>
+	 * Bundle which match the state criteria specified when this
+	 * {@code BundleTracker} was created are now tracked by this
+	 * {@code BundleTracker}.
+	 * 
+	 * @throws java.lang.IllegalStateException If the {@code BundleContext} with
+	 *         which this {@code BundleTracker} was created is no longer valid.
+	 * @throws java.lang.SecurityException If the caller and this class do not
+	 *         have the appropriate
+	 *         {@code AdminPermission[context bundle,LISTENER]}, and the Java
+	 *         Runtime Environment supports permissions.
+	 */
+	public void open() {
+		final Tracked t;
+		synchronized (this) {
+			if (tracked != null) {
+				return;
+			}
+			if (DEBUG) {
+				System.out.println("BundleTracker.open"); //$NON-NLS-1$
+			}
+			t = new Tracked();
+			synchronized (t) {
+				context.addBundleListener(t);
+				Bundle[] bundles = context.getBundles();
+				if (bundles != null) {
+					int length = bundles.length;
+					for (int i = 0; i < length; i++) {
+						int state = bundles[i].getState();
+						if ((state & mask) == 0) {
+							/* null out bundles whose states are not interesting */
+							bundles[i] = null;
+						}
+					}
+					/* set tracked with the initial bundles */
+					t.setInitial(bundles);
+				}
+			}
+			tracked = t;
+		}
+		/* Call tracked outside of synchronized region */
+		t.trackInitial(); /* process the initial references */
+	}
+
+	/**
+	 * Close this {@code BundleTracker}.
+	 * 
+	 * <p>
+	 * This method should be called when this {@code BundleTracker} should end
+	 * the tracking of bundles.
+	 * 
+	 * <p>
+	 * This implementation calls {@link #getBundles()} to get the list of
+	 * tracked bundles to remove.
+	 */
+	public void close() {
+		final Bundle[] bundles;
+		final Tracked outgoing;
+		synchronized (this) {
+			outgoing = tracked;
+			if (outgoing == null) {
+				return;
+			}
+			if (DEBUG) {
+				System.out.println("BundleTracker.close"); //$NON-NLS-1$
+			}
+			outgoing.close();
+			bundles = getBundles();
+			tracked = null;
+			try {
+				context.removeBundleListener(outgoing);
+			} catch (IllegalStateException e) {
+				/* In case the context was stopped. */
+			}
+		}
+		if (bundles != null) {
+			for (int i = 0; i < bundles.length; i++) {
+				outgoing.untrack(bundles[i], null);
+			}
+		}
+	}
+
+	/**
+	 * Default implementation of the
+	 * {@code BundleTrackerCustomizer.addingBundle} method.
+	 * 
+	 * <p>
+	 * This method is only called when this {@code BundleTracker} has been
+	 * constructed with a {@code null BundleTrackerCustomizer} argument.
+	 * 
+	 * <p>
+	 * This implementation simply returns the specified {@code Bundle}.
+	 * 
+	 * <p>
+	 * This method can be overridden in a subclass to customize the object to be
+	 * tracked for the bundle being added.
+	 * 
+	 * @param bundle The {@code Bundle} being added to this
+	 *        {@code BundleTracker} object.
+	 * @param event The bundle event which caused this customizer method to be
+	 *        called or {@code null} if there is no bundle event associated with
+	 *        the call to this method.
+	 * @return The specified bundle.
+	 * @see BundleTrackerCustomizer#addingBundle(Bundle, BundleEvent)
+	 */
+	public T addingBundle(Bundle bundle, BundleEvent event) {
+		T result = (T) bundle;
+		return result;
+	}
+
+	/**
+	 * Default implementation of the
+	 * {@code BundleTrackerCustomizer.modifiedBundle} method.
+	 * 
+	 * <p>
+	 * This method is only called when this {@code BundleTracker} has been
+	 * constructed with a {@code null BundleTrackerCustomizer} argument.
+	 * 
+	 * <p>
+	 * This implementation does nothing.
+	 * 
+	 * @param bundle The {@code Bundle} whose state has been modified.
+	 * @param event The bundle event which caused this customizer method to be
+	 *        called or {@code null} if there is no bundle event associated with
+	 *        the call to this method.
+	 * @param object The customized object for the specified Bundle.
+	 * @see BundleTrackerCustomizer#modifiedBundle(Bundle, BundleEvent, Object)
+	 */
+	public void modifiedBundle(Bundle bundle, BundleEvent event, T object) {
+		/* do nothing */
+	}
+
+	/**
+	 * Default implementation of the
+	 * {@code BundleTrackerCustomizer.removedBundle} method.
+	 * 
+	 * <p>
+	 * This method is only called when this {@code BundleTracker} has been
+	 * constructed with a {@code null BundleTrackerCustomizer} argument.
+	 * 
+	 * <p>
+	 * This implementation does nothing.
+	 * 
+	 * @param bundle The {@code Bundle} being removed.
+	 * @param event The bundle event which caused this customizer method to be
+	 *        called or {@code null} if there is no bundle event associated with
+	 *        the call to this method.
+	 * @param object The customized object for the specified bundle.
+	 * @see BundleTrackerCustomizer#removedBundle(Bundle, BundleEvent, Object)
+	 */
+	public void removedBundle(Bundle bundle, BundleEvent event, T object) {
+		/* do nothing */
+	}
+
+	/**
+	 * Return an array of {@code Bundle}s for all bundles being tracked by this
+	 * {@code BundleTracker}.
+	 * 
+	 * @return An array of {@code Bundle}s or {@code null} if no bundles are
+	 *         being tracked.
+	 */
+	public Bundle[] getBundles() {
+		final Tracked t = tracked();
+		if (t == null) { /* if BundleTracker is not open */
+			return null;
+		}
+		synchronized (t) {
+			int length = t.size();
+			if (length == 0) {
+				return null;
+			}
+			return t.copyKeys(new Bundle[length]);
+		}
+	}
+
+	/**
+	 * Returns the customized object for the specified {@code Bundle} if the
+	 * specified bundle is being tracked by this {@code BundleTracker}.
+	 * 
+	 * @param bundle The {@code Bundle} being tracked.
+	 * @return The customized object for the specified {@code Bundle} or
+	 *         {@code null} if the specified {@code Bundle} is not being
+	 *         tracked.
+	 */
+	public T getObject(Bundle bundle) {
+		final Tracked t = tracked();
+		if (t == null) { /* if BundleTracker is not open */
+			return null;
+		}
+		synchronized (t) {
+			return t.getCustomizedObject(bundle);
+		}
+	}
+
+	/**
+	 * Remove a bundle from this {@code BundleTracker}.
+	 * 
+	 * The specified bundle will be removed from this {@code BundleTracker} . If
+	 * the specified bundle was being tracked then the
+	 * {@code BundleTrackerCustomizer.removedBundle} method will be called for
+	 * that bundle.
+	 * 
+	 * @param bundle The {@code Bundle} to be removed.
+	 */
+	public void remove(Bundle bundle) {
+		final Tracked t = tracked();
+		if (t == null) { /* if BundleTracker is not open */
+			return;
+		}
+		t.untrack(bundle, null);
+	}
+
+	/**
+	 * Return the number of bundles being tracked by this {@code BundleTracker}.
+	 * 
+	 * @return The number of bundles being tracked.
+	 */
+	public int size() {
+		final Tracked t = tracked();
+		if (t == null) { /* if BundleTracker is not open */
+			return 0;
+		}
+		synchronized (t) {
+			return t.size();
+		}
+	}
+
+	/**
+	 * Returns the tracking count for this {@code BundleTracker}.
+	 * 
+	 * The tracking count is initialized to 0 when this {@code BundleTracker} is
+	 * opened. Every time a bundle is added, modified or removed from this
+	 * {@code BundleTracker} the tracking count is incremented.
+	 * 
+	 * <p>
+	 * The tracking count can be used to determine if this {@code BundleTracker}
+	 * has added, modified or removed a bundle by comparing a tracking count
+	 * value previously collected with the current tracking count value. If the
+	 * value has not changed, then no bundle has been added, modified or removed
+	 * from this {@code BundleTracker} since the previous tracking count was
+	 * collected.
+	 * 
+	 * @return The tracking count for this {@code BundleTracker} or -1 if this
+	 *         {@code BundleTracker} is not open.
+	 */
+	public int getTrackingCount() {
+		final Tracked t = tracked();
+		if (t == null) { /* if BundleTracker is not open */
+			return -1;
+		}
+		synchronized (t) {
+			return t.getTrackingCount();
+		}
+	}
+
+	/**
+	 * Return a {@code Map} with the {@code Bundle}s and customized objects for
+	 * all bundles being tracked by this {@code BundleTracker}.
+	 * 
+	 * @return A {@code Map} with the {@code Bundle}s and customized objects for
+	 *         all services being tracked by this {@code BundleTracker}. If no
+	 *         bundles are being tracked, then the returned map is empty.
+	 * @since 1.5
+	 */
+	public Map<Bundle, T> getTracked() {
+		Map<Bundle, T> map = new HashMap<Bundle, T>();
+		final Tracked t = tracked();
+		if (t == null) { /* if BundleTracker is not open */
+			return map;
+		}
+		synchronized (t) {
+			return t.copyEntries(map);
+		}
+	}
+
+	/**
+	 * Return if this {@code BundleTracker} is empty.
+	 * 
+	 * @return {@code true} if this {@code BundleTracker} is not tracking any
+	 *         bundles.
+	 * @since 1.5
+	 */
+	public boolean isEmpty() {
+		final Tracked t = tracked();
+		if (t == null) { /* if BundleTracker is not open */
+			return true;
+		}
+		synchronized (t) {
+			return t.isEmpty();
+		}
+	}
+
+	/**
+	 * Inner class which subclasses AbstractTracked. This class is the
+	 * {@code SynchronousBundleListener} object for the tracker.
+	 * 
+	 * @ThreadSafe
+	 * @since 1.4
+	 */
+	private final class Tracked extends AbstractTracked<Bundle, T, BundleEvent> implements SynchronousBundleListener {
+		/**
+		 * Tracked constructor.
+		 */
+		Tracked() {
+			super();
+		}
+
+		/**
+		 * {@code BundleListener} method for the {@code BundleTracker} class.
+		 * This method must NOT be synchronized to avoid deadlock potential.
+		 * 
+		 * @param event {@code BundleEvent} object from the framework.
+		 */
+		public void bundleChanged(final BundleEvent event) {
+			/*
+			 * Check if we had a delayed call (which could happen when we
+			 * close).
+			 */
+			if (closed) {
+				return;
+			}
+			final Bundle bundle = event.getBundle();
+			final int state = bundle.getState();
+			if (DEBUG) {
+				System.out.println("BundleTracker.Tracked.bundleChanged[" + state + "]: " + bundle); //$NON-NLS-1$ //$NON-NLS-2$
+			}
+
+			if ((state & mask) != 0) {
+				track(bundle, event);
+				/*
+				 * If the customizer throws an unchecked exception, it is safe
+				 * to let it propagate
+				 */
+			} else {
+				untrack(bundle, event);
+				/*
+				 * If the customizer throws an unchecked exception, it is safe
+				 * to let it propagate
+				 */
+			}
+		}
+
+		/**
+		 * Call the specific customizer adding method. This method must not be
+		 * called while synchronized on this object.
+		 * 
+		 * @param item Item to be tracked.
+		 * @param related Action related object.
+		 * @return Customized object for the tracked item or {@code null} if the
+		 *         item is not to be tracked.
+		 */
+		T customizerAdding(final Bundle item, final BundleEvent related) {
+			return customizer.addingBundle(item, related);
+		}
+
+		/**
+		 * Call the specific customizer modified method. This method must not be
+		 * called while synchronized on this object.
+		 * 
+		 * @param item Tracked item.
+		 * @param related Action related object.
+		 * @param object Customized object for the tracked item.
+		 */
+		void customizerModified(final Bundle item, final BundleEvent related, final T object) {
+			customizer.modifiedBundle(item, related, object);
+		}
+
+		/**
+		 * Call the specific customizer removed method. This method must not be
+		 * called while synchronized on this object.
+		 * 
+		 * @param item Tracked item.
+		 * @param related Action related object.
+		 * @param object Customized object for the tracked item.
+		 */
+		void customizerRemoved(final Bundle item, final BundleEvent related, final T object) {
+			customizer.removedBundle(item, related, object);
+		}
+	}
+}
diff --git a/src/main/java/org/osgi/util/tracker/BundleTrackerCustomizer.java b/src/main/java/org/osgi/util/tracker/BundleTrackerCustomizer.java
new file mode 100644
index 0000000..b0bb297
--- /dev/null
+++ b/src/main/java/org/osgi/util/tracker/BundleTrackerCustomizer.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) OSGi Alliance (2007, 2012). All Rights Reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.util.tracker;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleEvent;
+
+/**
+ * The {@code BundleTrackerCustomizer} interface allows a {@code BundleTracker}
+ * to customize the {@code Bundle}s that are tracked. A
+ * {@code BundleTrackerCustomizer} is called when a bundle is being added to a
+ * {@code BundleTracker}. The {@code BundleTrackerCustomizer} can then return an
+ * object for the tracked bundle. A {@code BundleTrackerCustomizer} is also
+ * called when a tracked bundle is modified or has been removed from a
+ * {@code BundleTracker}.
+ * 
+ * <p>
+ * The methods in this interface may be called as the result of a
+ * {@code BundleEvent} being received by a {@code BundleTracker}. Since
+ * {@code BundleEvent}s are received synchronously by the {@code BundleTracker},
+ * it is highly recommended that implementations of these methods do not alter
+ * bundle states while being synchronized on any object.
+ * 
+ * <p>
+ * The {@code BundleTracker} class is thread-safe. It does not call a
+ * {@code BundleTrackerCustomizer} while holding any locks.
+ * {@code BundleTrackerCustomizer} implementations must also be thread-safe.
+ * 
+ * @param <T> The type of the tracked object.
+ * @ThreadSafe
+ * @version $Id: 727e757d2fa2940c88c9b74c8d299de6b3a7d0d0 $
+ * @since 1.4
+ */
+public interface BundleTrackerCustomizer<T> {
+	/**
+	 * A bundle is being added to the {@code BundleTracker}.
+	 * 
+	 * <p>
+	 * This method is called before a bundle which matched the search parameters
+	 * of the {@code BundleTracker} is added to the {@code BundleTracker}. This
+	 * method should return the object to be tracked for the specified
+	 * {@code Bundle}. The returned object is stored in the
+	 * {@code BundleTracker} and is available from the
+	 * {@link BundleTracker#getObject(Bundle) getObject} method.
+	 * 
+	 * @param bundle The {@code Bundle} being added to the {@code BundleTracker}
+	 *        .
+	 * @param event The bundle event which caused this customizer method to be
+	 *        called or {@code null} if there is no bundle event associated with
+	 *        the call to this method.
+	 * @return The object to be tracked for the specified {@code Bundle} object
+	 *         or {@code null} if the specified {@code Bundle} object should not
+	 *         be tracked.
+	 */
+	public T addingBundle(Bundle bundle, BundleEvent event);
+
+	/**
+	 * A bundle tracked by the {@code BundleTracker} has been modified.
+	 * 
+	 * <p>
+	 * This method is called when a bundle being tracked by the
+	 * {@code BundleTracker} has had its state modified.
+	 * 
+	 * @param bundle The {@code Bundle} whose state has been modified.
+	 * @param event The bundle event which caused this customizer method to be
+	 *        called or {@code null} if there is no bundle event associated with
+	 *        the call to this method.
+	 * @param object The tracked object for the specified bundle.
+	 */
+	public void modifiedBundle(Bundle bundle, BundleEvent event, T object);
+
+	/**
+	 * A bundle tracked by the {@code BundleTracker} has been removed.
+	 * 
+	 * <p>
+	 * This method is called after a bundle is no longer being tracked by the
+	 * {@code BundleTracker}.
+	 * 
+	 * @param bundle The {@code Bundle} that has been removed.
+	 * @param event The bundle event which caused this customizer method to be
+	 *        called or {@code null} if there is no bundle event associated with
+	 *        the call to this method.
+	 * @param object The tracked object for the specified bundle.
+	 */
+	public void removedBundle(Bundle bundle, BundleEvent event, T object);
+}
diff --git a/src/main/java/org/osgi/util/tracker/ServiceTracker.java b/src/main/java/org/osgi/util/tracker/ServiceTracker.java
new file mode 100644
index 0000000..0c8022d
--- /dev/null
+++ b/src/main/java/org/osgi/util/tracker/ServiceTracker.java
@@ -0,0 +1,975 @@
+/*
+ * Copyright (c) OSGi Alliance (2000, 2012). All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.util.tracker;
+
+import java.lang.reflect.Array;
+import java.util.Collections;
+import java.util.SortedMap;
+import java.util.TreeMap;
+import org.osgi.framework.AllServiceListener;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Filter;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * The {@code ServiceTracker} class simplifies using services from the
+ * Framework's service registry.
+ * <p>
+ * A {@code ServiceTracker} object is constructed with search criteria and a
+ * {@code ServiceTrackerCustomizer} object. A {@code ServiceTracker} can use a
+ * {@code ServiceTrackerCustomizer} to customize the service objects to be
+ * tracked. The {@code ServiceTracker} can then be opened to begin tracking all
+ * services in the Framework's service registry that match the specified search
+ * criteria. The {@code ServiceTracker} correctly handles all of the details of
+ * listening to {@code ServiceEvent}s and getting and ungetting services.
+ * <p>
+ * The {@code getServiceReferences} method can be called to get references to
+ * the services being tracked. The {@code getService} and {@code getServices}
+ * methods can be called to get the service objects for the tracked service.
+ * <p>
+ * The {@code ServiceTracker} class is thread-safe. It does not call a
+ * {@code ServiceTrackerCustomizer} while holding any locks.
+ * {@code ServiceTrackerCustomizer} implementations must also be thread-safe.
+ * 
+ * @param <S> The type of the service being tracked.
+ * @param <T> The type of the tracked object.
+ * @ThreadSafe
+ * @version $Id: 21926ad8717a91633face6bbf570febfcd23b1c7 $
+ */
+public class ServiceTracker<S, T> implements ServiceTrackerCustomizer<S, T> {
+	/* set this to true to compile in debug messages */
+	static final boolean					DEBUG	= false;
+	/**
+	 * The Bundle Context used by this {@code ServiceTracker}.
+	 */
+	protected final BundleContext			context;
+	/**
+	 * The Filter used by this {@code ServiceTracker} which specifies the search
+	 * criteria for the services to track.
+	 * 
+	 * @since 1.1
+	 */
+	protected final Filter					filter;
+	/**
+	 * The {@code ServiceTrackerCustomizer} for this tracker.
+	 */
+	final ServiceTrackerCustomizer<S, T>	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.
+	 */
+	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<S>		trackReference;
+	/**
+	 * Tracked services: {@code ServiceReference} -> customized Object and
+	 * {@code ServiceListener} object
+	 */
+	private volatile Tracked				tracked;
+
+	/**
+	 * Accessor method for the current Tracked object. This method is only
+	 * intended to be used by the unsynchronized methods which do not modify the
+	 * tracked field.
+	 * 
+	 * @return The current Tracked object.
+	 */
+	private Tracked tracked() {
+		return tracked;
+	}
+
+	/**
+	 * Cached ServiceReference for getServiceReference.
+	 * 
+	 * This field is volatile since it is accessed by multiple threads.
+	 */
+	private volatile ServiceReference<S>	cachedReference;
+	/**
+	 * Cached service object for getService.
+	 * 
+	 * This field is volatile since it is accessed by multiple threads.
+	 */
+	private volatile T						cachedService;
+
+	/**
+	 * Create a {@code ServiceTracker} on the specified {@code ServiceReference}
+	 * .
+	 * 
+	 * <p>
+	 * The service referenced by the specified {@code ServiceReference} will be
+	 * tracked by this {@code ServiceTracker}.
+	 * 
+	 * @param context The {@code BundleContext} against which the tracking is
+	 *        done.
+	 * @param reference The {@code ServiceReference} for the service to be
+	 *        tracked.
+	 * @param customizer The customizer object to call when services are added,
+	 *        modified, or removed in this {@code ServiceTracker}. If customizer
+	 *        is {@code null}, then this {@code ServiceTracker} will be used as
+	 *        the {@code ServiceTrackerCustomizer} and this
+	 *        {@code ServiceTracker} will call the
+	 *        {@code ServiceTrackerCustomizer} methods on itself.
+	 */
+	public ServiceTracker(final BundleContext context, final ServiceReference<S> reference, final ServiceTrackerCustomizer<S, T> 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() + ")";
+		try {
+			this.filter = context.createFilter(listenerFilter);
+		} catch (InvalidSyntaxException e) {
+			/*
+			 * we could only get this exception if the ServiceReference was
+			 * invalid
+			 */
+			IllegalArgumentException iae = new IllegalArgumentException("unexpected InvalidSyntaxException: " + e.getMessage());
+			iae.initCause(e);
+			throw iae;
+		}
+	}
+
+	/**
+	 * Create a {@code ServiceTracker} on the specified class name.
+	 * 
+	 * <p>
+	 * Services registered under the specified class name will be tracked by
+	 * this {@code ServiceTracker}.
+	 * 
+	 * @param context The {@code BundleContext} against which the tracking is
+	 *        done.
+	 * @param clazz The 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}. If customizer
+	 *        is {@code null}, then this {@code ServiceTracker} will be used as
+	 *        the {@code ServiceTrackerCustomizer} and this
+	 *        {@code ServiceTracker} will call the
+	 *        {@code ServiceTrackerCustomizer} methods on itself.
+	 */
+	public ServiceTracker(final BundleContext context, final String clazz, final ServiceTrackerCustomizer<S, T> customizer) {
+		this.context = context;
+		this.trackReference = null;
+		this.trackClass = clazz;
+		this.customizer = (customizer == null) ? this : customizer;
+		// we call clazz.toString to verify clazz is non-null!
+		this.listenerFilter = "(" + Constants.OBJECTCLASS + "=" + clazz.toString() + ")";
+		try {
+			this.filter = context.createFilter(listenerFilter);
+		} catch (InvalidSyntaxException e) {
+			/*
+			 * we could only get this exception if the clazz argument was
+			 * malformed
+			 */
+			IllegalArgumentException iae = new IllegalArgumentException("unexpected InvalidSyntaxException: " + e.getMessage());
+			iae.initCause(e);
+			throw iae;
+		}
+	}
+
+	/**
+	 * Create a {@code ServiceTracker} on the specified {@code Filter} object.
+	 * 
+	 * <p>
+	 * Services which match the specified {@code Filter} object will be tracked
+	 * by this {@code ServiceTracker}.
+	 * 
+	 * @param context The {@code BundleContext} against which the tracking is
+	 *        done.
+	 * @param filter The {@code Filter} 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}. If customizer
+	 *        is null, then this {@code ServiceTracker} will be used as the
+	 *        {@code ServiceTrackerCustomizer} and this {@code ServiceTracker}
+	 *        will call the {@code ServiceTrackerCustomizer} methods on itself.
+	 * @since 1.1
+	 */
+	public ServiceTracker(final BundleContext context, final Filter filter, final ServiceTrackerCustomizer<S, T> customizer) {
+		this.context = context;
+		this.trackReference = null;
+		this.trackClass = null;
+		this.listenerFilter = filter.toString();
+		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();
+		}
+	}
+
+	/**
+	 * Create a {@code ServiceTracker} on the specified class.
+	 * 
+	 * <p>
+	 * Services registered under the name of the specified class will be tracked
+	 * by this {@code ServiceTracker}.
+	 * 
+	 * @param context The {@code BundleContext} against which the tracking is
+	 *        done.
+	 * @param clazz The class of the services to be tracked.
+	 * @param customizer The customizer object to call when services are added,
+	 *        modified, or removed in this {@code ServiceTracker}. If customizer
+	 *        is {@code null}, then this {@code ServiceTracker} will be used as
+	 *        the {@code ServiceTrackerCustomizer} and this
+	 *        {@code ServiceTracker} will call the
+	 *        {@code ServiceTrackerCustomizer} methods on itself.
+	 * @since 1.5
+	 */
+	public ServiceTracker(final BundleContext context, final Class<S> clazz, final ServiceTrackerCustomizer<S, T> customizer) {
+		this(context, clazz.getName(), customizer);
+	}
+
+	/**
+	 * Open this {@code ServiceTracker} and begin tracking services.
+	 * 
+	 * <p>
+	 * This implementation calls {@code open(false)}.
+	 * 
+	 * @throws java.lang.IllegalStateException If the {@code BundleContext} with
+	 *         which this {@code ServiceTracker} was created is no longer valid.
+	 * @see #open(boolean)
+	 */
+	public void open() {
+		open(false);
+	}
+
+	/**
+	 * Open this {@code ServiceTracker} and begin tracking services.
+	 * 
+	 * <p>
+	 * Services which match the search criteria specified when this
+	 * {@code ServiceTracker} was created are now tracked by this
+	 * {@code ServiceTracker}.
+	 * 
+	 * @param trackAllServices If {@code true}, then this {@code ServiceTracker}
+	 *        will track all matching services regardless of class loader
+	 *        accessibility. If {@code false}, then this {@code ServiceTracker}
+	 *        will only track matching services which are class loader
+	 *        accessible to the bundle whose {@code BundleContext} is used by
+	 *        this {@code ServiceTracker}.
+	 * @throws java.lang.IllegalStateException If the {@code BundleContext} with
+	 *         which this {@code ServiceTracker} was created is no longer valid.
+	 * @since 1.3
+	 */
+	public void open(boolean trackAllServices) {
+		final Tracked t;
+		synchronized (this) {
+			if (tracked != null) {
+				return;
+			}
+			if (DEBUG) {
+				System.out.println("ServiceTracker.open: " + filter);
+			}
+			t = trackAllServices ? new AllTracked() : new Tracked();
+			synchronized (t) {
+				try {
+					context.addServiceListener(t, listenerFilter);
+					ServiceReference<S>[] references = null;
+					if (trackClass != null) {
+						references = getInitialReferences(trackAllServices, trackClass, null);
+					} else {
+						if (trackReference != null) {
+							if (trackReference.getBundle() != null) {
+								ServiceReference<S>[] single = new ServiceReference[] {trackReference};
+								references = single;
+							}
+						} else { /* user supplied filter */
+							references = getInitialReferences(trackAllServices, null, listenerFilter);
+						}
+					}
+					/* set tracked with the initial references */
+					t.setInitial(references);
+				} catch (InvalidSyntaxException e) {
+					throw new RuntimeException("unexpected InvalidSyntaxException: " + e.getMessage(), e);
+				}
+			}
+			tracked = t;
+		}
+		/* Call tracked outside of synchronized region */
+		t.trackInitial(); /* process the initial references */
+	}
+
+	/**
+	 * Returns the list of initial {@code ServiceReference}s that will be
+	 * tracked by this {@code ServiceTracker}.
+	 * 
+	 * @param trackAllServices If {@code true}, use
+	 *        {@code getAllServiceReferences}.
+	 * @param className The class name with which the service was registered, or
+	 *        {@code null} for all services.
+	 * @param filterString The filter criteria or {@code null} for all services.
+	 * @return The list of initial {@code ServiceReference}s.
+	 * @throws InvalidSyntaxException If the specified filterString has an
+	 *         invalid syntax.
+	 */
+	private ServiceReference<S>[] getInitialReferences(boolean trackAllServices, String className, String filterString) throws InvalidSyntaxException {
+		ServiceReference<S>[] result = (ServiceReference<S>[]) ((trackAllServices) ? context.getAllServiceReferences(className, filterString) : context.getServiceReferences(className, filterString));
+		return result;
+	}
+
+	/**
+	 * Close this {@code ServiceTracker}.
+	 * 
+	 * <p>
+	 * This method should be called when this {@code ServiceTracker} should end
+	 * the tracking of services.
+	 * 
+	 * <p>
+	 * This implementation calls {@link #getServiceReferences()} to get the list
+	 * of tracked services to remove.
+	 */
+	public void close() {
+		final Tracked outgoing;
+		final ServiceReference<S>[] references;
+		synchronized (this) {
+			outgoing = tracked;
+			if (outgoing == null) {
+				return;
+			}
+			if (DEBUG) {
+				System.out.println("ServiceTracker.close: " + filter);
+			}
+			outgoing.close();
+			references = getServiceReferences();
+			tracked = null;
+			try {
+				context.removeServiceListener(outgoing);
+			} catch (IllegalStateException e) {
+				/* In case the context was stopped. */
+			}
+		}
+		modified(); /* clear the cache */
+		synchronized (outgoing) {
+			outgoing.notifyAll(); /* wake up any waiters */
+		}
+		if (references != null) {
+			for (int i = 0; i < references.length; i++) {
+				outgoing.untrack(references[i], null);
+			}
+		}
+		if (DEBUG) {
+			if ((cachedReference == null) && (cachedService == null)) {
+				System.out.println("ServiceTracker.close[cached cleared]: " + filter);
+			}
+		}
+	}
+
+	/**
+	 * Default implementation of the
+	 * {@code ServiceTrackerCustomizer.addingService} method.
+	 * 
+	 * <p>
+	 * This method is only called when this {@code ServiceTracker} has been
+	 * constructed with a {@code null ServiceTrackerCustomizer} argument.
+	 * 
+	 * <p>
+	 * This implementation returns the result of calling {@code getService} on
+	 * the {@code BundleContext} with which this {@code ServiceTracker} was
+	 * created passing the specified {@code ServiceReference}.
+	 * <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
+	 * {@link #removedService(ServiceReference, Object) removedService} to unget
+	 * the service.
+	 * 
+	 * @param reference The reference to the service being added to this
+	 *        {@code ServiceTracker}.
+	 * @return The service object to be tracked for the service added to this
+	 *         {@code ServiceTracker}.
+	 * @see ServiceTrackerCustomizer#addingService(ServiceReference)
+	 */
+	public T addingService(ServiceReference<S> reference) {
+		T result = (T) context.getService(reference);
+		return result;
+	}
+
+	/**
+	 * Default implementation of the
+	 * {@code ServiceTrackerCustomizer.modifiedService} method.
+	 * 
+	 * <p>
+	 * This method is only called when this {@code ServiceTracker} has been
+	 * constructed with a {@code null ServiceTrackerCustomizer} argument.
+	 * 
+	 * <p>
+	 * This implementation does nothing.
+	 * 
+	 * @param reference The reference to modified service.
+	 * @param service The service object for the modified service.
+	 * @see ServiceTrackerCustomizer#modifiedService(ServiceReference, Object)
+	 */
+	public void modifiedService(ServiceReference<S> reference, T service) {
+		/* do nothing */
+	}
+
+	/**
+	 * Default implementation of the
+	 * {@code ServiceTrackerCustomizer.removedService} method.
+	 * 
+	 * <p>
+	 * This method is only called when this {@code ServiceTracker} has been
+	 * constructed with a {@code null ServiceTrackerCustomizer} argument.
+	 * 
+	 * <p>
+	 * This implementation calls {@code ungetService}, on the
+	 * {@code BundleContext} with which this {@code ServiceTracker} was created,
+	 * passing the specified {@code ServiceReference}.
+	 * <p>
+	 * This method can be overridden in a subclass. If the default
+	 * implementation of {@link #addingService(ServiceReference) addingService}
+	 * method was used, this method must unget the service.
+	 * 
+	 * @param reference The reference to removed service.
+	 * @param service The service object for the removed service.
+	 * @see ServiceTrackerCustomizer#removedService(ServiceReference, Object)
+	 */
+	public void removedService(ServiceReference<S> reference, T service) {
+		context.ungetService(reference);
+	}
+
+	/**
+	 * Wait for at least one service to be tracked by this
+	 * {@code ServiceTracker}. This method will also return when this
+	 * {@code ServiceTracker} is closed.
+	 * 
+	 * <p>
+	 * It is strongly recommended that {@code waitForService} is not used during
+	 * the calling of the {@code BundleActivator} methods.
+	 * {@code BundleActivator} methods are expected to complete in a short
+	 * period of time.
+	 * 
+	 * <p>
+	 * This implementation calls {@link #getService()} to determine if a service
+	 * is being tracked.
+	 * 
+	 * @param timeout The time interval in milliseconds to wait. If zero, the
+	 *        method will wait indefinitely.
+	 * @return Returns the result of {@link #getService()}.
+	 * @throws InterruptedException If another thread has interrupted the
+	 *         current thread.
+	 * @throws IllegalArgumentException If the value of timeout is negative.
+	 */
+	public T waitForService(long timeout) throws InterruptedException {
+		if (timeout < 0) {
+			throw new IllegalArgumentException("timeout value is negative");
+		}
+
+		T object = getService();
+		if (object != null) {
+			return object;
+		}
+
+		final long endTime = (timeout == 0) ? 0 : (System.currentTimeMillis() + timeout);
+		do {
+			final Tracked t = tracked();
+			if (t == null) { /* if ServiceTracker is not open */
+				return null;
+			}
+			synchronized (t) {
+				if (t.size() == 0) {
+					t.wait(timeout);
+				}
+			}
+			object = getService();
+			if (endTime > 0) { // if we have a timeout
+				timeout = endTime - System.currentTimeMillis();
+				if (timeout <= 0) { // that has expired
+					break;
+				}
+			}
+		} while (object == null);
+		return object;
+	}
+
+	/**
+	 * Return an array of {@code ServiceReference}s for all services being
+	 * tracked by this {@code ServiceTracker}.
+	 * 
+	 * @return Array of {@code ServiceReference}s or {@code null} if no services
+	 *         are being tracked.
+	 */
+	public ServiceReference<S>[] getServiceReferences() {
+		final Tracked t = tracked();
+		if (t == null) { /* if ServiceTracker is not open */
+			return null;
+		}
+		synchronized (t) {
+			int length = t.size();
+			if (length == 0) {
+				return null;
+			}
+			ServiceReference<S>[] result = new ServiceReference[length];
+			return t.copyKeys(result);
+		}
+	}
+
+	/**
+	 * Returns a {@code ServiceReference} for one of the services being tracked
+	 * by this {@code ServiceTracker}.
+	 * 
+	 * <p>
+	 * If multiple services are being tracked, the service with the highest
+	 * ranking (as specified in its {@code service.ranking} property) is
+	 * returned. If there is a tie in ranking, the service with the lowest
+	 * service ID (as specified in its {@code service.id} property); that is,
+	 * the service that was registered first is returned. This is the same
+	 * algorithm used by {@code BundleContext.getServiceReference}.
+	 * 
+	 * <p>
+	 * This implementation calls {@link #getServiceReferences()} to get the list
+	 * of references for the tracked services.
+	 * 
+	 * @return A {@code ServiceReference} or {@code null} if no services are
+	 *         being tracked.
+	 * @since 1.1
+	 */
+	public ServiceReference<S> getServiceReference() {
+		ServiceReference<S> reference = cachedReference;
+		if (reference != null) {
+			if (DEBUG) {
+				System.out.println("ServiceTracker.getServiceReference[cached]: " + filter);
+			}
+			return reference;
+		}
+		if (DEBUG) {
+			System.out.println("ServiceTracker.getServiceReference: " + filter);
+		}
+		ServiceReference<S>[] 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} if
+	 * the specified referenced service is being tracked by this
+	 * {@code ServiceTracker}.
+	 * 
+	 * @param reference The reference to the desired service.
+	 * @return A service object or {@code null} if the service referenced by the
+	 *         specified {@code ServiceReference} is not being tracked.
+	 */
+	public T getService(ServiceReference<S> reference) {
+		final Tracked t = tracked();
+		if (t == null) { /* if ServiceTracker is not open */
+			return null;
+		}
+		synchronized (t) {
+			return t.getCustomizedObject(reference);
+		}
+	}
+
+	/**
+	 * Return an array of service objects for all services being tracked by this
+	 * {@code ServiceTracker}.
+	 * 
+	 * <p>
+	 * This implementation calls {@link #getServiceReferences()} to get the list
+	 * of references for the tracked services and then calls
+	 * {@link #getService(ServiceReference)} for each reference to get the
+	 * tracked service object.
+	 * 
+	 * @return An array of service objects or {@code null} if no services are
+	 *         being tracked.
+	 */
+	public Object[] getServices() {
+		final Tracked t = tracked();
+		if (t == null) { /* if ServiceTracker is not open */
+			return null;
+		}
+		synchronized (t) {
+			ServiceReference<S>[] 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}.
+	 * 
+	 * <p>
+	 * If any services are being tracked, this implementation returns the result
+	 * of calling {@code getService(getServiceReference())}.
+	 * 
+	 * @return A service object or {@code null} if no services are being
+	 *         tracked.
+	 */
+	public T getService() {
+		T service = cachedService;
+		if (service != null) {
+			if (DEBUG) {
+				System.out.println("ServiceTracker.getService[cached]: " + filter);
+			}
+			return service;
+		}
+		if (DEBUG) {
+			System.out.println("ServiceTracker.getService: " + filter);
+		}
+		ServiceReference<S> reference = getServiceReference();
+		if (reference == null) {
+			return null;
+		}
+		return cachedService = getService(reference);
+	}
+
+	/**
+	 * Remove a service from this {@code ServiceTracker}.
+	 * 
+	 * The specified service will be removed from this {@code ServiceTracker}.
+	 * If the specified service was being tracked then the
+	 * {@code ServiceTrackerCustomizer.removedService} method will be called for
+	 * that service.
+	 * 
+	 * @param reference The reference to the service to be removed.
+	 */
+	public void remove(ServiceReference<S> reference) {
+		final Tracked t = tracked();
+		if (t == null) { /* if ServiceTracker is not open */
+			return;
+		}
+		t.untrack(reference, null);
+	}
+
+	/**
+	 * Return the number of services being tracked by this
+	 * {@code ServiceTracker}.
+	 * 
+	 * @return The number of services being tracked.
+	 */
+	public int size() {
+		final Tracked t = tracked();
+		if (t == null) { /* if ServiceTracker is not open */
+			return 0;
+		}
+		synchronized (t) {
+			return t.size();
+		}
+	}
+
+	/**
+	 * Returns the tracking count for this {@code ServiceTracker}.
+	 * 
+	 * The tracking count is initialized to 0 when this {@code ServiceTracker}
+	 * is opened. Every time a service is added, modified or removed from this
+	 * {@code ServiceTracker}, the tracking count is incremented.
+	 * 
+	 * <p>
+	 * The tracking count can be used to determine if this
+	 * {@code ServiceTracker} has added, modified 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, modified or removed from this {@code ServiceTracker} since
+	 * the previous tracking count was collected.
+	 * 
+	 * @since 1.2
+	 * @return The tracking count for this {@code ServiceTracker} or -1 if this
+	 *         {@code ServiceTracker} is not open.
+	 */
+	public int getTrackingCount() {
+		final Tracked t = tracked();
+		if (t == null) { /* if ServiceTracker is not open */
+			return -1;
+		}
+		synchronized (t) {
+			return t.getTrackingCount();
+		}
+	}
+
+	/**
+	 * Called by the Tracked object whenever the set of tracked services is
+	 * modified. 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 listener thread and the user thread.
+	 */
+	void modified() {
+		cachedReference = null; /* clear cached value */
+		cachedService = null; /* clear cached value */
+		if (DEBUG) {
+			System.out.println("ServiceTracker.modified: " + filter);
+		}
+	}
+
+	/**
+	 * Return a {@code SortedMap} of the {@code ServiceReference}s and service
+	 * objects for all services being tracked by this {@code ServiceTracker}.
+	 * The map is sorted in reverse natural order of {@code ServiceReference}.
+	 * That is, the first entry is the service with the highest ranking and the
+	 * lowest service id.
+	 * 
+	 * @return A {@code SortedMap} with the {@code ServiceReference}s and
+	 *         service objects for all services being tracked by this
+	 *         {@code ServiceTracker}. If no services are being tracked, then
+	 *         the returned map is empty.
+	 * @since 1.5
+	 */
+	public SortedMap<ServiceReference<S>, T> getTracked() {
+		SortedMap<ServiceReference<S>, T> map = new TreeMap<ServiceReference<S>, T>(Collections.reverseOrder());
+		final Tracked t = tracked();
+		if (t == null) { /* if ServiceTracker is not open */
+			return map;
+		}
+		synchronized (t) {
+			return t.copyEntries(map);
+		}
+	}
+
+	/**
+	 * Return if this {@code ServiceTracker} is empty.
+	 * 
+	 * @return {@code true} if this {@code ServiceTracker} is not tracking any
+	 *         services.
+	 * @since 1.5
+	 */
+	public boolean isEmpty() {
+		final Tracked t = tracked();
+		if (t == null) { /* if ServiceTracker is not open */
+			return true;
+		}
+		synchronized (t) {
+			return t.isEmpty();
+		}
+	}
+
+	/**
+	 * Return an array of service objects for all services being tracked by this
+	 * {@code ServiceTracker}. The runtime type of the returned array is that of
+	 * the specified array.
+	 * 
+	 * <p>
+	 * This implementation calls {@link #getServiceReferences()} to get the list
+	 * of references for the tracked services and then calls
+	 * {@link #getService(ServiceReference)} for each reference to get the
+	 * tracked service object.
+	 * 
+	 * @param array An array into which the tracked service objects will be
+	 *        stored, if the array is large enough.
+	 * @return An array of service objects being tracked. If the specified array
+	 *         is large enough to hold the result, then the specified array is
+	 *         returned. If the specified array is longer then necessary to hold
+	 *         the result, the array element after the last service object is
+	 *         set to {@code null}. If the specified array is not large enough
+	 *         to hold the result, a new array is created and returned.
+	 * @since 1.5
+	 */
+	public T[] getServices(T[] array) {
+		final Tracked t = tracked();
+		if (t == null) { /* if ServiceTracker is not open */
+			if (array.length > 0) {
+				array[0] = null;
+			}
+			return array;
+		}
+		synchronized (t) {
+			ServiceReference<S>[] references = getServiceReferences();
+			int length = (references == null) ? 0 : references.length;
+			if (length == 0) {
+				if (array.length > 0) {
+					array[0] = null;
+				}
+				return array;
+			}
+			if (length > array.length) {
+				array = (T[]) Array.newInstance(array.getClass().getComponentType(), length);
+			}
+			for (int i = 0; i < length; i++) {
+				array[i] = getService(references[i]);
+			}
+			if (array.length > length) {
+				array[length] = null;
+			}
+			return array;
+		}
+	}
+
+	/**
+	 * Inner class which subclasses AbstractTracked. This class is the
+	 * {@code ServiceListener} object for the tracker.
+	 * 
+	 * @ThreadSafe
+	 */
+	private class Tracked extends AbstractTracked<ServiceReference<S>, T, ServiceEvent> implements ServiceListener {
+		/**
+		 * Tracked constructor.
+		 */
+		Tracked() {
+			super();
+		}
+
+		/**
+		 * {@code ServiceListener} method for the {@code ServiceTracker} class.
+		 * This method must NOT be synchronized to avoid deadlock potential.
+		 * 
+		 * @param event {@code ServiceEvent} object from the framework.
+		 */
+		final public void serviceChanged(final ServiceEvent event) {
+			/*
+			 * Check if we had a delayed call (which could happen when we
+			 * close).
+			 */
+			if (closed) {
+				return;
+			}
+			final ServiceReference<S> reference = (ServiceReference<S>) event.getServiceReference();
+			if (DEBUG) {
+				System.out.println("ServiceTracker.Tracked.serviceChanged[" + event.getType() + "]: " + reference);
+			}
+
+			switch (event.getType()) {
+				case ServiceEvent.REGISTERED :
+				case ServiceEvent.MODIFIED :
+					track(reference, event);
+					/*
+					 * If the customizer throws an unchecked exception, it is
+					 * safe to let it propagate
+					 */
+					break;
+				case ServiceEvent.MODIFIED_ENDMATCH :
+				case ServiceEvent.UNREGISTERING :
+					untrack(reference, event);
+					/*
+					 * If the customizer throws an unchecked exception, it is
+					 * safe to let it propagate
+					 */
+					break;
+			}
+		}
+
+		/**
+		 * Increment the tracking count and tell the tracker there was a
+		 * modification.
+		 * 
+		 * @GuardedBy this
+		 */
+		final void modified() {
+			super.modified(); /* increment the modification count */
+			ServiceTracker.this.modified();
+		}
+
+		/**
+		 * Call the specific customizer adding method. This method must not be
+		 * called while synchronized on this object.
+		 * 
+		 * @param item Item to be tracked.
+		 * @param related Action related object.
+		 * @return Customized object for the tracked item or {@code null} if the
+		 *         item is not to be tracked.
+		 */
+		final T customizerAdding(final ServiceReference<S> item, final ServiceEvent related) {
+			return customizer.addingService(item);
+		}
+
+		/**
+		 * Call the specific customizer modified method. This method must not be
+		 * called while synchronized on this object.
+		 * 
+		 * @param item Tracked item.
+		 * @param related Action related object.
+		 * @param object Customized object for the tracked item.
+		 */
+		final void customizerModified(final ServiceReference<S> item, final ServiceEvent related, final T object) {
+			customizer.modifiedService(item, object);
+		}
+
+		/**
+		 * Call the specific customizer removed method. This method must not be
+		 * called while synchronized on this object.
+		 * 
+		 * @param item Tracked item.
+		 * @param related Action related object.
+		 * @param object Customized object for the tracked item.
+		 */
+		final void customizerRemoved(final ServiceReference<S> item, final ServiceEvent related, final T object) {
+			customizer.removedService(item, object);
+		}
+	}
+
+	/**
+	 * Subclass of Tracked which implements the AllServiceListener interface.
+	 * This class is used by the ServiceTracker if open is called with true.
+	 * 
+	 * @since 1.3
+	 * @ThreadSafe
+	 */
+	private class AllTracked extends Tracked implements AllServiceListener {
+		/**
+		 * AllTracked constructor.
+		 */
+		AllTracked() {
+			super();
+		}
+	}
+}
diff --git a/src/main/java/org/osgi/util/tracker/ServiceTrackerCustomizer.java b/src/main/java/org/osgi/util/tracker/ServiceTrackerCustomizer.java
new file mode 100644
index 0000000..72bec7a
--- /dev/null
+++ b/src/main/java/org/osgi/util/tracker/ServiceTrackerCustomizer.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) OSGi Alliance (2000, 2012). All Rights Reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.util.tracker;
+
+import org.osgi.framework.ServiceReference;
+
+/**
+ * The {@code ServiceTrackerCustomizer} interface allows a
+ * {@code ServiceTracker} to customize the service objects that are tracked. A
+ * {@code ServiceTrackerCustomizer} is called when a service is being added to a
+ * {@code ServiceTracker}. The {@code ServiceTrackerCustomizer} can then return
+ * an object for the tracked service. A {@code ServiceTrackerCustomizer} is also
+ * called when a tracked service is modified or has been removed from a
+ * {@code ServiceTracker}.
+ * 
+ * <p>
+ * The methods in this interface may be called as the result of a
+ * {@code ServiceEvent} being received by a {@code ServiceTracker}. Since
+ * {@code ServiceEvent}s are synchronously delivered by the Framework, it is
+ * highly recommended that implementations of these methods do not register (
+ * {@code BundleContext.registerService}), modify (
+ * {@code ServiceRegistration.setProperties}) or unregister (
+ * {@code ServiceRegistration.unregister}) a service while being synchronized on
+ * any object.
+ * 
+ * <p>
+ * The {@code ServiceTracker} class is thread-safe. It does not call a
+ * {@code ServiceTrackerCustomizer} while holding any locks.
+ * {@code ServiceTrackerCustomizer} implementations must also be thread-safe.
+ * 
+ * @param <S> The type of the service being tracked.
+ * @param <T> The type of the tracked object.
+ * @ThreadSafe
+ * @version $Id: c14b8d47026b6bd4ba1f2db7bf7e755d00fc6f6a $
+ */
+public interface ServiceTrackerCustomizer<S, T> {
+	/**
+	 * A service is being added to the {@code ServiceTracker}.
+	 * 
+	 * <p>
+	 * This method is called before a service which matched the search
+	 * parameters of the {@code ServiceTracker} is added to the
+	 * {@code ServiceTracker}. This method should return the service object to
+	 * be tracked for the specified {@code ServiceReference}. The returned
+	 * service object is stored in the {@code ServiceTracker} and is available
+	 * from the {@code getService} and {@code getServices} methods.
+	 * 
+	 * @param reference The reference to the service being added to the
+	 *        {@code ServiceTracker}.
+	 * @return The service object to be tracked for the specified referenced
+	 *         service or {@code null} if the specified referenced service
+	 *         should not be tracked.
+	 */
+	public T addingService(ServiceReference<S> reference);
+
+	/**
+	 * A service tracked by the {@code ServiceTracker} has been modified.
+	 * 
+	 * <p>
+	 * This method is called when a service being tracked by the
+	 * {@code ServiceTracker} has had it properties modified.
+	 * 
+	 * @param reference The reference to the service that has been modified.
+	 * @param service The service object for the specified referenced service.
+	 */
+	public void modifiedService(ServiceReference<S> reference, T service);
+
+	/**
+	 * A service tracked by the {@code ServiceTracker} has been removed.
+	 * 
+	 * <p>
+	 * This method is called after a service is no longer being tracked by the
+	 * {@code ServiceTracker}.
+	 * 
+	 * @param reference The reference to the service that has been removed.
+	 * @param service The service object for the specified referenced service.
+	 */
+	public void removedService(ServiceReference<S> reference, T service);
+}
diff --git a/src/main/resources/META-INF/services/org.osgi.framework.launch.FrameworkFactory b/src/main/resources/META-INF/services/org.osgi.framework.launch.FrameworkFactory
new file mode 100644
index 0000000..425043b
--- /dev/null
+++ b/src/main/resources/META-INF/services/org.osgi.framework.launch.FrameworkFactory
@@ -0,0 +1 @@
+org.apache.felix.framework.FrameworkFactory
diff --git a/src/main/resources/default.properties b/src/main/resources/default.properties
new file mode 100644
index 0000000..dd2f6ad
--- /dev/null
+++ b/src/main/resources/default.properties
@@ -0,0 +1,794 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+#
+# Framework config properties.
+#
+
+# New-style generic execution environment capabilities.
+org.osgi.framework.system.capabilities= \
+ ${dollar}{eecap-${dollar}{java.specification.version}}
+
+eecap-1.8= osgi.ee; osgi.ee="OSGi/Minimum"; version:List<Version>="1.0,1.1,1.2", \
+ osgi.ee; osgi.ee="JavaSE"; version:List<Version>="1.0,1.1,1.2,1.3,1.4,1.5,1.6,1.7,1.8"
+eecap-1.7= osgi.ee; osgi.ee="OSGi/Minimum"; version:List<Version>="1.0,1.1,1.2", \
+ osgi.ee; osgi.ee="JavaSE"; version:List<Version>="1.0,1.1,1.2,1.3,1.4,1.5,1.6,1.7"
+eecap-1.6= osgi.ee; osgi.ee="OSGi/Minimum"; version:List<Version>="1.0,1.1,1.2", \
+ osgi.ee; osgi.ee="JavaSE"; version:List<Version>="1.0,1.1,1.2,1.3,1.4,1.5,1.6"
+eecap-1.5= osgi.ee; osgi.ee="OSGi/Minimum"; version:List<Version>="1.0,1.1,1.2", \
+ osgi.ee; osgi.ee="JavaSE"; version:List<Version>="1.0,1.1,1.2,1.3,1.4,1.5"
+eecap-1.4= osgi.ee; osgi.ee="OSGi/Minimum"; version:List<Version>="1.0,1.1,1.2", \
+ osgi.ee; osgi.ee="JavaSE"; version:List<Version>="1.0,1.1,1.2,1.3,1.4"
+eecap-1.3= osgi.ee; osgi.ee="OSGi/Minimum"; version:List<Version>="1.0,1.1", \
+ osgi.ee; osgi.ee="JavaSE"; version:List<Version>="1.0,1.1,1.2,1.3"
+eecap-1.2= osgi.ee; osgi.ee="OSGi/Minimum"; version:List<Version>="1.0,1.1", \
+ osgi.ee; osgi.ee="JavaSE"; version:List<Version>="1.0,1.1,1.2"
+
+# Deprecated old-style execution environment properties.
+org.osgi.framework.executionenvironment= \
+ ${dollar}{ee-${dollar}{java.specification.version}}
+
+ee-1.8=JavaSE-1.8,JavaSE-1.7,JavaSE-1.6,J2SE-1.5,J2SE-1.4,J2SE-1.3, \
+ J2SE-1.2,JRE-1.1,JRE-1.0,OSGi/Minimum-1.2,OSGi/Minimum-1.1, \
+ OSGi/Minimum-1.0
+ee-1.7=JavaSE-1.7,JavaSE-1.6,J2SE-1.5,J2SE-1.4,J2SE-1.3, \
+ J2SE-1.2,JRE-1.1,JRE-1.0,OSGi/Minimum-1.2,OSGi/Minimum-1.1, \
+ OSGi/Minimum-1.0
+ee-1.6=JavaSE-1.6,J2SE-1.5,J2SE-1.4,J2SE-1.3,J2SE-1.2, \
+ JRE-1.1,JRE-1.0,OSGi/Minimum-1.2,OSGi/Minimum-1.1,OSGi/Minimum-1.0
+ee-1.5=J2SE-1.5,J2SE-1.4,J2SE-1.3,J2SE-1.2,JRE-1.1,JRE-1.0, \
+ OSGi/Minimum-1.2,OSGi/Minimum-1.1,OSGi/Minimum-1.0
+ee-1.4=J2SE-1.4,J2SE-1.3,J2SE-1.2,JRE-1.1,JRE-1.0,OSGi/Minimum-1.2, \
+ OSGi/Minimum-1.1,OSGi/Minimum-1.0
+ee-1.3=J2SE-1.3,J2SE-1.2,JRE-1.1,JRE-1.0,OSGi/Minimum-1.1,OSGi/Minimum-1.0
+ee-1.2=J2SE-1.2,JRE-1.1,JRE-1.0,OSGi/Minimum-1.1,OSGi/Minimum-1.0
+
+# Default packages exported by system bundle.
+org.osgi.framework.system.packages=org.osgi.framework; version=1.7.0, \
+ org.osgi.framework.hooks.bundle; version=1.1.0, \
+ org.osgi.framework.hooks.resolver; version=1.0.0, \
+ org.osgi.framework.hooks.service; version=1.1.0, \
+ org.osgi.framework.hooks.weaving; version=1.0.0, \
+ org.osgi.framework.launch; version=1.1.0, \
+ org.osgi.framework.namespace; version=1.0.0, \
+ org.osgi.framework.startlevel; version=1.0.0, \
+ org.osgi.framework.wiring; version=1.1.0, \
+ org.osgi.resource; version=1.0.0, \
+ org.osgi.service.packageadmin; version=1.2.0, \
+ org.osgi.service.startlevel; version=1.1.0, \
+ org.osgi.service.url; version=1.0.0, \
+ org.osgi.util.tracker; version=1.5.1 \
+ ${dollar}{jre-${dollar}{java.specification.version}}
+
+#
+# Java platform package export properties.
+#
+jre-1.3=, \
+ javax.accessibility;uses:="javax.swing.text";version="0.0.0.1_003_J2SE", \
+ javax.naming.directory;uses:="javax.naming";version="0.0.0.1_003_J2SE", \
+ javax.naming.event;uses:="javax.naming,javax.naming.directory";version="0.0.0.1_003_J2SE", \
+ javax.naming.ldap;uses:="javax.naming,javax.naming.directory,javax.naming.event";version="0.0.0.1_003_J2SE", \
+ javax.naming.spi;uses:="javax.naming,javax.naming.directory";version="0.0.0.1_003_J2SE", \
+ javax.naming;uses:="javax.naming.spi";version="0.0.0.1_003_J2SE", \
+ javax.rmi.CORBA;uses:="org.omg.CORBA,org.omg.CORBA_2_3.portable,org.omg.CORBA.portable,org.omg.SendingContext";version="0.0.0.1_003_J2SE", \
+ javax.rmi;uses:="org.omg.CORBA,javax.rmi.CORBA";version="0.0.0.1_003_J2SE", \
+ javax.sound.midi.spi;uses:="javax.sound.midi";version="0.0.0.1_003_J2SE", \
+ javax.sound.midi;uses:="javax.sound.midi.spi";version="0.0.0.1_003_J2SE", \
+ javax.sound.sampled.spi;uses:="javax.sound.sampled";version="0.0.0.1_003_J2SE", \
+ javax.sound.sampled;uses:="javax.sound.sampled.spi";version="0.0.0.1_003_J2SE", \
+ javax.swing.border;uses:="javax.swing";version="0.0.0.1_003_J2SE", \
+ javax.swing.colorchooser;uses:="javax.swing.event,javax.swing,javax.swing.text,javax.swing.border,javax.accessibility";version="0.0.0.1_003_J2SE", \
+ javax.swing.event;uses:="javax.swing,javax.swing.text,javax.swing.table,javax.swing.tree,javax.swing.undo";version="0.0.0.1_003_J2SE", \
+ javax.swing.filechooser;uses:="javax.swing";version="0.0.0.1_003_J2SE", \
+ javax.swing.plaf.basic;uses:="javax.swing,javax.swing.border,javax.swing.plaf,javax.swing.text,javax.swing.event,javax.swing.colorchooser,javax.accessibility,javax.swing.filechooser,javax.swing.text.html,javax.swing.table,javax.swing.tree";version="0.0.0.1_003_J2SE", \
+ javax.swing.plaf.metal;uses:="javax.swing.plaf,javax.swing.border,javax.swing,javax.swing.text,javax.swing.plaf.basic,javax.swing.event,javax.swing.filechooser,javax.accessibility,javax.swing.tree";version="0.0.0.1_003_J2SE", \
+ javax.swing.plaf.multi;uses:="javax.accessibility,javax.swing,javax.swing.plaf,javax.swing.filechooser,javax.swing.text,javax.swing.tree";version="0.0.0.1_003_J2SE", \
+ javax.swing.plaf;uses:="javax.swing,javax.swing.border,javax.accessibility,javax.swing.filechooser,javax.swing.text,javax.swing.tree";version="0.0.0.1_003_J2SE", \
+ javax.swing.table;uses:="javax.swing.event,javax.swing.plaf,javax.swing.border,javax.swing,javax.accessibility";version="0.0.0.1_003_J2SE", \
+ javax.swing.text.html.parser;uses:="javax.swing.text.html,javax.swing.text";version="0.0.0.1_003_J2SE", \
+ javax.swing.text.html;uses:="javax.swing.text,javax.swing,javax.swing.event,javax.swing.border,javax.swing.undo,javax.swing.plaf,javax.swing.text.html.parser";version="0.0.0.1_003_J2SE", \
+ javax.swing.text.rtf;uses:="javax.swing.text";version="0.0.0.1_003_J2SE", \
+ javax.swing.text;uses:="javax.swing.event,javax.swing.tree,javax.swing.undo,javax.swing,javax.swing.plaf,javax.swing.plaf.basic,javax.accessibility,javax.swing.text.html";version="0.0.0.1_003_J2SE", \
+ javax.swing.tree;uses:="javax.swing.event,javax.swing,javax.swing.border,javax.swing.plaf";version="0.0.0.1_003_J2SE", \
+ javax.swing.undo;uses:="javax.swing,javax.swing.event";version="0.0.0.1_003_J2SE", \
+ javax.swing;uses:="javax.swing.event,javax.swing.text,javax.accessibility,javax.swing.plaf,javax.swing.border,javax.swing.tree,javax.swing.table,javax.swing.colorchooser,javax.swing.plaf.basic,javax.swing.text.html,javax.swing.filechooser";version="0.0.0.1_003_J2SE", \
+ javax.transaction;version="0.0.0.1_003_J2SE", \
+ org.omg.CORBA.DynAnyPackage;uses:="org.omg.CORBA";version="0.0.0.1_003_J2SE", \
+ org.omg.CORBA.ORBPackage;uses:="org.omg.CORBA";version="0.0.0.1_003_J2SE", \
+ org.omg.CORBA.TypeCodePackage;uses:="org.omg.CORBA";version="0.0.0.1_003_J2SE", \
+ org.omg.CORBA.portable;uses:="org.omg.CORBA,org.omg.CORBA_2_3.portable";version="0.0.0.1_003_J2SE", \
+ org.omg.CORBA;uses:="org.omg.CORBA.portable,org.omg.CORBA.DynAnyPackage,org.omg.CORBA.ORBPackage,org.omg.CORBA_2_3.portable,org.omg.CORBA.TypeCodePackage";version="0.0.0.1_003_J2SE", \
+ org.omg.CORBA_2_3.portable;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_003_J2SE", \
+ org.omg.CORBA_2_3;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_003_J2SE", \
+ org.omg.CosNaming.NamingContextPackage;uses:="org.omg.CORBA,org.omg.CORBA.portable,org.omg.CosNaming";version="0.0.0.1_003_J2SE", \
+ org.omg.CosNaming;uses:="org.omg.CORBA.portable,org.omg.CORBA,org.omg.CosNaming.NamingContextPackage";version="0.0.0.1_003_J2SE", \
+ org.omg.SendingContext;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_003_J2SE", \
+ org.omg.stub.java.rmi;uses:="javax.rmi.CORBA";version="0.0.0.1_003_J2SE"
+
+jre-1.4=, \
+ javax.accessibility;uses:="javax.swing.text";version="0.0.0.1_004_J2SE", \
+ javax.crypto.interfaces;uses:="javax.crypto.spec,javax.crypto";version="0.0.0.1_004_J2SE", \
+ javax.crypto.spec;uses:="javax.crypto";version="0.0.0.1_004_J2SE", \
+ javax.crypto;uses:="javax.crypto.spec";version="0.0.0.1_004_J2SE", \
+ javax.imageio.event;uses:="javax.imageio";version="0.0.0.1_004_J2SE", \
+ javax.imageio.metadata;uses:="org.w3c.dom,javax.imageio,org.apache.crimson.util";version="0.0.0.1_004_J2SE", \
+ javax.imageio.plugins.jpeg;uses:="javax.imageio";version="0.0.0.1_004_J2SE", \
+ javax.imageio.spi;uses:="javax.imageio.stream,javax.imageio,javax.imageio.metadata";version="0.0.0.1_004_J2SE", \
+ javax.imageio.stream;uses:="javax.imageio";version="0.0.0.1_004_J2SE", \
+ javax.imageio;uses:="javax.imageio.metadata,javax.imageio.stream,javax.imageio.spi,javax.imageio.event";version="0.0.0.1_004_J2SE", \
+ javax.naming.directory;uses:="javax.naming";version="0.0.0.1_004_J2SE", \
+ javax.naming.event;uses:="javax.naming,javax.naming.directory";version="0.0.0.1_004_J2SE", \
+ javax.naming.ldap;uses:="javax.naming,javax.naming.directory,javax.net.ssl,javax.naming.event";version="0.0.0.1_004_J2SE", \
+ javax.naming.spi;uses:="javax.naming,javax.naming.directory";version="0.0.0.1_004_J2SE", \
+ javax.naming;uses:="javax.naming.spi";version="0.0.0.1_004_J2SE", \
+ javax.net.ssl;uses:="javax.security.cert,javax.net";version="0.0.0.1_004_J2SE", \
+ javax.net;version="0.0.0.1_004_J2SE", \
+ javax.print.attribute.standard;uses:="javax.print.attribute";version="0.0.0.1_004_J2SE", \
+ javax.print.attribute;version="0.0.0.1_004_J2SE", \
+ javax.print.event;uses:="javax.print,javax.print.attribute";version="0.0.0.1_004_J2SE", \
+ javax.print;uses:="javax.print.attribute,javax.print.event,javax.print.attribute.standard";version="0.0.0.1_004_J2SE", \
+ javax.rmi.CORBA;uses:="org.omg.CORBA,org.omg.CORBA_2_3.portable,org.omg.CORBA.portable,org.omg.SendingContext";version="0.0.0.1_004_J2SE", \
+ javax.rmi;uses:="org.omg.CORBA,javax.rmi.CORBA";version="0.0.0.1_004_J2SE", \
+ javax.security.auth.callback;version="0.0.0.1_004_J2SE", \
+ javax.security.auth.kerberos;uses:="javax.security.auth,javax.crypto";version="0.0.0.1_004_J2SE", \
+ javax.security.auth.login;uses:="javax.security.auth,javax.security.auth.callback";version="0.0.0.1_004_J2SE", \
+ javax.security.auth.spi;uses:="javax.security.auth.callback,javax.security.auth.login,javax.security.auth";version="0.0.0.1_004_J2SE", \
+ javax.security.auth.x500;uses:="javax.security.auth";version="0.0.0.1_004_J2SE", \
+ javax.security.auth;version="0.0.0.1_004_J2SE", \
+ javax.security.cert;version="0.0.0.1_004_J2SE", \
+ javax.sound.midi.spi;uses:="javax.sound.midi";version="0.0.0.1_004_J2SE", \
+ javax.sound.midi;uses:="javax.sound.midi.spi";version="0.0.0.1_004_J2SE", \
+ javax.sound.sampled.spi;uses:="javax.sound.sampled";version="0.0.0.1_004_J2SE", \
+ javax.sound.sampled;uses:="javax.sound.sampled.spi";version="0.0.0.1_004_J2SE", \
+ javax.sql;uses:="javax.transaction.xa";version="0.0.0.1_004_J2SE", \
+ javax.swing.border;uses:="javax.swing";version="0.0.0.1_004_J2SE", \
+ javax.swing.colorchooser;uses:="javax.swing.event,javax.swing,javax.swing.border,javax.accessibility";version="0.0.0.1_004_J2SE", \
+ javax.swing.event;uses:="javax.swing,javax.swing.text,javax.swing.table,javax.swing.tree,javax.swing.undo";version="0.0.0.1_004_J2SE", \
+ javax.swing.filechooser;uses:="javax.swing";version="0.0.0.1_004_J2SE", \
+ javax.swing.plaf.basic;uses:="javax.swing.border,javax.swing,javax.swing.plaf,javax.swing.text,javax.swing.event,javax.swing.colorchooser,javax.accessibility,javax.swing.filechooser,javax.swing.text.html,javax.sound.sampled,javax.swing.table,javax.swing.tree";version="0.0.0.1_004_J2SE", \
+ javax.swing.plaf.metal;uses:="javax.swing.plaf,javax.swing,javax.swing.border,javax.swing.text,javax.swing.plaf.basic,javax.swing.filechooser,javax.swing.event,javax.swing.table,javax.accessibility,javax.swing.tree";version="0.0.0.1_004_J2SE", \
+ javax.swing.plaf.multi;uses:="javax.accessibility,javax.swing,javax.swing.plaf,javax.swing.filechooser,javax.swing.text,javax.swing.tree";version="0.0.0.1_004_J2SE", \
+ javax.swing.plaf;uses:="javax.swing,javax.swing.border,javax.accessibility,javax.swing.filechooser,javax.swing.text,javax.swing.tree";version="0.0.0.1_004_J2SE", \
+ javax.swing.table;uses:="javax.swing.event,javax.swing.plaf,javax.swing.border,javax.swing,javax.accessibility";version="0.0.0.1_004_J2SE", \
+ javax.swing.text.html.parser;uses:="javax.swing.text,javax.swing.text.html";version="0.0.0.1_004_J2SE", \
+ javax.swing.text.html;uses:="javax.swing.event,javax.swing.text,javax.accessibility,javax.swing,javax.swing.plaf,javax.swing.border,javax.swing.undo,javax.swing.text.html.parser";version="0.0.0.1_004_J2SE", \
+ javax.swing.text.rtf;uses:="javax.swing.text";version="0.0.0.1_004_J2SE", \
+ javax.swing.text;uses:="javax.swing.event,javax.swing.tree,javax.swing.undo,javax.swing,javax.swing.plaf,javax.swing.plaf.basic,javax.accessibility,javax.swing.text.html";version="0.0.0.1_004_J2SE", \
+ javax.swing.tree;uses:="javax.swing.event,javax.swing,javax.swing.border,javax.swing.plaf";version="0.0.0.1_004_J2SE", \
+ javax.swing.undo;uses:="javax.swing,javax.swing.event";version="0.0.0.1_004_J2SE", \
+ javax.swing;uses:="javax.swing.event,javax.accessibility,javax.swing.text,javax.swing.plaf,javax.swing.border,javax.swing.tree,javax.swing.table,javax.swing.colorchooser,javax.swing.plaf.basic,javax.swing.text.html,javax.swing.filechooser";version="0.0.0.1_004_J2SE", \
+ javax.transaction.xa;version="0.0.0.1_004_J2SE", \
+ javax.transaction;version="0.0.0.1_004_J2SE", \
+ javax.xml.parsers;uses:="org.w3c.dom,org.xml.sax,org.xml.sax.helpers";version="0.0.0.1_004_J2SE", \
+ javax.xml.transform.dom;uses:="javax.xml.transform,org.w3c.dom";version="0.0.0.1_004_J2SE", \
+ javax.xml.transform.sax;uses:="org.xml.sax.ext,javax.xml.transform,org.xml.sax,javax.xml.transform.stream";version="0.0.0.1_004_J2SE", \
+ javax.xml.transform.stream;uses:="javax.xml.transform";version="0.0.0.1_004_J2SE", \
+ javax.xml.transform;version="0.0.0.1_004_J2SE", \
+ org.ietf.jgss;version="0.0.0.1_004_J2SE", \
+ org.omg.CORBA.DynAnyPackage;uses:="org.omg.CORBA";version="0.0.0.1_004_J2SE", \
+ org.omg.CORBA.ORBPackage;uses:="org.omg.CORBA";version="0.0.0.1_004_J2SE", \
+ org.omg.CORBA.TypeCodePackage;uses:="org.omg.CORBA";version="0.0.0.1_004_J2SE", \
+ org.omg.CORBA.portable;uses:="org.omg.CORBA,org.omg.CORBA_2_3.portable";version="0.0.0.1_004_J2SE", \
+ org.omg.CORBA;uses:="org.omg.CORBA.portable,org.omg.CORBA.DynAnyPackage,org.omg.CORBA.ORBPackage,org.omg.CORBA_2_3.portable,org.omg.CORBA.TypeCodePackage";version="0.0.0.1_004_J2SE", \
+ org.omg.CORBA_2_3.portable;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_004_J2SE", \
+ org.omg.CORBA_2_3;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_004_J2SE", \
+ org.omg.CosNaming.NamingContextExtPackage;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_004_J2SE", \
+ org.omg.CosNaming.NamingContextPackage;uses:="org.omg.CORBA,org.omg.CORBA.portable,org.omg.CosNaming";version="0.0.0.1_004_J2SE", \
+ org.omg.CosNaming;uses:="org.omg.CORBA.portable,org.omg.CORBA,org.omg.PortableServer,org.omg.CosNaming.NamingContextPackage,org.omg.CosNaming.NamingContextExtPackage";version="0.0.0.1_004_J2SE", \
+ org.omg.Dynamic;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_004_J2SE", \
+ org.omg.DynamicAny.DynAnyFactoryPackage;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_004_J2SE", \
+ org.omg.DynamicAny.DynAnyPackage;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_004_J2SE", \
+ org.omg.DynamicAny;uses:="org.omg.CORBA,org.omg.CORBA.portable,org.omg.DynamicAny.DynAnyFactoryPackage,org.omg.DynamicAny.DynAnyPackage";version="0.0.0.1_004_J2SE", \
+ org.omg.IOP.CodecFactoryPackage;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_004_J2SE", \
+ org.omg.IOP.CodecPackage;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_004_J2SE", \
+ org.omg.IOP;uses:="org.omg.CORBA,org.omg.CORBA.portable,org.omg.IOP.CodecFactoryPackage,org.omg.IOP.CodecPackage";version="0.0.0.1_004_J2SE", \
+ org.omg.Messaging;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_004_J2SE", \
+ org.omg.PortableInterceptor.ORBInitInfoPackage;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_004_J2SE", \
+ org.omg.PortableInterceptor;uses:="org.omg.CORBA.portable,org.omg.CORBA,org.omg.IOP,org.omg.PortableInterceptor.ORBInitInfoPackage,org.omg.Dynamic";version="0.0.0.1_004_J2SE", \
+ org.omg.PortableServer.CurrentPackage;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_004_J2SE", \
+ org.omg.PortableServer.POAManagerPackage;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_004_J2SE", \
+ org.omg.PortableServer.POAPackage;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_004_J2SE", \
+ org.omg.PortableServer.ServantLocatorPackage;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_004_J2SE", \
+ org.omg.PortableServer.portable;uses:="org.omg.CORBA,org.omg.PortableServer";version="0.0.0.1_004_J2SE", \
+ org.omg.PortableServer;uses:="org.omg.CORBA,org.omg.CORBA.portable,org.omg.PortableServer.CurrentPackage,org.omg.PortableServer.POAManagerPackage,org.omg.PortableServer.POAPackage,org.omg.PortableServer.portable,org.omg.CORBA_2_3,org.omg.PortableServer.ServantLocatorPackage";version="0.0.0.1_004_J2SE", \
+ org.omg.SendingContext;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_004_J2SE", \
+ org.omg.stub.java.rmi;uses:="javax.rmi.CORBA";version="0.0.0.1_004_J2SE", \
+ org.w3c.dom;version="0.0.0.1_004_J2SE", \
+ org.xml.sax.ext;uses:="org.xml.sax";version="0.0.0.1_004_J2SE", \
+ org.xml.sax.helpers;uses:="org.xml.sax";version="0.0.0.1_004_J2SE", \
+ org.xml.sax;version="0.0.0.1_004_J2SE"
+
+jre-1.5=, \
+ javax.accessibility;uses:="javax.swing.text";version="0.0.0.1_005_J2SE", \
+ javax.activity;version="0.0.0.1_005_J2SE", \
+ javax.crypto.interfaces;uses:="javax.crypto.spec,javax.crypto";version="0.0.0.1_005_J2SE", \
+ javax.crypto.spec;uses:="javax.crypto";version="0.0.0.1_005_J2SE", \
+ javax.crypto;uses:="javax.crypto.spec";version="0.0.0.1_005_J2SE", \
+ javax.imageio.event;uses:="javax.imageio";version="0.0.0.1_005_J2SE", \
+ javax.imageio.metadata;uses:="org.w3c.dom,javax.imageio";version="0.0.0.1_005_J2SE", \
+ javax.imageio.plugins.bmp;uses:="javax.imageio";version="0.0.0.1_005_J2SE", \
+ javax.imageio.plugins.jpeg;uses:="javax.imageio";version="0.0.0.1_005_J2SE", \
+ javax.imageio.spi;uses:="javax.imageio.stream,javax.imageio,javax.imageio.metadata";version="0.0.0.1_005_J2SE", \
+ javax.imageio.stream;uses:="javax.imageio";version="0.0.0.1_005_J2SE", \
+ javax.imageio;uses:="javax.imageio.metadata,javax.imageio.stream,javax.imageio.spi,javax.imageio.event";version="0.0.0.1_005_J2SE", \
+ javax.management.loading;uses:="javax.management";version="0.0.0.1_005_J2SE", \
+ javax.management.modelmbean;uses:="javax.management,javax.management.loading";version="0.0.0.1_005_J2SE", \
+ javax.management.monitor;uses:="javax.management";version="0.0.0.1_005_J2SE", \
+ javax.management.openmbean;uses:="javax.management";version="0.0.0.1_005_J2SE", \
+ javax.management.relation;uses:="javax.management";version="0.0.0.1_005_J2SE", \
+ javax.management.remote.rmi;uses:="javax.management.remote,javax.security.auth,javax.management,javax.naming,org.omg.CORBA,org.omg.CORBA.portable,javax.rmi,javax.rmi.CORBA";version="0.0.0.1_005_J2SE", \
+ javax.management.remote;uses:="javax.security.auth,javax.management";version="0.0.0.1_005_J2SE", \
+ javax.management.timer;uses:="javax.management";version="0.0.0.1_005_J2SE", \
+ javax.management;uses:="javax.management.loading";version="0.0.0.1_005_J2SE", \
+ javax.naming.directory;uses:="javax.naming";version="0.0.0.1_005_J2SE", \
+ javax.naming.event;uses:="javax.naming,javax.naming.directory";version="0.0.0.1_005_J2SE", \
+ javax.naming.ldap;uses:="javax.naming,javax.naming.directory,javax.net.ssl,javax.naming.event";version="0.0.0.1_005_J2SE", \
+ javax.naming.spi;uses:="javax.naming,javax.naming.directory";version="0.0.0.1_005_J2SE", \
+ javax.naming;uses:="javax.naming.spi";version="0.0.0.1_005_J2SE", \
+ javax.net.ssl;uses:="javax.security.cert,javax.security.auth.x500,javax.net";version="0.0.0.1_005_J2SE", \
+ javax.net;version="0.0.0.1_005_J2SE", \
+ javax.print.attribute.standard;uses:="javax.print.attribute";version="0.0.0.1_005_J2SE", \
+ javax.print.attribute;version="0.0.0.1_005_J2SE", \
+ javax.print.event;uses:="javax.print,javax.print.attribute";version="0.0.0.1_005_J2SE", \
+ javax.print;uses:="javax.print.attribute,javax.print.event,javax.print.attribute.standard";version="0.0.0.1_005_J2SE", \
+ javax.rmi.CORBA;uses:="org.omg.CORBA,org.omg.CORBA_2_3.portable,org.omg.CORBA.portable,org.omg.SendingContext";version="0.0.0.1_005_J2SE", \
+ javax.rmi.ssl;uses:="javax.net,javax.net.ssl";version="0.0.0.1_005_J2SE", \
+ javax.rmi;uses:="org.omg.CORBA,javax.rmi.CORBA";version="0.0.0.1_005_J2SE", \
+ javax.security.auth.callback;version="0.0.0.1_005_J2SE", \
+ javax.security.auth.kerberos;uses:="javax.security.auth,javax.crypto";version="0.0.0.1_005_J2SE", \
+ javax.security.auth.login;uses:="javax.security.auth,javax.security.auth.callback";version="0.0.0.1_005_J2SE", \
+ javax.security.auth.spi;uses:="javax.security.auth.callback,javax.security.auth.login,javax.security.auth";version="0.0.0.1_005_J2SE", \
+ javax.security.auth.x500;uses:="javax.security.auth";version="0.0.0.1_005_J2SE", \
+ javax.security.auth;version="0.0.0.1_005_J2SE", \
+ javax.security.cert;version="0.0.0.1_005_J2SE", \
+ javax.security.sasl;uses:="javax.security.auth.callback";version="0.0.0.1_005_J2SE", \
+ javax.sound.midi.spi;uses:="javax.sound.midi";version="0.0.0.1_005_J2SE", \
+ javax.sound.midi;uses:="javax.sound.midi.spi";version="0.0.0.1_005_J2SE", \
+ javax.sound.sampled.spi;uses:="javax.sound.sampled";version="0.0.0.1_005_J2SE", \
+ javax.sound.sampled;uses:="javax.sound.sampled.spi";version="0.0.0.1_005_J2SE", \
+ javax.sql.rowset.serial;uses:="javax.sql.rowset";version="0.0.0.1_005_J2SE", \
+ javax.sql.rowset.spi;uses:="javax.sql,javax.naming,javax.sql.rowset";version="0.0.0.1_005_J2SE", \
+ javax.sql.rowset;uses:="javax.sql,javax.sql.rowset.serial,javax.sql.rowset.spi";version="0.0.0.1_005_J2SE", \
+ javax.sql;uses:="javax.transaction.xa";version="0.0.0.1_005_J2SE", \
+ javax.swing.border;uses:="javax.swing";version="0.0.0.1_005_J2SE", \
+ javax.swing.colorchooser;uses:="javax.swing.event,javax.swing,javax.swing.border";version="0.0.0.1_005_J2SE", \
+ javax.swing.event;uses:="javax.swing,javax.swing.text,javax.swing.table,javax.swing.tree,javax.swing.undo";version="0.0.0.1_005_J2SE", \
+ javax.swing.filechooser;uses:="javax.swing";version="0.0.0.1_005_J2SE", \
+ javax.swing.plaf.basic;uses:="javax.swing.border,javax.swing,javax.swing.plaf,javax.swing.text,javax.swing.event,javax.swing.colorchooser,javax.accessibility,javax.swing.filechooser,javax.swing.text.html,javax.sound.sampled,javax.swing.table,javax.swing.tree";version="0.0.0.1_005_J2SE", \
+ javax.swing.plaf.metal;uses:="javax.swing.plaf,javax.swing,javax.swing.border,javax.swing.text,javax.swing.plaf.basic,javax.swing.filechooser,javax.swing.event,javax.accessibility,javax.swing.tree";version="0.0.0.1_005_J2SE", \
+ javax.swing.plaf.multi;uses:="javax.accessibility,javax.swing,javax.swing.plaf,javax.swing.filechooser,javax.swing.text,javax.swing.tree";version="0.0.0.1_005_J2SE", \
+ javax.swing.plaf.synth;uses:="javax.swing,javax.swing.plaf,javax.swing.border,javax.swing.plaf.basic,javax.swing.colorchooser,javax.swing.text,javax.xml.parsers,org.xml.sax,javax.swing.table,javax.swing.tree";version="0.0.0.1_005_J2SE", \
+ javax.swing.plaf;uses:="javax.swing,javax.swing.border,javax.accessibility,javax.swing.filechooser,javax.swing.text,javax.swing.tree";version="0.0.0.1_005_J2SE", \
+ javax.swing.table;uses:="javax.swing.event,javax.swing.plaf,javax.swing.border,javax.swing,javax.accessibility";version="0.0.0.1_005_J2SE", \
+ javax.swing.text.html.parser;uses:="javax.swing.text.html,javax.swing.text";version="0.0.0.1_005_J2SE", \
+ javax.swing.text.html;uses:="javax.swing.event,javax.swing.text,javax.accessibility,javax.swing,javax.swing.plaf,javax.swing.border,javax.swing.undo";version="0.0.0.1_005_J2SE", \
+ javax.swing.text.rtf;uses:="javax.swing.text";version="0.0.0.1_005_J2SE", \
+ javax.swing.text;uses:="javax.swing.event,javax.swing.tree,javax.swing.undo,javax.swing,javax.swing.plaf,javax.swing.plaf.basic,javax.accessibility,javax.swing.text.html";version="0.0.0.1_005_J2SE", \
+ javax.swing.tree;uses:="javax.swing.event,javax.swing,javax.swing.border,javax.swing.plaf,javax.swing.plaf.basic";version="0.0.0.1_005_J2SE", \
+ javax.swing.undo;uses:="javax.swing,javax.swing.event";version="0.0.0.1_005_J2SE", \
+ javax.swing;uses:="javax.swing.event,javax.accessibility,javax.swing.text,javax.swing.plaf,javax.swing.border,javax.swing.tree,javax.swing.table,javax.swing.colorchooser,javax.swing.plaf.basic,javax.swing.text.html,javax.swing.filechooser,javax.print.attribute,javax.swing.plaf.metal";version="0.0.0.1_005_J2SE", \
+ javax.transaction.xa;version="0.0.0.1_005_J2SE", \
+ javax.transaction;version="0.0.0.1_005_J2SE", \
+ javax.xml.datatype;uses:="javax.xml.namespace";version="0.0.0.1_005_J2SE", \
+ javax.xml.namespace;version="0.0.0.1_005_J2SE", \
+ javax.xml.parsers;uses:="javax.xml.validation,org.w3c.dom,org.xml.sax,org.xml.sax.helpers";version="0.0.0.1_005_J2SE", \
+ javax.xml.transform.dom;uses:="javax.xml.transform,org.w3c.dom";version="0.0.0.1_005_J2SE", \
+ javax.xml.transform.sax;uses:="org.xml.sax.ext,javax.xml.transform,org.xml.sax,javax.xml.transform.stream";version="0.0.0.1_005_J2SE", \
+ javax.xml.transform.stream;uses:="javax.xml.transform";version="0.0.0.1_005_J2SE", \
+ javax.xml.transform;version="0.0.0.1_005_J2SE", \
+ javax.xml.validation;uses:="org.w3c.dom.ls,javax.xml.transform,javax.xml.transform.stream,org.xml.sax,org.w3c.dom";version="0.0.0.1_005_J2SE", \
+ javax.xml.xpath;uses:="org.xml.sax,javax.xml.namespace";version="0.0.0.1_005_J2SE", \
+ javax.xml;version="0.0.0.1_005_J2SE", \
+ org.ietf.jgss;version="0.0.0.1_005_J2SE", \
+ org.omg.CORBA.DynAnyPackage;uses:="org.omg.CORBA";version="0.0.0.1_005_J2SE", \
+ org.omg.CORBA.ORBPackage;uses:="org.omg.CORBA";version="0.0.0.1_005_J2SE", \
+ org.omg.CORBA.TypeCodePackage;uses:="org.omg.CORBA";version="0.0.0.1_005_J2SE", \
+ org.omg.CORBA.portable;uses:="org.omg.CORBA,org.omg.CORBA_2_3.portable";version="0.0.0.1_005_J2SE", \
+ org.omg.CORBA;uses:="org.omg.CORBA.portable,org.omg.CORBA.DynAnyPackage,org.omg.CORBA.ORBPackage,org.omg.CORBA_2_3.portable,org.omg.CORBA.TypeCodePackage";version="0.0.0.1_005_J2SE", \
+ org.omg.CORBA_2_3.portable;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_005_J2SE", \
+ org.omg.CORBA_2_3;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_005_J2SE", \
+ org.omg.CosNaming.NamingContextExtPackage;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_005_J2SE", \
+ org.omg.CosNaming.NamingContextPackage;uses:="org.omg.CORBA,org.omg.CORBA.portable,org.omg.CosNaming";version="0.0.0.1_005_J2SE", \
+ org.omg.CosNaming;uses:="org.omg.CORBA.portable,org.omg.CORBA,org.omg.PortableServer,org.omg.CosNaming.NamingContextPackage,org.omg.CosNaming.NamingContextExtPackage";version="0.0.0.1_005_J2SE", \
+ org.omg.Dynamic;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_005_J2SE", \
+ org.omg.DynamicAny.DynAnyFactoryPackage;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_005_J2SE", \
+ org.omg.DynamicAny.DynAnyPackage;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_005_J2SE", \
+ org.omg.DynamicAny;uses:="org.omg.CORBA,org.omg.CORBA.portable,org.omg.DynamicAny.DynAnyFactoryPackage,org.omg.DynamicAny.DynAnyPackage";version="0.0.0.1_005_J2SE", \
+ org.omg.IOP.CodecFactoryPackage;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_005_J2SE", \
+ org.omg.IOP.CodecPackage;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_005_J2SE", \
+ org.omg.IOP;uses:="org.omg.CORBA,org.omg.CORBA.portable,org.omg.IOP.CodecFactoryPackage,org.omg.IOP.CodecPackage";version="0.0.0.1_005_J2SE", \
+ org.omg.Messaging;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_005_J2SE", \
+ org.omg.PortableInterceptor.ORBInitInfoPackage;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_005_J2SE", \
+ org.omg.PortableInterceptor;uses:="org.omg.CORBA,org.omg.CORBA.portable,org.omg.IOP,org.omg.PortableInterceptor.ORBInitInfoPackage,org.omg.CORBA_2_3.portable,org.omg.Dynamic";version="0.0.0.1_005_J2SE", \
+ org.omg.PortableServer.CurrentPackage;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_005_J2SE", \
+ org.omg.PortableServer.POAManagerPackage;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_005_J2SE", \
+ org.omg.PortableServer.POAPackage;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_005_J2SE", \
+ org.omg.PortableServer.ServantLocatorPackage;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_005_J2SE", \
+ org.omg.PortableServer.portable;uses:="org.omg.CORBA,org.omg.PortableServer";version="0.0.0.1_005_J2SE", \
+ org.omg.PortableServer;uses:="org.omg.CORBA,org.omg.CORBA.portable,org.omg.PortableServer.CurrentPackage,org.omg.PortableServer.POAManagerPackage,org.omg.PortableServer.POAPackage,org.omg.PortableServer.portable,org.omg.CORBA_2_3,org.omg.PortableServer.ServantLocatorPackage";version="0.0.0.1_005_J2SE", \
+ org.omg.SendingContext;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_005_J2SE", \
+ org.omg.stub.java.rmi;uses:="javax.rmi.CORBA";version="0.0.0.1_005_J2SE", \
+ org.w3c.dom.bootstrap;uses:="org.w3c.dom";version="0.0.0.1_005_J2SE", \
+ org.w3c.dom.events;uses:="org.w3c.dom,org.w3c.dom.views";version="0.0.0.1_005_J2SE", \
+ org.w3c.dom.ls;uses:="org.w3c.dom,org.w3c.dom.events,org.w3c.dom.traversal";version="0.0.0.1_005_J2SE", \
+ org.w3c.dom;version="0.0.0.1_005_J2SE", \
+ org.xml.sax.ext;uses:="org.xml.sax,org.xml.sax.helpers";version="0.0.0.1_005_J2SE", \
+ org.xml.sax.helpers;uses:="org.xml.sax";version="0.0.0.1_005_J2SE", \
+ org.xml.sax;version="0.0.0.1_005_J2SE"
+
+jre-1.6=, \
+ javax.accessibility;uses:="javax.swing.text";version="0.0.0.1_006_JavaSE", \
+ javax.activation;version="0.0.0.1_006_JavaSE", \
+ javax.activity;version="0.0.0.1_006_JavaSE", \
+ javax.annotation.processing;uses:="javax.tools,javax.lang.model,javax.lang.model.element,javax.lang.model.util";version="0.0.0.1_006_JavaSE", \
+ javax.annotation;version="0.0.0.1_006_JavaSE", \
+ javax.crypto.interfaces;uses:="javax.crypto.spec,javax.crypto";version="0.0.0.1_006_JavaSE", \
+ javax.crypto.spec;uses:="javax.crypto";version="0.0.0.1_006_JavaSE", \
+ javax.crypto;uses:="javax.crypto.spec";version="0.0.0.1_006_JavaSE", \
+ javax.imageio.event;uses:="javax.imageio";version="0.0.0.1_006_JavaSE", \
+ javax.imageio.metadata;uses:="org.w3c.dom,javax.imageio";version="0.0.0.1_006_JavaSE", \
+ javax.imageio.plugins.bmp;uses:="javax.imageio";version="0.0.0.1_006_JavaSE", \
+ javax.imageio.plugins.jpeg;uses:="javax.imageio";version="0.0.0.1_006_JavaSE", \
+ javax.imageio.spi;uses:="javax.imageio.stream,javax.imageio,javax.imageio.metadata";version="0.0.0.1_006_JavaSE", \
+ javax.imageio.stream;uses:="javax.imageio";version="0.0.0.1_006_JavaSE", \
+ javax.imageio;uses:="javax.imageio.metadata,javax.imageio.stream,javax.imageio.spi,javax.imageio.event";version="0.0.0.1_006_JavaSE", \
+ javax.jws.soap;version="0.0.0.1_006_JavaSE", \
+ javax.jws;version="0.0.0.1_006_JavaSE", \
+ javax.lang.model.element;uses:="javax.lang.model.type";version="0.0.0.1_006_JavaSE", \
+ javax.lang.model.type;uses:="javax.lang.model.element";version="0.0.0.1_006_JavaSE", \
+ javax.lang.model.util;uses:="javax.lang.model,javax.lang.model.element,javax.annotation.processing,javax.lang.model.type";version="0.0.0.1_006_JavaSE", \
+ javax.lang.model;version="0.0.0.1_006_JavaSE", \
+ javax.management.loading;uses:="javax.management";version="0.0.0.1_006_JavaSE", \
+ javax.management.modelmbean;uses:="javax.management,javax.management.loading";version="0.0.0.1_006_JavaSE", \
+ javax.management.monitor;uses:="javax.management,javax.management.openmbean";version="0.0.0.1_006_JavaSE", \
+ javax.management.openmbean;uses:="javax.management";version="0.0.0.1_006_JavaSE", \
+ javax.management.relation;uses:="javax.management";version="0.0.0.1_006_JavaSE", \
+ javax.management.remote.rmi;uses:="javax.management.remote,javax.security.auth,javax.management,javax.management.loading,javax.naming,org.omg.CORBA,javax.rmi.ssl,org.omg.CORBA.portable,javax.rmi,javax.rmi.CORBA,org.omg.CORBA_2_3.portable";version="0.0.0.1_006_JavaSE", \
+ javax.management.remote;uses:="javax.security.auth,javax.management";version="0.0.0.1_006_JavaSE", \
+ javax.management.timer;uses:="javax.management";version="0.0.0.1_006_JavaSE", \
+ javax.management;uses:="javax.management.loading,javax.management.openmbean";version="0.0.0.1_006_JavaSE", \
+ javax.naming.directory;uses:="javax.naming";version="0.0.0.1_006_JavaSE", \
+ javax.naming.event;uses:="javax.naming,javax.naming.directory";version="0.0.0.1_006_JavaSE", \
+ javax.naming.ldap;uses:="javax.naming,javax.naming.directory,javax.net.ssl,javax.naming.event";version="0.0.0.1_006_JavaSE", \
+ javax.naming.spi;uses:="javax.naming,javax.naming.directory";version="0.0.0.1_006_JavaSE", \
+ javax.naming;uses:="javax.naming.spi";version="0.0.0.1_006_JavaSE", \
+ javax.net.ssl;uses:="javax.security.cert,javax.security.auth.x500,javax.net";version="0.0.0.1_006_JavaSE", \
+ javax.net;version="0.0.0.1_006_JavaSE", \
+ javax.print.attribute.standard;uses:="javax.print.attribute";version="0.0.0.1_006_JavaSE", \
+ javax.print.attribute;version="0.0.0.1_006_JavaSE", \
+ javax.print.event;uses:="javax.print,javax.print.attribute";version="0.0.0.1_006_JavaSE", \
+ javax.print;uses:="javax.print.attribute,javax.print.event,javax.print.attribute.standard";version="0.0.0.1_006_JavaSE", \
+ javax.rmi.CORBA;uses:="org.omg.CORBA,org.omg.CORBA_2_3.portable,org.omg.CORBA.portable,org.omg.SendingContext";version="0.0.0.1_006_JavaSE", \
+ javax.rmi.ssl;uses:="javax.net,javax.net.ssl";version="0.0.0.1_006_JavaSE", \
+ javax.rmi;uses:="org.omg.CORBA,javax.rmi.CORBA";version="0.0.0.1_006_JavaSE", \
+ javax.script;version="0.0.0.1_006_JavaSE", \
+ javax.security.auth.callback;version="0.0.0.1_006_JavaSE", \
+ javax.security.auth.kerberos;uses:="javax.security.auth,javax.crypto";version="0.0.0.1_006_JavaSE", \
+ javax.security.auth.login;uses:="javax.security.auth,javax.security.auth.callback";version="0.0.0.1_006_JavaSE", \
+ javax.security.auth.spi;uses:="javax.security.auth.callback,javax.security.auth.login,javax.security.auth";version="0.0.0.1_006_JavaSE", \
+ javax.security.auth.x500;uses:="javax.security.auth";version="0.0.0.1_006_JavaSE", \
+ javax.security.auth;version="0.0.0.1_006_JavaSE", \
+ javax.security.cert;version="0.0.0.1_006_JavaSE", \
+ javax.security.sasl;uses:="javax.security.auth.callback";version="0.0.0.1_006_JavaSE", \
+ javax.sound.midi.spi;uses:="javax.sound.midi";version="0.0.0.1_006_JavaSE", \
+ javax.sound.midi;uses:="javax.sound.midi.spi";version="0.0.0.1_006_JavaSE", \
+ javax.sound.sampled.spi;uses:="javax.sound.sampled";version="0.0.0.1_006_JavaSE", \
+ javax.sound.sampled;uses:="javax.sound.sampled.spi";version="0.0.0.1_006_JavaSE", \
+ javax.sql.rowset.serial;uses:="javax.sql.rowset";version="0.0.0.1_006_JavaSE", \
+ javax.sql.rowset.spi;uses:="javax.sql,javax.naming,javax.sql.rowset";version="0.0.0.1_006_JavaSE", \
+ javax.sql.rowset;uses:="javax.sql,javax.sql.rowset.serial,javax.sql.rowset.spi";version="0.0.0.1_006_JavaSE", \
+ javax.sql;uses:="javax.transaction.xa";version="0.0.0.1_006_JavaSE", \
+ javax.swing.border;uses:="javax.swing";version="0.0.0.1_006_JavaSE", \
+ javax.swing.colorchooser;uses:="javax.swing.event,javax.swing,javax.swing.border";version="0.0.0.1_006_JavaSE", \
+ javax.swing.event;uses:="javax.swing,javax.swing.text,javax.swing.table,javax.swing.tree,javax.swing.undo";version="0.0.0.1_006_JavaSE", \
+ javax.swing.filechooser;uses:="javax.swing";version="0.0.0.1_006_JavaSE", \
+ javax.swing.plaf.basic;uses:="javax.swing.border,javax.swing,javax.swing.plaf,javax.swing.text,javax.swing.event,javax.swing.colorchooser,javax.accessibility,javax.swing.filechooser,javax.swing.text.html,javax.sound.sampled,javax.swing.table,javax.swing.tree";version="0.0.0.1_006_JavaSE", \
+ javax.swing.plaf.metal;uses:="javax.swing.plaf,javax.swing,javax.swing.border,javax.swing.text,javax.swing.plaf.basic,javax.swing.filechooser,javax.swing.event,javax.swing.tree";version="0.0.0.1_006_JavaSE", \
+ javax.swing.plaf.multi;uses:="javax.accessibility,javax.swing,javax.swing.plaf,javax.swing.filechooser,javax.swing.text,javax.swing.tree";version="0.0.0.1_006_JavaSE", \
+ javax.swing.plaf.synth;uses:="javax.swing,javax.swing.plaf,javax.swing.text,javax.swing.border,javax.swing.plaf.basic,javax.swing.colorchooser,javax.swing.event,javax.xml.parsers,org.xml.sax,javax.swing.table,javax.swing.tree";version="0.0.0.1_006_JavaSE", \
+ javax.swing.plaf;uses:="javax.swing,javax.swing.border,javax.accessibility,javax.swing.filechooser,javax.swing.text,javax.swing.tree";version="0.0.0.1_006_JavaSE", \
+ javax.swing.table;uses:="javax.swing.event,javax.swing.plaf,javax.swing.border,javax.swing,javax.accessibility";version="0.0.0.1_006_JavaSE", \
+ javax.swing.text.html.parser;uses:="javax.swing.text.html,javax.swing.text";version="0.0.0.1_006_JavaSE", \
+ javax.swing.text.html;uses:="javax.swing.event,javax.swing.text,javax.accessibility,javax.swing,javax.swing.plaf,javax.swing.border,javax.swing.undo";version="0.0.0.1_006_JavaSE", \
+ javax.swing.text.rtf;uses:="javax.swing.text";version="0.0.0.1_006_JavaSE", \
+ javax.swing.text;uses:="javax.swing.event,javax.swing.tree,javax.swing.undo,javax.swing,javax.swing.plaf,javax.swing.plaf.basic,javax.print,javax.print.attribute,javax.accessibility,javax.swing.text.html";version="0.0.0.1_006_JavaSE", \
+ javax.swing.tree;uses:="javax.swing.event,javax.swing,javax.swing.border,javax.swing.plaf,javax.swing.plaf.basic";version="0.0.0.1_006_JavaSE", \
+ javax.swing.undo;uses:="javax.swing,javax.swing.event";version="0.0.0.1_006_JavaSE", \
+ javax.swing;uses:="javax.swing.event,javax.accessibility,javax.swing.text,javax.swing.plaf,javax.swing.border,javax.swing.tree,javax.swing.table,javax.swing.colorchooser,javax.swing.plaf.basic,javax.swing.text.html,javax.swing.filechooser,javax.print,javax.print.attribute,javax.swing.plaf.metal";version="0.0.0.1_006_JavaSE", \
+ javax.tools;uses:="javax.lang.model.element,javax.annotation.processing,javax.lang.model";version="0.0.0.1_006_JavaSE", \
+ javax.transaction.xa;version="0.0.0.1_006_JavaSE", \
+ javax.transaction;version="0.0.0.1_006_JavaSE", \
+ javax.xml.bind.annotation.adapters;uses:="javax.xml.bind";version="0.0.0.1_006_JavaSE", \
+ javax.xml.bind.annotation;uses:="javax.xml.transform,javax.xml.bind,javax.xml.parsers,javax.xml.transform.dom,org.w3c.dom";version="0.0.0.1_006_JavaSE", \
+ javax.xml.bind.attachment;uses:="javax.activation";version="0.0.0.1_006_JavaSE", \
+ javax.xml.bind.helpers;uses:="javax.xml.bind.annotation.adapters,javax.xml.transform.dom,org.w3c.dom,org.xml.sax,javax.xml.bind.attachment,javax.xml.stream,javax.xml.transform,javax.xml.transform.stream,javax.xml.validation,javax.xml.transform.sax,javax.xml.bind,javax.xml.parsers";version="0.0.0.1_006_JavaSE", \
+ javax.xml.bind.util;uses:="javax.xml.transform.sax,javax.xml.bind,org.xml.sax,org.xml.sax.ext,org.xml.sax.helpers";version="0.0.0.1_006_JavaSE", \
+ javax.xml.bind;uses:="javax.xml.validation,javax.xml.namespace,javax.xml.datatype,javax.xml.transform,javax.xml.bind.annotation,javax.xml.transform.stream,org.w3c.dom,javax.xml.bind.attachment,javax.xml.stream,javax.xml.bind.annotation.adapters,org.xml.sax";version="0.0.0.1_006_JavaSE", \
+ javax.xml.crypto.dom;uses:="javax.xml.crypto,org.w3c.dom";version="0.0.0.1_006_JavaSE", \
+ javax.xml.crypto.dsig.dom;uses:="javax.xml.crypto.dsig,javax.xml.crypto,org.w3c.dom,javax.xml.crypto.dom";version="0.0.0.1_006_JavaSE", \
+ javax.xml.crypto.dsig.keyinfo;uses:="javax.xml.crypto";version="0.0.0.1_006_JavaSE", \
+ javax.xml.crypto.dsig.spec;uses:="javax.xml.crypto";version="0.0.0.1_006_JavaSE", \
+ javax.xml.crypto.dsig;uses:="javax.xml.crypto,javax.xml.crypto.dsig.spec,javax.xml.crypto.dsig.keyinfo";version="0.0.0.1_006_JavaSE", \
+ javax.xml.crypto;uses:="javax.xml.crypto.dsig.keyinfo";version="0.0.0.1_006_JavaSE", \
+ javax.xml.datatype;uses:="javax.xml.namespace";version="0.0.0.1_006_JavaSE", \
+ javax.xml.namespace;version="0.0.0.1_006_JavaSE", \
+ javax.xml.parsers;uses:="javax.xml.validation,org.w3c.dom,org.xml.sax,org.xml.sax.helpers";version="0.0.0.1_006_JavaSE", \
+ javax.xml.soap;uses:="javax.activation,javax.xml.namespace,org.w3c.dom,javax.xml.transform.dom,javax.xml.transform";version="0.0.0.1_006_JavaSE", \
+ javax.xml.stream.events;uses:="javax.xml.namespace,javax.xml.stream";version="0.0.0.1_006_JavaSE", \
+ javax.xml.stream.util;uses:="javax.xml.stream,javax.xml.stream.events,javax.xml.namespace";version="0.0.0.1_006_JavaSE", \
+ javax.xml.stream;uses:="javax.xml.stream.events,javax.xml.namespace,javax.xml.stream.util,javax.xml.transform";version="0.0.0.1_006_JavaSE", \
+ javax.xml.transform.dom;uses:="javax.xml.transform,org.w3c.dom";version="0.0.0.1_006_JavaSE", \
+ javax.xml.transform.sax;uses:="org.xml.sax.ext,javax.xml.transform,org.xml.sax,javax.xml.transform.stream";version="0.0.0.1_006_JavaSE", \
+ javax.xml.transform.stax;uses:="javax.xml.stream,javax.xml.transform,javax.xml.stream.events";version="0.0.0.1_006_JavaSE", \
+ javax.xml.transform.stream;uses:="javax.xml.transform";version="0.0.0.1_006_JavaSE", \
+ javax.xml.transform;version="0.0.0.1_006_JavaSE", \
+ javax.xml.validation;uses:="org.w3c.dom.ls,javax.xml.transform,javax.xml.transform.stream,org.xml.sax,org.w3c.dom";version="0.0.0.1_006_JavaSE", \
+ javax.xml.ws.handler.soap;uses:="javax.xml.ws.handler,javax.xml.namespace,javax.xml.soap,javax.xml.bind";version="0.0.0.1_006_JavaSE", \
+ javax.xml.ws.handler;uses:="javax.xml.ws,javax.xml.namespace";version="0.0.0.1_006_JavaSE", \
+ javax.xml.ws.http;uses:="javax.xml.ws";version="0.0.0.1_006_JavaSE", \
+ javax.xml.ws.soap;uses:="javax.xml.ws.spi,javax.xml.ws,javax.xml.soap";version="0.0.0.1_006_JavaSE", \
+ javax.xml.ws.spi;uses:="javax.xml.ws,javax.xml.ws.wsaddressing,javax.xml.transform,org.w3c.dom,javax.xml.namespace,javax.xml.ws.handler,javax.xml.bind";version="0.0.0.1_006_JavaSE", \
+ javax.xml.ws.wsaddressing;uses:="javax.xml.bind.annotation,javax.xml.namespace,org.w3c.dom,javax.xml.transform,javax.xml.bind,javax.xml.ws,javax.xml.ws.spi";version="0.0.0.1_006_JavaSE", \
+ javax.xml.ws;uses:="javax.xml.ws.handler,javax.xml.ws.spi,javax.xml.transform,org.w3c.dom,javax.xml.bind.annotation,javax.xml.transform.stream,javax.xml.bind,javax.xml.namespace";version="0.0.0.1_006_JavaSE", \
+ javax.xml.xpath;uses:="org.xml.sax,javax.xml.namespace";version="0.0.0.1_006_JavaSE", \
+ javax.xml;version="0.0.0.1_006_JavaSE", \
+ org.ietf.jgss;version="0.0.0.1_006_JavaSE", \
+ org.omg.CORBA.DynAnyPackage;uses:="org.omg.CORBA";version="0.0.0.1_006_JavaSE", \
+ org.omg.CORBA.ORBPackage;uses:="org.omg.CORBA";version="0.0.0.1_006_JavaSE", \
+ org.omg.CORBA.TypeCodePackage;uses:="org.omg.CORBA";version="0.0.0.1_006_JavaSE", \
+ org.omg.CORBA.portable;uses:="org.omg.CORBA,org.omg.CORBA_2_3.portable";version="0.0.0.1_006_JavaSE", \
+ org.omg.CORBA;uses:="org.omg.CORBA.portable,org.omg.CORBA.DynAnyPackage,org.omg.CORBA.ORBPackage,org.omg.CORBA_2_3.portable,org.omg.CORBA.TypeCodePackage";version="0.0.0.1_006_JavaSE", \
+ org.omg.CORBA_2_3.portable;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_006_JavaSE", \
+ org.omg.CORBA_2_3;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_006_JavaSE", \
+ org.omg.CosNaming.NamingContextExtPackage;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_006_JavaSE", \
+ org.omg.CosNaming.NamingContextPackage;uses:="org.omg.CORBA,org.omg.CORBA.portable,org.omg.CosNaming";version="0.0.0.1_006_JavaSE", \
+ org.omg.CosNaming;uses:="org.omg.CORBA.portable,org.omg.CORBA,org.omg.PortableServer,org.omg.CosNaming.NamingContextPackage,org.omg.CosNaming.NamingContextExtPackage";version="0.0.0.1_006_JavaSE", \
+ org.omg.Dynamic;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_006_JavaSE", \
+ org.omg.DynamicAny.DynAnyFactoryPackage;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_006_JavaSE", \
+ org.omg.DynamicAny.DynAnyPackage;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_006_JavaSE", \
+ org.omg.DynamicAny;uses:="org.omg.CORBA,org.omg.CORBA.portable,org.omg.DynamicAny.DynAnyFactoryPackage,org.omg.DynamicAny.DynAnyPackage";version="0.0.0.1_006_JavaSE", \
+ org.omg.IOP.CodecFactoryPackage;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_006_JavaSE", \
+ org.omg.IOP.CodecPackage;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_006_JavaSE", \
+ org.omg.IOP;uses:="org.omg.CORBA,org.omg.CORBA.portable,org.omg.IOP.CodecFactoryPackage,org.omg.IOP.CodecPackage";version="0.0.0.1_006_JavaSE", \
+ org.omg.Messaging;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_006_JavaSE", \
+ org.omg.PortableInterceptor.ORBInitInfoPackage;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_006_JavaSE", \
+ org.omg.PortableInterceptor;uses:="org.omg.CORBA,org.omg.CORBA.portable,org.omg.IOP,org.omg.PortableInterceptor.ORBInitInfoPackage,org.omg.CORBA_2_3.portable,org.omg.Dynamic";version="0.0.0.1_006_JavaSE", \
+ org.omg.PortableServer.CurrentPackage;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_006_JavaSE", \
+ org.omg.PortableServer.POAManagerPackage;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_006_JavaSE", \
+ org.omg.PortableServer.POAPackage;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_006_JavaSE", \
+ org.omg.PortableServer.ServantLocatorPackage;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_006_JavaSE", \
+ org.omg.PortableServer.portable;uses:="org.omg.CORBA,org.omg.PortableServer";version="0.0.0.1_006_JavaSE", \
+ org.omg.PortableServer;uses:="org.omg.CORBA,org.omg.CORBA.portable,org.omg.PortableServer.CurrentPackage,org.omg.PortableServer.POAManagerPackage,org.omg.PortableServer.POAPackage,org.omg.PortableServer.portable,org.omg.CORBA_2_3,org.omg.PortableServer.ServantLocatorPackage";version="0.0.0.1_006_JavaSE", \
+ org.omg.SendingContext;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_006_JavaSE", \
+ org.omg.stub.java.rmi;uses:="javax.rmi.CORBA";version="0.0.0.1_006_JavaSE", \
+ org.w3c.dom.bootstrap;uses:="org.w3c.dom";version="0.0.0.1_006_JavaSE", \
+ org.w3c.dom.events;uses:="org.w3c.dom,org.w3c.dom.views";version="0.0.0.1_006_JavaSE", \
+ org.w3c.dom.ls;uses:="org.w3c.dom,org.w3c.dom.events,org.w3c.dom.traversal";version="0.0.0.1_006_JavaSE", \
+ org.w3c.dom;version="0.0.0.1_006_JavaSE", \
+ org.xml.sax.ext;uses:="org.xml.sax,org.xml.sax.helpers";version="0.0.0.1_006_JavaSE", \
+ org.xml.sax.helpers;uses:="org.xml.sax";version="0.0.0.1_006_JavaSE", \
+ org.xml.sax;version="0.0.0.1_006_JavaSE"
+
+jre-1.7=, \
+ javax.accessibility;uses:="javax.swing.text";version="0.0.0.1_007_JavaSE", \
+ javax.activation;version="0.0.0.1_007_JavaSE", \
+ javax.activity;version="0.0.0.1_007_JavaSE", \
+ javax.annotation.processing;uses:="javax.tools,javax.lang.model,javax.lang.model.element,javax.lang.model.util";version="0.0.0.1_007_JavaSE", \
+ javax.annotation;version="0.0.0.1_007_JavaSE", \
+ javax.crypto.interfaces;uses:="javax.crypto.spec,javax.crypto";version="0.0.0.1_007_JavaSE", \
+ javax.crypto.spec;uses:="javax.crypto";version="0.0.0.1_007_JavaSE", \
+ javax.crypto;uses:="javax.crypto.spec";version="0.0.0.1_007_JavaSE", \
+ javax.imageio.event;uses:="javax.imageio";version="0.0.0.1_007_JavaSE", \
+ javax.imageio.metadata;uses:="org.w3c.dom,javax.imageio";version="0.0.0.1_007_JavaSE", \
+ javax.imageio.plugins.bmp;uses:="javax.imageio";version="0.0.0.1_007_JavaSE", \
+ javax.imageio.plugins.jpeg;uses:="javax.imageio";version="0.0.0.1_007_JavaSE", \
+ javax.imageio.spi;uses:="javax.imageio.stream,javax.imageio,javax.imageio.metadata";version="0.0.0.1_007_JavaSE", \
+ javax.imageio.stream;uses:="javax.imageio";version="0.0.0.1_007_JavaSE", \
+ javax.imageio;uses:="javax.imageio.metadata,javax.imageio.stream,javax.imageio.spi,javax.imageio.event";version="0.0.0.1_007_JavaSE", \
+ javax.jws.soap;version="0.0.0.1_007_JavaSE", \
+ javax.jws;version="0.0.0.1_007_JavaSE", \
+ javax.lang.model.element;uses:="javax.lang.model.type,javax.lang.model";version="0.0.0.1_007_JavaSE", \
+ javax.lang.model.type;uses:="javax.lang.model.element,javax.lang.model";version="0.0.0.1_007_JavaSE", \
+ javax.lang.model.util;uses:="javax.lang.model,javax.lang.model.element,javax.annotation.processing,javax.lang.model.type";version="0.0.0.1_007_JavaSE", \
+ javax.lang.model;version="0.0.0.1_007_JavaSE", \
+ javax.management.loading;uses:="javax.management";version="0.0.0.1_007_JavaSE", \
+ javax.management.modelmbean;uses:="javax.management,javax.management.loading";version="0.0.0.1_007_JavaSE", \
+ javax.management.monitor;uses:="javax.management";version="0.0.0.1_007_JavaSE", \
+ javax.management.openmbean;uses:="javax.management";version="0.0.0.1_007_JavaSE", \
+ javax.management.relation;uses:="javax.management";version="0.0.0.1_007_JavaSE", \
+ javax.management.remote.rmi;uses:="javax.management.remote,javax.security.auth,javax.management,javax.management.loading,javax.naming,javax.rmi.ssl,org.omg.CORBA,org.omg.CORBA_2_3.portable,org.omg.CORBA.portable,javax.rmi.CORBA,javax.rmi";version="0.0.0.1_007_JavaSE", \
+ javax.management.remote;uses:="javax.security.auth,javax.management";version="0.0.0.1_007_JavaSE", \
+ javax.management.timer;uses:="javax.management";version="0.0.0.1_007_JavaSE", \
+ javax.management;uses:="javax.management.loading,javax.management.openmbean";version="0.0.0.1_007_JavaSE", \
+ javax.naming.directory;uses:="javax.naming";version="0.0.0.1_007_JavaSE", \
+ javax.naming.event;uses:="javax.naming,javax.naming.directory";version="0.0.0.1_007_JavaSE", \
+ javax.naming.ldap;uses:="javax.naming,javax.naming.directory,javax.net.ssl,javax.naming.event";version="0.0.0.1_007_JavaSE", \
+ javax.naming.spi;uses:="javax.naming,javax.naming.directory";version="0.0.0.1_007_JavaSE", \
+ javax.naming;uses:="javax.naming.spi";version="0.0.0.1_007_JavaSE", \
+ javax.net.ssl;uses:="javax.security.cert,javax.security.auth.x500,javax.net";version="0.0.0.1_007_JavaSE", \
+ javax.net;version="0.0.0.1_007_JavaSE", \
+ javax.print.attribute.standard;uses:="javax.print.attribute";version="0.0.0.1_007_JavaSE", \
+ javax.print.attribute;version="0.0.0.1_007_JavaSE", \
+ javax.print.event;uses:="javax.print,javax.print.attribute";version="0.0.0.1_007_JavaSE", \
+ javax.print;uses:="javax.print.attribute,javax.print.event,javax.print.attribute.standard";version="0.0.0.1_007_JavaSE", \
+ javax.rmi.CORBA;uses:="org.omg.CORBA,org.omg.CORBA_2_3.portable,org.omg.CORBA.portable,org.omg.SendingContext";version="0.0.0.1_007_JavaSE", \
+ javax.rmi.ssl;uses:="javax.net,javax.net.ssl";version="0.0.0.1_007_JavaSE", \
+ javax.rmi;uses:="org.omg.CORBA,javax.rmi.CORBA";version="0.0.0.1_007_JavaSE", \
+ javax.script;version="0.0.0.1_007_JavaSE", \
+ javax.security.auth.callback;version="0.0.0.1_007_JavaSE", \
+ javax.security.auth.kerberos;uses:="javax.security.auth,javax.crypto";version="0.0.0.1_007_JavaSE", \
+ javax.security.auth.login;uses:="javax.security.auth,javax.security.auth.callback";version="0.0.0.1_007_JavaSE", \
+ javax.security.auth.spi;uses:="javax.security.auth.callback,javax.security.auth.login,javax.security.auth";version="0.0.0.1_007_JavaSE", \
+ javax.security.auth.x500;uses:="javax.security.auth";version="0.0.0.1_007_JavaSE", \
+ javax.security.auth;version="0.0.0.1_007_JavaSE", \
+ javax.security.cert;version="0.0.0.1_007_JavaSE", \
+ javax.security.sasl;uses:="javax.security.auth.callback";version="0.0.0.1_007_JavaSE", \
+ javax.sound.midi.spi;uses:="javax.sound.midi";version="0.0.0.1_007_JavaSE", \
+ javax.sound.midi;uses:="javax.sound.midi.spi";version="0.0.0.1_007_JavaSE", \
+ javax.sound.sampled.spi;uses:="javax.sound.sampled";version="0.0.0.1_007_JavaSE", \
+ javax.sound.sampled;uses:="javax.sound.sampled.spi";version="0.0.0.1_007_JavaSE", \
+ javax.sql.rowset.serial;uses:="javax.sql.rowset";version="0.0.0.1_007_JavaSE", \
+ javax.sql.rowset.spi;uses:="javax.sql,javax.naming,javax.sql.rowset";version="0.0.0.1_007_JavaSE", \
+ javax.sql.rowset;uses:="javax.sql,javax.sql.rowset.serial,javax.sql.rowset.spi";version="0.0.0.1_007_JavaSE", \
+ javax.sql;uses:="javax.transaction.xa";version="0.0.0.1_007_JavaSE", \
+ javax.swing.border;uses:="javax.swing";version="0.0.0.1_007_JavaSE", \
+ javax.swing.colorchooser;uses:="javax.swing,javax.swing.border,javax.swing.event,javax.swing.text";version="0.0.0.1_007_JavaSE", \
+ javax.swing.event;uses:="javax.swing,javax.swing.text,javax.swing.table,javax.swing.tree,javax.swing.undo";version="0.0.0.1_007_JavaSE", \
+ javax.swing.filechooser;uses:="javax.swing";version="0.0.0.1_007_JavaSE", \
+ javax.swing.plaf.basic;uses:="javax.swing.border,javax.swing,javax.swing.plaf,javax.swing.text,javax.swing.event,javax.swing.colorchooser,javax.accessibility,javax.swing.filechooser,javax.swing.text.html,javax.sound.sampled,javax.swing.table,javax.swing.plaf.synth,javax.swing.tree";version="0.0.0.1_007_JavaSE", \
+ javax.swing.plaf.metal;uses:="javax.swing.plaf,javax.swing,javax.swing.border,javax.swing.text,javax.swing.plaf.basic,javax.swing.filechooser,javax.swing.event,javax.swing.tree";version="0.0.0.1_007_JavaSE", \
+ javax.swing.plaf.multi;uses:="javax.accessibility,javax.swing,javax.swing.plaf,javax.swing.filechooser,javax.swing.text,javax.swing.tree";version="0.0.0.1_007_JavaSE", \
+ javax.swing.plaf.nimbus;uses:="javax.swing,javax.swing.plaf,javax.swing.border,javax.swing.plaf.synth";version="0.0.0.1_007_JavaSE", \
+ javax.swing.plaf.synth;uses:="javax.swing,javax.swing.plaf,javax.swing.text,javax.swing.border,javax.swing.plaf.basic,javax.swing.colorchooser,javax.swing.event,javax.xml.parsers,org.xml.sax,org.xml.sax.helpers,javax.swing.table,javax.swing.tree";version="0.0.0.1_007_JavaSE", \
+ javax.swing.plaf;uses:="javax.swing,javax.swing.border,javax.accessibility,javax.swing.filechooser,javax.swing.text,javax.swing.tree";version="0.0.0.1_007_JavaSE", \
+ javax.swing.table;uses:="javax.swing.event,javax.swing.plaf,javax.swing.border,javax.swing,javax.accessibility";version="0.0.0.1_007_JavaSE", \
+ javax.swing.text.html.parser;uses:="javax.swing.text,javax.swing.text.html";version="0.0.0.1_007_JavaSE", \
+ javax.swing.text.html;uses:="javax.swing.event,javax.swing.text,javax.accessibility,javax.swing,javax.swing.plaf,javax.swing.border,javax.swing.undo";version="0.0.0.1_007_JavaSE", \
+ javax.swing.text.rtf;uses:="javax.swing.text";version="0.0.0.1_007_JavaSE", \
+ javax.swing.text;uses:="javax.swing.event,javax.swing.tree,javax.swing.undo,javax.swing,javax.swing.plaf,javax.swing.plaf.basic,javax.print,javax.print.attribute,javax.accessibility,javax.swing.text.html";version="0.0.0.1_007_JavaSE", \
+ javax.swing.tree;uses:="javax.swing.event,javax.swing,javax.swing.border,javax.swing.plaf,javax.swing.plaf.basic";version="0.0.0.1_007_JavaSE", \
+ javax.swing.undo;uses:="javax.swing,javax.swing.event";version="0.0.0.1_007_JavaSE", \
+ javax.swing;uses:="javax.swing.event,javax.accessibility,javax.swing.text,javax.swing.plaf,javax.swing.border,javax.swing.tree,javax.swing.table,javax.swing.colorchooser,javax.swing.plaf.basic,javax.swing.text.html,javax.swing.filechooser,javax.print,javax.print.attribute,javax.swing.plaf.metal";version="0.0.0.1_007_JavaSE", \
+ javax.tools;uses:="javax.lang.model.element,javax.annotation.processing,javax.lang.model";version="0.0.0.1_007_JavaSE", \
+ javax.transaction.xa;version="0.0.0.1_007_JavaSE", \
+ javax.transaction;version="0.0.0.1_007_JavaSE", \
+ javax.xml.bind.annotation.adapters;uses:="javax.xml.bind";version="0.0.0.1_007_JavaSE", \
+ javax.xml.bind.annotation;uses:="javax.xml.transform,javax.xml.bind,javax.xml.parsers,javax.xml.transform.dom,org.w3c.dom";version="0.0.0.1_007_JavaSE", \
+ javax.xml.bind.attachment;uses:="javax.activation";version="0.0.0.1_007_JavaSE", \
+ javax.xml.bind.helpers;uses:="javax.xml.bind.annotation.adapters,javax.xml.transform.dom,org.w3c.dom,org.xml.sax,javax.xml.bind.attachment,javax.xml.stream,javax.xml.transform,javax.xml.transform.stream,javax.xml.validation,javax.xml.transform.sax,javax.xml.bind,javax.xml.parsers";version="0.0.0.1_007_JavaSE", \
+ javax.xml.bind.util;uses:="javax.xml.transform.sax,javax.xml.bind,org.xml.sax,org.xml.sax.ext,org.xml.sax.helpers";version="0.0.0.1_007_JavaSE", \
+ javax.xml.bind;uses:="javax.xml.validation,javax.xml.namespace,javax.xml.datatype,javax.xml.transform,javax.xml.bind.annotation,javax.xml.transform.stream,org.w3c.dom,javax.xml.bind.attachment,javax.xml.stream,javax.xml.bind.annotation.adapters,org.xml.sax";version="0.0.0.1_007_JavaSE", \
+ javax.xml.crypto.dom;uses:="javax.xml.crypto,org.w3c.dom";version="0.0.0.1_007_JavaSE", \
+ javax.xml.crypto.dsig.dom;uses:="javax.xml.crypto.dsig,javax.xml.crypto,org.w3c.dom,javax.xml.crypto.dom";version="0.0.0.1_007_JavaSE", \
+ javax.xml.crypto.dsig.keyinfo;uses:="javax.xml.crypto";version="0.0.0.1_007_JavaSE", \
+ javax.xml.crypto.dsig.spec;uses:="javax.xml.crypto";version="0.0.0.1_007_JavaSE", \
+ javax.xml.crypto.dsig;uses:="javax.xml.crypto,javax.xml.crypto.dsig.spec,javax.xml.crypto.dsig.keyinfo";version="0.0.0.1_007_JavaSE", \
+ javax.xml.crypto;uses:="javax.xml.crypto.dsig.keyinfo";version="0.0.0.1_007_JavaSE", \
+ javax.xml.datatype;uses:="javax.xml.namespace";version="0.0.0.1_007_JavaSE", \
+ javax.xml.namespace;version="0.0.0.1_007_JavaSE", \
+ javax.xml.parsers;uses:="javax.xml.validation,org.w3c.dom,org.xml.sax,org.xml.sax.helpers";version="0.0.0.1_007_JavaSE", \
+ javax.xml.soap;uses:="javax.activation,javax.xml.namespace,org.w3c.dom,javax.xml.transform.dom,javax.xml.transform";version="0.0.0.1_007_JavaSE", \
+ javax.xml.stream.events;uses:="javax.xml.namespace,javax.xml.stream";version="0.0.0.1_007_JavaSE", \
+ javax.xml.stream.util;uses:="javax.xml.stream,javax.xml.stream.events,javax.xml.namespace";version="0.0.0.1_007_JavaSE", \
+ javax.xml.stream;uses:="javax.xml.stream.events,javax.xml.namespace,javax.xml.stream.util,javax.xml.transform";version="0.0.0.1_007_JavaSE", \
+ javax.xml.transform.dom;uses:="javax.xml.transform,org.w3c.dom";version="0.0.0.1_007_JavaSE", \
+ javax.xml.transform.sax;uses:="org.xml.sax.ext,javax.xml.transform,org.xml.sax,javax.xml.transform.stream";version="0.0.0.1_007_JavaSE", \
+ javax.xml.transform.stax;uses:="javax.xml.stream,javax.xml.transform,javax.xml.stream.events";version="0.0.0.1_007_JavaSE", \
+ javax.xml.transform.stream;uses:="javax.xml.transform";version="0.0.0.1_007_JavaSE", \
+ javax.xml.transform;version="0.0.0.1_007_JavaSE", \
+ javax.xml.validation;uses:="org.w3c.dom.ls,javax.xml.transform,javax.xml.transform.stream,org.xml.sax,org.w3c.dom";version="0.0.0.1_007_JavaSE", \
+ javax.xml.ws.handler.soap;uses:="javax.xml.ws.handler,javax.xml.namespace,javax.xml.soap,javax.xml.bind";version="0.0.0.1_007_JavaSE", \
+ javax.xml.ws.handler;uses:="javax.xml.ws,javax.xml.namespace";version="0.0.0.1_007_JavaSE", \
+ javax.xml.ws.http;uses:="javax.xml.ws";version="0.0.0.1_007_JavaSE", \
+ javax.xml.ws.soap;uses:="javax.xml.ws.spi,javax.xml.ws,javax.xml.soap";version="0.0.0.1_007_JavaSE", \
+ javax.xml.ws.spi.http;version="0.0.0.1_007_JavaSE", \
+ javax.xml.ws.spi;uses:="javax.xml.ws,javax.xml.ws.wsaddressing,javax.xml.transform,org.w3c.dom,javax.xml.namespace,javax.xml.ws.handler,javax.xml.bind";version="0.0.0.1_007_JavaSE", \
+ javax.xml.ws.wsaddressing;uses:="javax.xml.bind.annotation,javax.xml.namespace,org.w3c.dom,javax.xml.transform,javax.xml.bind,javax.xml.ws,javax.xml.ws.spi";version="0.0.0.1_007_JavaSE", \
+ javax.xml.ws;uses:="javax.xml.ws.handler,javax.xml.ws.spi,javax.xml.ws.spi.http,javax.xml.transform,org.w3c.dom,javax.xml.bind.annotation,javax.xml.transform.stream,javax.xml.bind,javax.xml.namespace";version="0.0.0.1_007_JavaSE", \
+ javax.xml.xpath;uses:="org.xml.sax,javax.xml.namespace";version="0.0.0.1_007_JavaSE", \
+ javax.xml;version="0.0.0.1_007_JavaSE", \
+ org.ietf.jgss;version="0.0.0.1_007_JavaSE", \
+ org.omg.CORBA.DynAnyPackage;uses:="org.omg.CORBA";version="0.0.0.1_007_JavaSE", \
+ org.omg.CORBA.ORBPackage;uses:="org.omg.CORBA";version="0.0.0.1_007_JavaSE", \
+ org.omg.CORBA.TypeCodePackage;uses:="org.omg.CORBA";version="0.0.0.1_007_JavaSE", \
+ org.omg.CORBA.portable;uses:="org.omg.CORBA,org.omg.CORBA_2_3.portable";version="0.0.0.1_007_JavaSE", \
+ org.omg.CORBA;uses:="org.omg.CORBA.portable,org.omg.CORBA.DynAnyPackage,org.omg.CORBA.ORBPackage,org.omg.CORBA_2_3.portable,org.omg.CORBA.TypeCodePackage";version="0.0.0.1_007_JavaSE", \
+ org.omg.CORBA_2_3.portable;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_007_JavaSE", \
+ org.omg.CORBA_2_3;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_007_JavaSE", \
+ org.omg.CosNaming.NamingContextExtPackage;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_007_JavaSE", \
+ org.omg.CosNaming.NamingContextPackage;uses:="org.omg.CORBA,org.omg.CORBA.portable,org.omg.CosNaming";version="0.0.0.1_007_JavaSE", \
+ org.omg.CosNaming;uses:="org.omg.CORBA.portable,org.omg.CORBA,org.omg.PortableServer,org.omg.CosNaming.NamingContextPackage,org.omg.CosNaming.NamingContextExtPackage";version="0.0.0.1_007_JavaSE", \
+ org.omg.Dynamic;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_007_JavaSE", \
+ org.omg.DynamicAny.DynAnyFactoryPackage;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_007_JavaSE", \
+ org.omg.DynamicAny.DynAnyPackage;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_007_JavaSE", \
+ org.omg.DynamicAny;uses:="org.omg.CORBA,org.omg.CORBA.portable,org.omg.DynamicAny.DynAnyFactoryPackage,org.omg.DynamicAny.DynAnyPackage";version="0.0.0.1_007_JavaSE", \
+ org.omg.IOP.CodecFactoryPackage;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_007_JavaSE", \
+ org.omg.IOP.CodecPackage;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_007_JavaSE", \
+ org.omg.IOP;uses:="org.omg.CORBA,org.omg.CORBA.portable,org.omg.IOP.CodecFactoryPackage,org.omg.IOP.CodecPackage";version="0.0.0.1_007_JavaSE", \
+ org.omg.Messaging;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_007_JavaSE", \
+ org.omg.PortableInterceptor.ORBInitInfoPackage;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_007_JavaSE", \
+ org.omg.PortableInterceptor;uses:="org.omg.CORBA,org.omg.CORBA.portable,org.omg.IOP,org.omg.PortableInterceptor.ORBInitInfoPackage,org.omg.CORBA_2_3.portable,org.omg.Dynamic";version="0.0.0.1_007_JavaSE", \
+ org.omg.PortableServer.CurrentPackage;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_007_JavaSE", \
+ org.omg.PortableServer.POAManagerPackage;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_007_JavaSE", \
+ org.omg.PortableServer.POAPackage;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_007_JavaSE", \
+ org.omg.PortableServer.ServantLocatorPackage;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_007_JavaSE", \
+ org.omg.PortableServer.portable;uses:="org.omg.CORBA,org.omg.PortableServer";version="0.0.0.1_007_JavaSE", \
+ org.omg.PortableServer;uses:="org.omg.CORBA,org.omg.CORBA.portable,org.omg.PortableServer.CurrentPackage,org.omg.PortableServer.POAManagerPackage,org.omg.PortableServer.POAPackage,org.omg.PortableServer.portable,org.omg.CORBA_2_3,org.omg.PortableServer.ServantLocatorPackage";version="0.0.0.1_007_JavaSE", \
+ org.omg.SendingContext;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_007_JavaSE", \
+ org.omg.stub.java.rmi;uses:="javax.rmi.CORBA";version="0.0.0.1_007_JavaSE", \
+ org.w3c.dom.bootstrap;uses:="org.w3c.dom";version="0.0.0.1_007_JavaSE", \
+ org.w3c.dom.events;uses:="org.w3c.dom,org.w3c.dom.views";version="0.0.0.1_007_JavaSE", \
+ org.w3c.dom.ls;uses:="org.w3c.dom,org.w3c.dom.events,org.w3c.dom.traversal";version="0.0.0.1_007_JavaSE", \
+ org.w3c.dom;version="0.0.0.1_007_JavaSE", \
+ org.xml.sax.ext;uses:="org.xml.sax,org.xml.sax.helpers";version="0.0.0.1_007_JavaSE", \
+ org.xml.sax.helpers;uses:="org.xml.sax";version="0.0.0.1_007_JavaSE", \
+ org.xml.sax;version="0.0.0.1_007_JavaSE"
+ 
+jre-1.8=, \
+ javax.accessibility;uses:="javax.swing.text";version="0.0.0.1_008_JavaSE", \
+ javax.activation;version="0.0.0.1_008_JavaSE", \
+ javax.activity;version="0.0.0.1_008_JavaSE", \
+ javax.annotation.processing;uses:="javax.tools,javax.lang.model,javax.lang.model.element,javax.lang.model.util";version="0.0.0.1_008_JavaSE", \
+ javax.annotation;version="0.0.0.1_008_JavaSE", \
+ javax.crypto.interfaces;uses:="javax.crypto.spec,javax.crypto";version="0.0.0.1_008_JavaSE", \
+ javax.crypto.spec;uses:="javax.crypto";version="0.0.0.1_008_JavaSE", \
+ javax.crypto;uses:="javax.crypto.spec";version="0.0.0.1_008_JavaSE", \
+ javax.imageio.event;uses:="javax.imageio";version="0.0.0.1_008_JavaSE", \
+ javax.imageio.metadata;uses:="org.w3c.dom,javax.imageio";version="0.0.0.1_008_JavaSE", \
+ javax.imageio.plugins.bmp;uses:="javax.imageio";version="0.0.0.1_008_JavaSE", \
+ javax.imageio.plugins.jpeg;uses:="javax.imageio";version="0.0.0.1_008_JavaSE", \
+ javax.imageio.spi;uses:="javax.imageio.stream,javax.imageio,javax.imageio.metadata";version="0.0.0.1_008_JavaSE", \
+ javax.imageio.stream;uses:="javax.imageio";version="0.0.0.1_008_JavaSE", \
+ javax.imageio;uses:="javax.imageio.metadata,javax.imageio.stream,javax.imageio.spi,javax.imageio.event";version="0.0.0.1_008_JavaSE", \
+ javax.jws.soap;version="0.0.0.1_008_JavaSE", \
+ javax.jws;version="0.0.0.1_008_JavaSE", \
+ javax.lang.model.element;uses:="javax.lang.model.type,javax.lang.model";version="0.0.0.1_008_JavaSE", \
+ javax.lang.model.type;uses:="javax.lang.model.element,javax.lang.model";version="0.0.0.1_008_JavaSE", \
+ javax.lang.model.util;uses:="javax.lang.model,javax.lang.model.element,javax.annotation.processing,javax.lang.model.type";version="0.0.0.1_008_JavaSE", \
+ javax.lang.model;version="0.0.0.1_008_JavaSE", \
+ javax.management.loading;uses:="javax.management";version="0.0.0.1_008_JavaSE", \
+ javax.management.modelmbean;uses:="javax.management,javax.management.loading";version="0.0.0.1_008_JavaSE", \
+ javax.management.monitor;uses:="javax.management";version="0.0.0.1_008_JavaSE", \
+ javax.management.openmbean;uses:="javax.management";version="0.0.0.1_008_JavaSE", \
+ javax.management.relation;uses:="javax.management";version="0.0.0.1_008_JavaSE", \
+ javax.management.remote.rmi;uses:="javax.management.remote,javax.security.auth,javax.management,javax.management.loading,javax.naming,javax.rmi.ssl,org.omg.CORBA,org.omg.CORBA_2_3.portable,org.omg.CORBA.portable,javax.rmi.CORBA,javax.rmi";version="0.0.0.1_008_JavaSE", \
+ javax.management.remote;uses:="javax.security.auth,javax.management";version="0.0.0.1_008_JavaSE", \
+ javax.management.timer;uses:="javax.management";version="0.0.0.1_008_JavaSE", \
+ javax.management;uses:="javax.management.loading,javax.management.openmbean";version="0.0.0.1_008_JavaSE", \
+ javax.naming.directory;uses:="javax.naming";version="0.0.0.1_008_JavaSE", \
+ javax.naming.event;uses:="javax.naming,javax.naming.directory";version="0.0.0.1_008_JavaSE", \
+ javax.naming.ldap;uses:="javax.naming,javax.naming.directory,javax.net.ssl,javax.naming.event";version="0.0.0.1_008_JavaSE", \
+ javax.naming.spi;uses:="javax.naming,javax.naming.directory";version="0.0.0.1_008_JavaSE", \
+ javax.naming;uses:="javax.naming.spi";version="0.0.0.1_008_JavaSE", \
+ javax.net.ssl;uses:="javax.security.cert,javax.security.auth.x500,javax.net";version="0.0.0.1_008_JavaSE", \
+ javax.net;version="0.0.0.1_008_JavaSE", \
+ javax.print.attribute.standard;uses:="javax.print.attribute";version="0.0.0.1_008_JavaSE", \
+ javax.print.attribute;version="0.0.0.1_008_JavaSE", \
+ javax.print.event;uses:="javax.print,javax.print.attribute";version="0.0.0.1_008_JavaSE", \
+ javax.print;uses:="javax.print.attribute,javax.print.event,javax.print.attribute.standard";version="0.0.0.1_008_JavaSE", \
+ javax.rmi.CORBA;uses:="org.omg.CORBA,org.omg.CORBA_2_3.portable,org.omg.CORBA.portable,org.omg.SendingContext";version="0.0.0.1_008_JavaSE", \
+ javax.rmi.ssl;uses:="javax.net,javax.net.ssl";version="0.0.0.1_008_JavaSE", \
+ javax.rmi;uses:="org.omg.CORBA,javax.rmi.CORBA";version="0.0.0.1_008_JavaSE", \
+ javax.script;version="0.0.0.1_008_JavaSE", \
+ javax.security.auth.callback;version="0.0.0.1_008_JavaSE", \
+ javax.security.auth.kerberos;uses:="javax.security.auth,javax.crypto";version="0.0.0.1_008_JavaSE", \
+ javax.security.auth.login;uses:="javax.security.auth,javax.security.auth.callback";version="0.0.0.1_008_JavaSE", \
+ javax.security.auth.spi;uses:="javax.security.auth.callback,javax.security.auth.login,javax.security.auth";version="0.0.0.1_008_JavaSE", \
+ javax.security.auth.x500;uses:="javax.security.auth";version="0.0.0.1_008_JavaSE", \
+ javax.security.auth;version="0.0.0.1_008_JavaSE", \
+ javax.security.cert;version="0.0.0.1_008_JavaSE", \
+ javax.security.sasl;uses:="javax.security.auth.callback";version="0.0.0.1_008_JavaSE", \
+ javax.sound.midi.spi;uses:="javax.sound.midi";version="0.0.0.1_008_JavaSE", \
+ javax.sound.midi;uses:="javax.sound.midi.spi";version="0.0.0.1_008_JavaSE", \
+ javax.sound.sampled.spi;uses:="javax.sound.sampled";version="0.0.0.1_008_JavaSE", \
+ javax.sound.sampled;uses:="javax.sound.sampled.spi";version="0.0.0.1_008_JavaSE", \
+ javax.sql.rowset.serial;uses:="javax.sql.rowset";version="0.0.0.1_008_JavaSE", \
+ javax.sql.rowset.spi;uses:="javax.sql,javax.naming,javax.sql.rowset";version="0.0.0.1_008_JavaSE", \
+ javax.sql.rowset;uses:="javax.sql,javax.sql.rowset.serial,javax.sql.rowset.spi";version="0.0.0.1_008_JavaSE", \
+ javax.sql;uses:="javax.transaction.xa";version="0.0.0.1_008_JavaSE", \
+ javax.swing.border;uses:="javax.swing";version="0.0.0.1_008_JavaSE", \
+ javax.swing.colorchooser;uses:="javax.swing,javax.swing.border,javax.swing.event,javax.swing.text";version="0.0.0.1_008_JavaSE", \
+ javax.swing.event;uses:="javax.swing,javax.swing.text,javax.swing.table,javax.swing.tree,javax.swing.undo";version="0.0.0.1_008_JavaSE", \
+ javax.swing.filechooser;uses:="javax.swing";version="0.0.0.1_008_JavaSE", \
+ javax.swing.plaf.basic;uses:="javax.swing.border,javax.swing,javax.swing.plaf,javax.swing.text,javax.swing.event,javax.swing.colorchooser,javax.accessibility,javax.swing.filechooser,javax.swing.text.html,javax.sound.sampled,javax.swing.table,javax.swing.plaf.synth,javax.swing.tree";version="0.0.0.1_008_JavaSE", \
+ javax.swing.plaf.metal;uses:="javax.swing.plaf,javax.swing,javax.swing.border,javax.swing.text,javax.swing.plaf.basic,javax.swing.filechooser,javax.swing.event,javax.swing.tree";version="0.0.0.1_008_JavaSE", \
+ javax.swing.plaf.multi;uses:="javax.accessibility,javax.swing,javax.swing.plaf,javax.swing.filechooser,javax.swing.text,javax.swing.tree";version="0.0.0.1_008_JavaSE", \
+ javax.swing.plaf.nimbus;uses:="javax.swing,javax.swing.plaf,javax.swing.border,javax.swing.plaf.synth";version="0.0.0.1_008_JavaSE", \
+ javax.swing.plaf.synth;uses:="javax.swing,javax.swing.plaf,javax.swing.text,javax.swing.border,javax.swing.plaf.basic,javax.swing.colorchooser,javax.swing.event,javax.xml.parsers,org.xml.sax,org.xml.sax.helpers,javax.swing.table,javax.swing.tree";version="0.0.0.1_008_JavaSE", \
+ javax.swing.plaf;uses:="javax.swing,javax.swing.border,javax.accessibility,javax.swing.filechooser,javax.swing.text,javax.swing.tree";version="0.0.0.1_008_JavaSE", \
+ javax.swing.table;uses:="javax.swing.event,javax.swing.plaf,javax.swing.border,javax.swing,javax.accessibility";version="0.0.0.1_008_JavaSE", \
+ javax.swing.text.html.parser;uses:="javax.swing.text,javax.swing.text.html";version="0.0.0.1_008_JavaSE", \
+ javax.swing.text.html;uses:="javax.swing.event,javax.swing.text,javax.accessibility,javax.swing,javax.swing.plaf,javax.swing.border,javax.swing.undo";version="0.0.0.1_008_JavaSE", \
+ javax.swing.text.rtf;uses:="javax.swing.text";version="0.0.0.1_008_JavaSE", \
+ javax.swing.text;uses:="javax.swing.event,javax.swing.tree,javax.swing.undo,javax.swing,javax.swing.plaf,javax.swing.plaf.basic,javax.print,javax.print.attribute,javax.accessibility,javax.swing.text.html";version="0.0.0.1_008_JavaSE", \
+ javax.swing.tree;uses:="javax.swing.event,javax.swing,javax.swing.border,javax.swing.plaf,javax.swing.plaf.basic";version="0.0.0.1_008_JavaSE", \
+ javax.swing.undo;uses:="javax.swing,javax.swing.event";version="0.0.0.1_008_JavaSE", \
+ javax.swing;uses:="javax.swing.event,javax.accessibility,javax.swing.text,javax.swing.plaf,javax.swing.border,javax.swing.tree,javax.swing.table,javax.swing.colorchooser,javax.swing.plaf.basic,javax.swing.text.html,javax.swing.filechooser,javax.print,javax.print.attribute,javax.swing.plaf.metal";version="0.0.0.1_008_JavaSE", \
+ javax.tools;uses:="javax.lang.model.element,javax.annotation.processing,javax.lang.model";version="0.0.0.1_008_JavaSE", \
+ javax.transaction.xa;version="0.0.0.1_008_JavaSE", \
+ javax.transaction;version="0.0.0.1_008_JavaSE", \
+ javax.xml.bind.annotation.adapters;uses:="javax.xml.bind";version="0.0.0.1_008_JavaSE", \
+ javax.xml.bind.annotation;uses:="javax.xml.transform,javax.xml.bind,javax.xml.parsers,javax.xml.transform.dom,org.w3c.dom";version="0.0.0.1_008_JavaSE", \
+ javax.xml.bind.attachment;uses:="javax.activation";version="0.0.0.1_008_JavaSE", \
+ javax.xml.bind.helpers;uses:="javax.xml.bind.annotation.adapters,javax.xml.transform.dom,org.w3c.dom,org.xml.sax,javax.xml.bind.attachment,javax.xml.stream,javax.xml.transform,javax.xml.transform.stream,javax.xml.validation,javax.xml.transform.sax,javax.xml.bind,javax.xml.parsers";version="0.0.0.1_008_JavaSE", \
+ javax.xml.bind.util;uses:="javax.xml.transform.sax,javax.xml.bind,org.xml.sax,org.xml.sax.ext,org.xml.sax.helpers";version="0.0.0.1_008_JavaSE", \
+ javax.xml.bind;uses:="javax.xml.validation,javax.xml.namespace,javax.xml.datatype,javax.xml.transform,javax.xml.bind.annotation,javax.xml.transform.stream,org.w3c.dom,javax.xml.bind.attachment,javax.xml.stream,javax.xml.bind.annotation.adapters,org.xml.sax";version="0.0.0.1_008_JavaSE", \
+ javax.xml.crypto.dom;uses:="javax.xml.crypto,org.w3c.dom";version="0.0.0.1_008_JavaSE", \
+ javax.xml.crypto.dsig.dom;uses:="javax.xml.crypto.dsig,javax.xml.crypto,org.w3c.dom,javax.xml.crypto.dom";version="0.0.0.1_008_JavaSE", \
+ javax.xml.crypto.dsig.keyinfo;uses:="javax.xml.crypto";version="0.0.0.1_008_JavaSE", \
+ javax.xml.crypto.dsig.spec;uses:="javax.xml.crypto";version="0.0.0.1_008_JavaSE", \
+ javax.xml.crypto.dsig;uses:="javax.xml.crypto,javax.xml.crypto.dsig.spec,javax.xml.crypto.dsig.keyinfo";version="0.0.0.1_008_JavaSE", \
+ javax.xml.crypto;uses:="javax.xml.crypto.dsig.keyinfo";version="0.0.0.1_008_JavaSE", \
+ javax.xml.datatype;uses:="javax.xml.namespace";version="0.0.0.1_008_JavaSE", \
+ javax.xml.namespace;version="0.0.0.1_008_JavaSE", \
+ javax.xml.parsers;uses:="javax.xml.validation,org.w3c.dom,org.xml.sax,org.xml.sax.helpers";version="0.0.0.1_008_JavaSE", \
+ javax.xml.soap;uses:="javax.activation,javax.xml.namespace,org.w3c.dom,javax.xml.transform.dom,javax.xml.transform";version="0.0.0.1_008_JavaSE", \
+ javax.xml.stream.events;uses:="javax.xml.namespace,javax.xml.stream";version="0.0.0.1_008_JavaSE", \
+ javax.xml.stream.util;uses:="javax.xml.stream,javax.xml.stream.events,javax.xml.namespace";version="0.0.0.1_008_JavaSE", \
+ javax.xml.stream;uses:="javax.xml.stream.events,javax.xml.namespace,javax.xml.stream.util,javax.xml.transform";version="0.0.0.1_008_JavaSE", \
+ javax.xml.transform.dom;uses:="javax.xml.transform,org.w3c.dom";version="0.0.0.1_008_JavaSE", \
+ javax.xml.transform.sax;uses:="org.xml.sax.ext,javax.xml.transform,org.xml.sax,javax.xml.transform.stream";version="0.0.0.1_008_JavaSE", \
+ javax.xml.transform.stax;uses:="javax.xml.stream,javax.xml.transform,javax.xml.stream.events";version="0.0.0.1_008_JavaSE", \
+ javax.xml.transform.stream;uses:="javax.xml.transform";version="0.0.0.1_008_JavaSE", \
+ javax.xml.transform;version="0.0.0.1_008_JavaSE", \
+ javax.xml.validation;uses:="org.w3c.dom.ls,javax.xml.transform,javax.xml.transform.stream,org.xml.sax,org.w3c.dom";version="0.0.0.1_008_JavaSE", \
+ javax.xml.ws.handler.soap;uses:="javax.xml.ws.handler,javax.xml.namespace,javax.xml.soap,javax.xml.bind";version="0.0.0.1_008_JavaSE", \
+ javax.xml.ws.handler;uses:="javax.xml.ws,javax.xml.namespace";version="0.0.0.1_008_JavaSE", \
+ javax.xml.ws.http;uses:="javax.xml.ws";version="0.0.0.1_008_JavaSE", \
+ javax.xml.ws.soap;uses:="javax.xml.ws.spi,javax.xml.ws,javax.xml.soap";version="0.0.0.1_008_JavaSE", \
+ javax.xml.ws.spi.http;version="0.0.0.1_008_JavaSE", \
+ javax.xml.ws.spi;uses:="javax.xml.ws,javax.xml.ws.wsaddressing,javax.xml.transform,org.w3c.dom,javax.xml.namespace,javax.xml.ws.handler,javax.xml.bind";version="0.0.0.1_008_JavaSE", \
+ javax.xml.ws.wsaddressing;uses:="javax.xml.bind.annotation,javax.xml.namespace,org.w3c.dom,javax.xml.transform,javax.xml.bind,javax.xml.ws,javax.xml.ws.spi";version="0.0.0.1_008_JavaSE", \
+ javax.xml.ws;uses:="javax.xml.ws.handler,javax.xml.ws.spi,javax.xml.ws.spi.http,javax.xml.transform,org.w3c.dom,javax.xml.bind.annotation,javax.xml.transform.stream,javax.xml.bind,javax.xml.namespace";version="0.0.0.1_008_JavaSE", \
+ javax.xml.xpath;uses:="org.xml.sax,javax.xml.namespace";version="0.0.0.1_008_JavaSE", \
+ javax.xml;version="0.0.0.1_008_JavaSE", \
+ org.ietf.jgss;version="0.0.0.1_008_JavaSE", \
+ org.omg.CORBA.DynAnyPackage;uses:="org.omg.CORBA";version="0.0.0.1_008_JavaSE", \
+ org.omg.CORBA.ORBPackage;uses:="org.omg.CORBA";version="0.0.0.1_008_JavaSE", \
+ org.omg.CORBA.TypeCodePackage;uses:="org.omg.CORBA";version="0.0.0.1_008_JavaSE", \
+ org.omg.CORBA.portable;uses:="org.omg.CORBA,org.omg.CORBA_2_3.portable";version="0.0.0.1_008_JavaSE", \
+ org.omg.CORBA;uses:="org.omg.CORBA.portable,org.omg.CORBA.DynAnyPackage,org.omg.CORBA.ORBPackage,org.omg.CORBA_2_3.portable,org.omg.CORBA.TypeCodePackage";version="0.0.0.1_008_JavaSE", \
+ org.omg.CORBA_2_3.portable;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_008_JavaSE", \
+ org.omg.CORBA_2_3;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_008_JavaSE", \
+ org.omg.CosNaming.NamingContextExtPackage;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_008_JavaSE", \
+ org.omg.CosNaming.NamingContextPackage;uses:="org.omg.CORBA,org.omg.CORBA.portable,org.omg.CosNaming";version="0.0.0.1_008_JavaSE", \
+ org.omg.CosNaming;uses:="org.omg.CORBA.portable,org.omg.CORBA,org.omg.PortableServer,org.omg.CosNaming.NamingContextPackage,org.omg.CosNaming.NamingContextExtPackage";version="0.0.0.1_008_JavaSE", \
+ org.omg.Dynamic;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_008_JavaSE", \
+ org.omg.DynamicAny.DynAnyFactoryPackage;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_008_JavaSE", \
+ org.omg.DynamicAny.DynAnyPackage;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_008_JavaSE", \
+ org.omg.DynamicAny;uses:="org.omg.CORBA,org.omg.CORBA.portable,org.omg.DynamicAny.DynAnyFactoryPackage,org.omg.DynamicAny.DynAnyPackage";version="0.0.0.1_008_JavaSE", \
+ org.omg.IOP.CodecFactoryPackage;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_008_JavaSE", \
+ org.omg.IOP.CodecPackage;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_008_JavaSE", \
+ org.omg.IOP;uses:="org.omg.CORBA,org.omg.CORBA.portable,org.omg.IOP.CodecFactoryPackage,org.omg.IOP.CodecPackage";version="0.0.0.1_008_JavaSE", \
+ org.omg.Messaging;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_008_JavaSE", \
+ org.omg.PortableInterceptor.ORBInitInfoPackage;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_008_JavaSE", \
+ org.omg.PortableInterceptor;uses:="org.omg.CORBA,org.omg.CORBA.portable,org.omg.IOP,org.omg.PortableInterceptor.ORBInitInfoPackage,org.omg.CORBA_2_3.portable,org.omg.Dynamic";version="0.0.0.1_008_JavaSE", \
+ org.omg.PortableServer.CurrentPackage;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_008_JavaSE", \
+ org.omg.PortableServer.POAManagerPackage;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_008_JavaSE", \
+ org.omg.PortableServer.POAPackage;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_008_JavaSE", \
+ org.omg.PortableServer.ServantLocatorPackage;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_008_JavaSE", \
+ org.omg.PortableServer.portable;uses:="org.omg.CORBA,org.omg.PortableServer";version="0.0.0.1_008_JavaSE", \
+ org.omg.PortableServer;uses:="org.omg.CORBA,org.omg.CORBA.portable,org.omg.PortableServer.CurrentPackage,org.omg.PortableServer.POAManagerPackage,org.omg.PortableServer.POAPackage,org.omg.PortableServer.portable,org.omg.CORBA_2_3,org.omg.PortableServer.ServantLocatorPackage";version="0.0.0.1_008_JavaSE", \
+ org.omg.SendingContext;uses:="org.omg.CORBA,org.omg.CORBA.portable";version="0.0.0.1_008_JavaSE", \
+ org.omg.stub.java.rmi;uses:="javax.rmi.CORBA";version="0.0.0.1_008_JavaSE", \
+ org.w3c.dom.bootstrap;uses:="org.w3c.dom";version="0.0.0.1_008_JavaSE", \
+ org.w3c.dom.events;uses:="org.w3c.dom,org.w3c.dom.views";version="0.0.0.1_008_JavaSE", \
+ org.w3c.dom.ls;uses:="org.w3c.dom,org.w3c.dom.events,org.w3c.dom.traversal";version="0.0.0.1_008_JavaSE", \
+ org.w3c.dom;version="0.0.0.1_008_JavaSE", \
+ org.xml.sax.ext;uses:="org.xml.sax,org.xml.sax.helpers";version="0.0.0.1_008_JavaSE", \
+ org.xml.sax.helpers;uses:="org.xml.sax";version="0.0.0.1_008_JavaSE", \
+ org.xml.sax;version="0.0.0.1_008_JavaSE"
diff --git a/src/main/resources/org/apache/felix/framework/Felix.properties b/src/main/resources/org/apache/felix/framework/Felix.properties
new file mode 100644
index 0000000..768c415
--- /dev/null
+++ b/src/main/resources/org/apache/felix/framework/Felix.properties
@@ -0,0 +1 @@
+felix.version=${pom.version}
diff --git a/src/main/resources/org/osgi/framework/hooks/bundle/packageinfo b/src/main/resources/org/osgi/framework/hooks/bundle/packageinfo
new file mode 100644
index 0000000..3987f9c
--- /dev/null
+++ b/src/main/resources/org/osgi/framework/hooks/bundle/packageinfo
@@ -0,0 +1 @@
+version 1.1
diff --git a/src/main/resources/org/osgi/framework/hooks/resolver/packageinfo b/src/main/resources/org/osgi/framework/hooks/resolver/packageinfo
new file mode 100644
index 0000000..7c8de03
--- /dev/null
+++ b/src/main/resources/org/osgi/framework/hooks/resolver/packageinfo
@@ -0,0 +1 @@
+version 1.0
diff --git a/src/main/resources/org/osgi/framework/hooks/service/packageinfo b/src/main/resources/org/osgi/framework/hooks/service/packageinfo
new file mode 100644
index 0000000..3987f9c
--- /dev/null
+++ b/src/main/resources/org/osgi/framework/hooks/service/packageinfo
@@ -0,0 +1 @@
+version 1.1
diff --git a/src/main/resources/org/osgi/framework/hooks/weaving/packageinfo b/src/main/resources/org/osgi/framework/hooks/weaving/packageinfo
new file mode 100644
index 0000000..7c8de03
--- /dev/null
+++ b/src/main/resources/org/osgi/framework/hooks/weaving/packageinfo
@@ -0,0 +1 @@
+version 1.0
diff --git a/src/main/resources/org/osgi/framework/launch/packageinfo b/src/main/resources/org/osgi/framework/launch/packageinfo
new file mode 100644
index 0000000..3987f9c
--- /dev/null
+++ b/src/main/resources/org/osgi/framework/launch/packageinfo
@@ -0,0 +1 @@
+version 1.1
diff --git a/src/main/resources/org/osgi/framework/namespace/packageinfo b/src/main/resources/org/osgi/framework/namespace/packageinfo
new file mode 100644
index 0000000..7c8de03
--- /dev/null
+++ b/src/main/resources/org/osgi/framework/namespace/packageinfo
@@ -0,0 +1 @@
+version 1.0
diff --git a/src/main/resources/org/osgi/framework/packageinfo b/src/main/resources/org/osgi/framework/packageinfo
new file mode 100644
index 0000000..5d21e63
--- /dev/null
+++ b/src/main/resources/org/osgi/framework/packageinfo
@@ -0,0 +1 @@
+version 1.7
diff --git a/src/main/resources/org/osgi/framework/startlevel/packageinfo b/src/main/resources/org/osgi/framework/startlevel/packageinfo
new file mode 100644
index 0000000..7c8de03
--- /dev/null
+++ b/src/main/resources/org/osgi/framework/startlevel/packageinfo
@@ -0,0 +1 @@
+version 1.0
diff --git a/src/main/resources/org/osgi/framework/wiring/packageinfo b/src/main/resources/org/osgi/framework/wiring/packageinfo
new file mode 100644
index 0000000..3987f9c
--- /dev/null
+++ b/src/main/resources/org/osgi/framework/wiring/packageinfo
@@ -0,0 +1 @@
+version 1.1
diff --git a/src/main/resources/org/osgi/resource/packageinfo b/src/main/resources/org/osgi/resource/packageinfo
new file mode 100644
index 0000000..7c8de03
--- /dev/null
+++ b/src/main/resources/org/osgi/resource/packageinfo
@@ -0,0 +1 @@
+version 1.0
diff --git a/src/main/resources/org/osgi/service/packageadmin/packageinfo b/src/main/resources/org/osgi/service/packageadmin/packageinfo
new file mode 100644
index 0000000..ef7df68
--- /dev/null
+++ b/src/main/resources/org/osgi/service/packageadmin/packageinfo
@@ -0,0 +1 @@
+version 1.2
diff --git a/src/main/resources/org/osgi/service/startlevel/packageinfo b/src/main/resources/org/osgi/service/startlevel/packageinfo
new file mode 100644
index 0000000..3987f9c
--- /dev/null
+++ b/src/main/resources/org/osgi/service/startlevel/packageinfo
@@ -0,0 +1 @@
+version 1.1
diff --git a/src/main/resources/org/osgi/service/url/packageinfo b/src/main/resources/org/osgi/service/url/packageinfo
new file mode 100644
index 0000000..7c8de03
--- /dev/null
+++ b/src/main/resources/org/osgi/service/url/packageinfo
@@ -0,0 +1 @@
+version 1.0
diff --git a/src/main/resources/org/osgi/util/tracker/packageinfo b/src/main/resources/org/osgi/util/tracker/packageinfo
new file mode 100644
index 0000000..1213efd
--- /dev/null
+++ b/src/main/resources/org/osgi/util/tracker/packageinfo
@@ -0,0 +1 @@
+version 1.5.1
diff --git a/src/test/java/org/apache/felix/framework/BootLoaderTest.java b/src/test/java/org/apache/felix/framework/BootLoaderTest.java
new file mode 100644
index 0000000..e392a64
--- /dev/null
+++ b/src/test/java/org/apache/felix/framework/BootLoaderTest.java
@@ -0,0 +1,119 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.jar.JarOutputStream;
+import java.util.jar.Manifest;
+import junit.framework.TestCase;
+import org.apache.felix.framework.util.FelixConstants;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.Constants;
+import org.osgi.framework.launch.Framework;
+
+/**
+ * Test to ensure behaviour of "felix.bootdelegation.classloaders" contract.
+ * 
+ */
+public class BootLoaderTest extends TestCase
+{
+    private File cacheDir;
+
+    public void testCanProvideOwnClassLoader() throws Exception
+    {
+        final ClassLoader myClassloader = new CL();
+        final List queriedFor = new ArrayList();
+
+        Map bundle2Classloader = new HashMap()
+        {
+            public Object get(Object o)
+            {
+                queriedFor.add(o);
+                return myClassloader;
+            }
+        };
+
+        Map params = new HashMap();
+        params.put(Constants.FRAMEWORK_SYSTEMPACKAGES,
+            "org.osgi.framework; version=1.4.0,"
+            + "org.osgi.service.packageadmin; version=1.2.0,"
+            + "org.osgi.service.startlevel; version=1.1.0,"
+            + "org.osgi.util.tracker; version=1.3.3,"
+            + "org.osgi.service.url; version=1.0.0");
+        params.put(FelixConstants.BOOT_CLASSLOADERS_PROP, bundle2Classloader);
+        cacheDir = File.createTempFile("felix-cache", ".dir");
+        cacheDir.delete();
+        cacheDir.mkdirs();
+        String cache = cacheDir.getPath();
+        params.put("felix.cache.profiledir", cache);
+        params.put("felix.cache.dir", cache);
+        params.put(Constants.FRAMEWORK_STORAGE, cache);
+
+        String mf = "Bundle-SymbolicName: boot.test\n"
+            + "Bundle-Version: 1.1.0\n"
+            + "Bundle-ManifestVersion: 2\n"
+            + "Export-Package: boot.test";
+        File bundle = createBundle(mf);
+
+        Framework f = new Felix(params);
+        f.init();
+        f.getBundleContext().installBundle(bundle.toURI().toString());
+        f.start();
+
+        Bundle[] arr = f.getBundleContext().getBundles();
+        assertEquals("Two, system and mine: " + Arrays.toString(arr), 2, arr.length);
+        Class c = arr[1].loadClass("boot.test.Test");
+        assertNotNull("Class loaded", c);
+        assertEquals("One query", 1, queriedFor.size());
+        assertEquals("Queried for my bundle", arr[1], queriedFor.get(0));
+    }
+
+    public static final class CL extends ClassLoader
+    {
+        protected Class findClass(String name) throws ClassNotFoundException
+        {
+            if (name.equals("boot.test.Test"))
+            {
+                return String.class;
+            }
+            throw new ClassNotFoundException();
+        }
+    }
+
+    private static File createBundle(String manifest) throws IOException
+    {
+        File f = File.createTempFile("felix-bundle", ".jar");
+        f.deleteOnExit();
+
+        Manifest mf = new Manifest(new ByteArrayInputStream(manifest.getBytes("utf-8")));
+        mf.getMainAttributes().putValue("Manifest-Version", "1.0");
+        JarOutputStream os = new JarOutputStream(new FileOutputStream(f), mf);
+        os.close();
+
+        return f;
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/org/apache/felix/framework/CollisionHookTest.java b/src/test/java/org/apache/felix/framework/CollisionHookTest.java
new file mode 100644
index 0000000..31eafd0
--- /dev/null
+++ b/src/test/java/org/apache/felix/framework/CollisionHookTest.java
@@ -0,0 +1,297 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+import org.apache.felix.framework.cache.BundleArchive;
+import org.apache.felix.framework.cache.BundleArchiveRevision;
+import org.mockito.Mockito;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.Version;
+import org.osgi.framework.hooks.bundle.CollisionHook;
+
+public class CollisionHookTest extends TestCase
+{
+    public void testCollisionHookInstall() throws Exception
+    {
+        BundleImpl identicalBundle = mockBundleImpl(1L, "foo", "1.2.1.a");
+        BundleImpl differentBundle = mockBundleImpl(2L, "bar", "1.2.1.a");
+        BundleImpl originatingBundle = mockBundleImpl(4L, "xyz", "1.0.0");
+
+        CollisionHook testCollisionHook = new CollisionHook()
+        {
+            public void filterCollisions(int operationType, Bundle target, Collection<Bundle> collisionCandidates)
+            {
+                if ((target.getBundleId() == 4L) && (operationType == CollisionHook.INSTALLING))
+                {
+                    collisionCandidates.clear();
+                }
+            }
+        };
+
+        @SuppressWarnings("unchecked")
+        ServiceReference<CollisionHook> chRef = Mockito.mock(ServiceReference.class);
+
+        // Mock the framework
+        StatefulResolver mockResolver = Mockito.mock(StatefulResolver.class);
+        Felix felixMock = Mockito.mock(Felix.class);
+        Mockito.when(felixMock.getHooks(CollisionHook.class)).thenReturn(Collections.singleton(chRef));
+        Mockito.when(felixMock.getResolver()).thenReturn(mockResolver);
+        Mockito.when(felixMock.getBundles()).thenReturn(new Bundle[]
+        {
+            differentBundle, identicalBundle
+        });
+        Mockito.when(felixMock.getService(felixMock, chRef)).thenReturn(testCollisionHook);
+
+        // Mock the archive of the bundle being installed
+        Map<String, String> headerMap = new HashMap<String, String>();
+        headerMap.put(Constants.BUNDLE_SYMBOLICNAME, "foo");
+        headerMap.put(Constants.BUNDLE_VERSION, "1.2.1.a");
+        headerMap.put(Constants.BUNDLE_MANIFESTVERSION, "2");
+
+        BundleArchiveRevision archiveRevision = Mockito.mock(BundleArchiveRevision.class);
+        Mockito.when(archiveRevision.getManifestHeader()).thenReturn(headerMap);
+
+        BundleArchive archive = Mockito.mock(BundleArchive.class);
+        Mockito.when(archive.getCurrentRevision()).thenReturn(archiveRevision);
+        Mockito.when(archive.getId()).thenReturn(3L);
+
+        BundleImpl bi = new BundleImpl(felixMock, originatingBundle, archive);
+        assertEquals(3L, bi.getBundleId());
+
+        // Do the revise operation.
+        try
+        {
+            bi.revise(null, null);
+            fail("Should have thrown a BundleException because the installed bundle is not unique");
+        }
+        catch (BundleException be)
+        {
+            // good
+            assertTrue(be.getMessage().contains("not unique"));
+        }
+    }
+
+    public void testCollisionHookUpdate() throws Exception
+    {
+        BundleImpl identicalBundle = mockBundleImpl(1L, "foo", "1.2.1.a");
+        BundleImpl differentBundle = mockBundleImpl(2L, "foo", "1.2.1");
+
+        CollisionHook testCollisionHook = new CollisionHook()
+        {
+            public void filterCollisions(int operationType, Bundle target, Collection<Bundle> collisionCandidates)
+            {
+                if ((target.getBundleId() == 3L) && (operationType == CollisionHook.UPDATING))
+                {
+                    collisionCandidates.clear();
+                }
+            }
+        };
+
+        @SuppressWarnings("unchecked")
+        ServiceReference<CollisionHook> chRef = Mockito.mock(ServiceReference.class);
+
+        Map<String, String> config = new HashMap<String, String>();
+        config.put(Constants.FRAMEWORK_BSNVERSION, Constants.FRAMEWORK_BSNVERSION_MANAGED);
+
+        // Mock the framework
+        StatefulResolver mockResolver = Mockito.mock(StatefulResolver.class);
+        Felix felixMock = Mockito.mock(Felix.class);
+        Mockito.when(felixMock.getConfig()).thenReturn(config);
+        Mockito.when(felixMock.getHooks(CollisionHook.class)).thenReturn(Collections.singleton(chRef));
+        Mockito.when(felixMock.getResolver()).thenReturn(mockResolver);
+        Mockito.when(felixMock.getBundles()).thenReturn(new Bundle[]
+        {
+            differentBundle, identicalBundle
+        });
+        Mockito.when(felixMock.getService(felixMock, chRef)).thenReturn(testCollisionHook);
+
+        // Mock the archive of the bundle being installed
+        Map<String, String> headerMap = new HashMap<String, String>();
+        headerMap.put(Constants.BUNDLE_SYMBOLICNAME, "zar");
+        headerMap.put(Constants.BUNDLE_VERSION, "1.2.1.a");
+        headerMap.put(Constants.BUNDLE_MANIFESTVERSION, "2");
+
+        BundleArchiveRevision archiveRevision = Mockito.mock(BundleArchiveRevision.class);
+        Mockito.when(archiveRevision.getManifestHeader()).thenReturn(headerMap);
+
+        BundleArchive archive = Mockito.mock(BundleArchive.class);
+        Mockito.when(archive.getCurrentRevision()).thenReturn(archiveRevision);
+        Mockito.when(archive.getId()).thenReturn(3L);
+
+        BundleImpl bi = new BundleImpl(felixMock, null, archive);
+        assertEquals("zar", bi.getSymbolicName());
+
+        // Do the revise operation, change the bsn to foo
+        headerMap.put(Constants.BUNDLE_SYMBOLICNAME, "foo");
+        bi.revise(null, null);
+        assertEquals("foo", bi.getSymbolicName());
+    }
+
+    public void testCollisionNotEnabled() throws Exception
+    {
+        BundleImpl identicalBundle = mockBundleImpl(1L, "foo", "1.2.1.a");
+        BundleImpl differentBundle = mockBundleImpl(2L, "bar", "1.2.1.a");
+
+        CollisionHook testCollisionHook = new CollisionHook()
+        {
+            public void filterCollisions(int operationType, Bundle target, Collection<Bundle> collisionCandidates)
+            {
+                if ((target.getBundleId() == 3L) && (operationType == CollisionHook.INSTALLING))
+                {
+                    collisionCandidates.clear();
+                }
+            }
+        };
+
+        @SuppressWarnings("unchecked")
+        ServiceReference<CollisionHook> chRef = Mockito.mock(ServiceReference.class);
+
+        Map<String, String> config = new HashMap<String, String>();
+        config.put(Constants.FRAMEWORK_BSNVERSION, Constants.FRAMEWORK_BSNVERSION_SINGLE);
+
+        // Mock the framework
+        StatefulResolver mockResolver = Mockito.mock(StatefulResolver.class);
+        Felix felixMock = Mockito.mock(Felix.class);
+        Mockito.when(felixMock.getConfig()).thenReturn(config);
+        Mockito.when(felixMock.getHooks(CollisionHook.class)).thenReturn(Collections.singleton(chRef));
+        Mockito.when(felixMock.getResolver()).thenReturn(mockResolver);
+        Mockito.when(felixMock.getBundles()).thenReturn(new Bundle[]
+        {
+            differentBundle, identicalBundle
+        });
+        Mockito.when(felixMock.getService(felixMock, chRef)).thenReturn(testCollisionHook);
+
+        // Mock the archive of the bundle being installed
+        Map<String, String> headerMap = new HashMap<String, String>();
+        headerMap.put(Constants.BUNDLE_SYMBOLICNAME, "foo");
+        headerMap.put(Constants.BUNDLE_VERSION, "1.2.1.a");
+        headerMap.put(Constants.BUNDLE_MANIFESTVERSION, "2");
+
+        BundleArchiveRevision archiveRevision = Mockito.mock(BundleArchiveRevision.class);
+        Mockito.when(archiveRevision.getManifestHeader()).thenReturn(headerMap);
+
+        BundleArchive archive = Mockito.mock(BundleArchive.class);
+        Mockito.when(archive.getCurrentRevision()).thenReturn(archiveRevision);
+        Mockito.when(archive.getId()).thenReturn(3L);
+
+        try
+        {
+            new BundleImpl(felixMock, null, archive);
+            fail("Should have thrown a BundleException because the collision hook is not enabled");
+        }
+        catch (BundleException be)
+        {
+            // good
+            assertTrue(be.getMessage().contains("not unique"));
+        }
+    }
+
+    public void testAllowMultiple() throws Exception
+    {
+        BundleImpl identicalBundle = mockBundleImpl(1L, "foo", "1.2.1.a");
+        BundleImpl differentBundle = mockBundleImpl(2L, "bar", "1.2.1.a");
+
+        Map<String, String> config = new HashMap<String, String>();
+        config.put(Constants.FRAMEWORK_BSNVERSION, Constants.FRAMEWORK_BSNVERSION_MULTIPLE);
+
+        // Mock the framework
+        StatefulResolver mockResolver = Mockito.mock(StatefulResolver.class);
+        Felix felixMock = Mockito.mock(Felix.class);
+        Mockito.when(felixMock.getConfig()).thenReturn(config);
+        Mockito.when(felixMock.getResolver()).thenReturn(mockResolver);
+        Mockito.when(felixMock.getBundles()).thenReturn(new Bundle[]
+        {
+            differentBundle, identicalBundle
+        });
+
+        // Mock the archive of the bundle being installed
+        Map<String, String> headerMap = new HashMap<String, String>();
+        headerMap.put(Constants.BUNDLE_SYMBOLICNAME, "foo");
+        headerMap.put(Constants.BUNDLE_VERSION, "1.2.1.a");
+        headerMap.put(Constants.BUNDLE_MANIFESTVERSION, "2");
+
+        BundleArchiveRevision archiveRevision = Mockito.mock(BundleArchiveRevision.class);
+        Mockito.when(archiveRevision.getManifestHeader()).thenReturn(headerMap);
+
+        BundleArchive archive = Mockito.mock(BundleArchive.class);
+        Mockito.when(archive.getCurrentRevision()).thenReturn(archiveRevision);
+        Mockito.when(archive.getId()).thenReturn(3L);
+
+        BundleImpl bi = new BundleImpl(felixMock, null, archive);
+        assertEquals(3L, bi.getBundleId());
+    }
+
+    public void testNoCollisionHook() throws Exception
+    {
+        BundleImpl identicalBundle = mockBundleImpl(1L, "foo", "1.2.1.a");
+        BundleImpl differentBundle = mockBundleImpl(2L, "bar", "1.2.1.a");
+
+        // Mock the framework
+        StatefulResolver mockResolver = Mockito.mock(StatefulResolver.class);
+        Felix felixMock = Mockito.mock(Felix.class);
+        Mockito.when(felixMock.getResolver()).thenReturn(mockResolver);
+        Mockito.when(felixMock.getBundles()).thenReturn(new Bundle[]
+        {
+            differentBundle, identicalBundle
+        });
+
+        // Mock the archive of the bundle being installed
+        Map<String, String> headerMap = new HashMap<String, String>();
+        headerMap.put(Constants.BUNDLE_SYMBOLICNAME, "foo");
+        headerMap.put(Constants.BUNDLE_VERSION, "1.2.1.a");
+        headerMap.put(Constants.BUNDLE_MANIFESTVERSION, "2");
+
+        BundleArchiveRevision archiveRevision = Mockito.mock(BundleArchiveRevision.class);
+        Mockito.when(archiveRevision.getManifestHeader()).thenReturn(headerMap);
+
+        BundleArchive archive = Mockito.mock(BundleArchive.class);
+        Mockito.when(archive.getCurrentRevision()).thenReturn(archiveRevision);
+        Mockito.when(archive.getId()).thenReturn(3L);
+
+        try
+        {
+            new BundleImpl(felixMock, null, archive);
+            fail("Should have thrown a BundleException because the installed bundle is not unique");
+        }
+        catch (BundleException be)
+        {
+            // good
+            assertTrue(be.getMessage().contains("not unique"));
+        }
+    }
+
+    private BundleImpl mockBundleImpl(long id, String bsn, String version)
+    {
+        BundleImpl identicalBundle = Mockito.mock(BundleImpl.class);
+        Mockito.when(identicalBundle.getSymbolicName()).thenReturn(bsn);
+        Mockito.when(identicalBundle.getVersion()).thenReturn(Version.parseVersion(version));
+        Mockito.when(identicalBundle.getBundleId()).thenReturn(id);
+        return identicalBundle;
+    }
+}
diff --git a/src/test/java/org/apache/felix/framework/FilterTest.java b/src/test/java/org/apache/felix/framework/FilterTest.java
new file mode 100644
index 0000000..ceb1114
--- /dev/null
+++ b/src/test/java/org/apache/felix/framework/FilterTest.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+import junit.framework.TestCase;
+import org.osgi.framework.Filter;
+import org.osgi.framework.FrameworkUtil;
+
+public class FilterTest extends TestCase
+{
+    public void testMissingAttribute()
+    {
+        Dictionary dict = new Hashtable();
+        dict.put("one", "one-value");
+        dict.put("two", "two-value");
+        dict.put("three", "three-value");
+        Filter filter = null;
+        try
+        {
+            filter = FrameworkUtil.createFilter("(missing=value)");
+        }
+        catch (Exception ex)
+        {
+            assertTrue("Filter should parse: " + ex, false);
+        }
+        assertFalse("Filter should not match: " + filter, filter.match(dict));
+    }
+}
diff --git a/src/test/java/org/apache/felix/framework/RequirementsCapabilitiesTest.java b/src/test/java/org/apache/felix/framework/RequirementsCapabilitiesTest.java
new file mode 100644
index 0000000..8cc34e6
--- /dev/null
+++ b/src/test/java/org/apache/felix/framework/RequirementsCapabilitiesTest.java
@@ -0,0 +1,267 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.jar.JarOutputStream;
+import java.util.jar.Manifest;
+
+import junit.framework.TestCase;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Version;
+import org.osgi.framework.launch.Framework;
+import org.osgi.framework.wiring.BundleCapability;
+import org.osgi.framework.wiring.BundleRevision;
+import org.osgi.framework.wiring.BundleWiring;
+import org.osgi.resource.Capability;
+import org.osgi.resource.Resource;
+
+public class RequirementsCapabilitiesTest extends TestCase
+{
+    private File tempDir;
+    private Framework felix;
+    private File cacheDir;
+
+    @Override
+    protected void setUp() throws Exception
+    {
+        super.setUp();
+        tempDir = File.createTempFile("felix-temp", ".dir");
+        assertTrue("precondition", tempDir.delete());
+        assertTrue("precondition", tempDir.mkdirs());
+
+        cacheDir = new File(tempDir, "felix-cache");
+        assertTrue("precondition", cacheDir.mkdir());
+
+        String cache = cacheDir.getPath();
+
+        Map<String,String> params = new HashMap<String, String>();
+        params.put("felix.cache.profiledir", cache);
+        params.put("felix.cache.dir", cache);
+        params.put(Constants.FRAMEWORK_STORAGE, cache);
+
+        felix = new Felix(params);
+        felix.init();
+        felix.start();
+    }
+
+    @Override
+    protected void tearDown() throws Exception
+    {
+        super.tearDown();
+
+        felix.stop(); // Note that this method is async
+        felix = null;
+
+        deleteDir(tempDir);
+        tempDir = null;
+        cacheDir = null;
+    }
+
+    public void testIdentityCapabilityBundleFragment() throws Exception
+    {
+        String bmf = "Bundle-SymbolicName: cap.bundle\n"
+                + "Bundle-Version: 1.2.3.Blah\n"
+                + "Bundle-ManifestVersion: 2\n"
+                + "Import-Package: org.osgi.framework\n";
+        File bundleFile = createBundle(bmf);
+
+        String fmf = "Bundle-SymbolicName: cap.frag\n"
+                + "Bundle-Version: 1.0.0\n"
+                + "Fragment-Host: cap.bundle\n"
+                + "Bundle-ManifestVersion: 2\n"
+                + "Export-Package: org.foo.bar;version=\"2.0.0\"\n"
+                + "Import-Package: org.osgi.util.tracker\n";
+        File fragFile = createBundle(fmf);
+
+        Bundle b = felix.getBundleContext().installBundle(bundleFile.toURI().toASCIIString());
+        Bundle f = felix.getBundleContext().installBundle(fragFile.toURI().toASCIIString());
+
+        // Check the bundle capabilities.
+        // First check the capabilities on the Bundle Revision, which is available on installed bundles
+        BundleRevision bbr = b.adapt(BundleRevision.class);
+        List<Capability> bwbCaps = bbr.getCapabilities("osgi.wiring.bundle");
+        assertEquals(1, bwbCaps.size());
+
+        Map<String, Object> expectedBWBAttrs = new HashMap<String, Object>();
+        expectedBWBAttrs.put("osgi.wiring.bundle", "cap.bundle");
+        expectedBWBAttrs.put("bundle-version", Version.parseVersion("1.2.3.Blah"));
+        Capability expectedBWBCap = new TestCapability("osgi.wiring.bundle",
+                expectedBWBAttrs, Collections.<String, String>emptyMap());
+        assertCapsEquals(expectedBWBCap, bwbCaps.get(0));
+
+        List<Capability> bwhCaps = bbr.getCapabilities("osgi.wiring.host");
+        assertEquals(1, bwhCaps.size());
+
+        Map<String, Object> expectedBWHAttrs = new HashMap<String, Object>();
+        expectedBWHAttrs.put("osgi.wiring.host", "cap.bundle");
+        expectedBWHAttrs.put("bundle-version", Version.parseVersion("1.2.3.Blah"));
+        Capability expectedBWHCap = new TestCapability("osgi.wiring.host",
+                expectedBWHAttrs, Collections.<String, String>emptyMap());
+        assertCapsEquals(expectedBWHCap, bwhCaps.get(0));
+
+        List<Capability> bwiCaps = bbr.getCapabilities("osgi.identity");
+        assertEquals(1, bwiCaps.size());
+
+        Map<String, Object> expectedBWIAttrs = new HashMap<String, Object>();
+        expectedBWIAttrs.put("osgi.identity", "cap.bundle");
+        expectedBWIAttrs.put("type", "osgi.bundle");
+        expectedBWIAttrs.put("version", Version.parseVersion("1.2.3.Blah"));
+        Capability expectedBWICap = new TestCapability("osgi.identity",
+                expectedBWIAttrs, Collections.<String, String>emptyMap());
+        assertCapsEquals(expectedBWICap, bwiCaps.get(0));
+
+        assertEquals("The Bundle should not directly expose osgi.wiring.package",
+                0, bbr.getCapabilities("osgi.wiring.package").size());
+
+        // Check the fragment's capabilities.
+        // First check the capabilities on the Bundle Revision, which is available on installed fragments
+        BundleRevision fbr = f.adapt(BundleRevision.class);
+        List<Capability> fwpCaps = fbr.getCapabilities("osgi.wiring.package");
+        assertEquals(1, fwpCaps.size());
+
+        Map<String, Object> expectedFWAttrs = new HashMap<String, Object>();
+        expectedFWAttrs.put("osgi.wiring.package", "org.foo.bar");
+        expectedFWAttrs.put("version", Version.parseVersion("2"));
+        expectedFWAttrs.put("bundle-symbolic-name", "cap.frag");
+        expectedFWAttrs.put("bundle-version", Version.parseVersion("1.0.0"));
+        Capability expectedFWCap = new TestCapability("osgi.wiring.package",
+                expectedFWAttrs, Collections.<String, String>emptyMap());
+        assertCapsEquals(expectedFWCap, fwpCaps.get(0));
+
+        List<Capability> fiCaps = fbr.getCapabilities("osgi.identity");
+        assertEquals(1, fiCaps.size());
+        Map<String, Object> expectedFIAttrs = new HashMap<String, Object>();
+        expectedFIAttrs.put("osgi.identity", "cap.frag");
+        expectedFIAttrs.put("type", "osgi.fragment");
+        expectedFIAttrs.put("version", Version.parseVersion("1.0.0"));
+        Capability expectedFICap = new TestCapability("osgi.identity",
+                expectedFIAttrs, Collections.<String, String>emptyMap());
+        assertCapsEquals(expectedFICap, fiCaps.get(0));
+
+        // Start the bundle. This will make the BundleWiring available on both the bundle and the fragment
+        b.start();
+
+        // Check the Bundle Wiring on the fragment. It should only contain the osgi.identity capability
+        // All the other capabilities should have migrated to the bundle's BundleWiring.
+        BundleWiring fbw = f.adapt(BundleWiring.class);
+        List<BundleCapability> fbwCaps = fbw.getCapabilities(null);
+        assertEquals("Fragment should only have 1 capability: it's osgi.identity", 1, fbwCaps.size());
+        assertCapsEquals(expectedFICap, fbwCaps.get(0));
+
+        // Check the Bundle Wiring on the bundle. It should contain all the capabilities originally on the
+        // bundle and also contain the osgi.wiring.package capability from the fragment.
+        BundleWiring bbw = b.adapt(BundleWiring.class);
+        List<BundleCapability> bwbCaps2 = bbw.getCapabilities("osgi.wiring.bundle");
+        assertEquals(1, bwbCaps2.size());
+        assertCapsEquals(expectedBWBCap, bwbCaps2.get(0));
+        List<BundleCapability> bwhCaps2 = bbw.getCapabilities("osgi.wiring.host");
+        assertEquals(1, bwhCaps2.size());
+        assertCapsEquals(expectedBWHCap, bwhCaps2.get(0));
+        List<BundleCapability> bwiCaps2 = bbw.getCapabilities("osgi.identity");
+        assertEquals(1, bwiCaps2.size());
+        assertCapsEquals(expectedBWICap, bwiCaps2.get(0));
+        List<BundleCapability> bwpCaps2 = bbw.getCapabilities("osgi.wiring.package");
+        assertEquals("Bundle should have inherited the osgi.wiring.package capability from the fragment",
+                1, bwpCaps2.size());
+        assertCapsEquals(expectedFWCap, bwpCaps2.get(0));
+    }
+
+    private File createBundle(String manifest) throws IOException
+    {
+        File f = File.createTempFile("felix-bundle", ".jar", tempDir);
+
+        Manifest mf = new Manifest(new ByteArrayInputStream(manifest.getBytes("utf-8")));
+        mf.getMainAttributes().putValue("Manifest-Version", "1.0");
+        JarOutputStream os = new JarOutputStream(new FileOutputStream(f), mf);
+        os.close();
+        return f;
+    }
+
+    private static void assertCapsEquals(Capability expected, Capability actual)
+    {
+        assertEquals(expected.getNamespace(), actual.getNamespace());
+        assertSubMap(expected.getAttributes(), actual.getAttributes());
+        assertSubMap(expected.getDirectives(), actual.getDirectives());
+        // We ignore the resource in the comparison
+    }
+
+    private static void assertSubMap(Map<?,?> subMap, Map<?,?> fullMap)
+    {
+        for (Map.Entry<?,?> entry : subMap.entrySet())
+        {
+            assertEquals(entry.getValue(), fullMap.get(entry.getKey()));
+        }
+    }
+
+    private static void deleteDir(File root) throws IOException
+    {
+        if (root.isDirectory())
+        {
+            for (File file : root.listFiles())
+            {
+                deleteDir(file);
+            }
+        }
+        assertTrue(root.delete());
+    }
+
+    static class TestCapability implements Capability
+    {
+        private final String namespace;
+        private final Map<String, Object> attributes;
+        private final Map<String, String> directives;
+
+        TestCapability(String ns, Map<String,Object> attrs, Map<String,String> dirs)
+        {
+            namespace = ns;
+            attributes = attrs;
+            directives = dirs;
+        }
+
+        public String getNamespace()
+        {
+            return namespace;
+        }
+
+        public Map<String, Object> getAttributes()
+        {
+            return attributes;
+        }
+
+        public Map<String, String> getDirectives()
+        {
+            return directives;
+        }
+
+        public Resource getResource()
+        {
+            return null;
+        }
+    }
+}
diff --git a/src/test/java/org/apache/felix/framework/ServiceRegistryTest.java b/src/test/java/org/apache/felix/framework/ServiceRegistryTest.java
new file mode 100644
index 0000000..6f3b92f
--- /dev/null
+++ b/src/test/java/org/apache/felix/framework/ServiceRegistryTest.java
@@ -0,0 +1,334 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework;
+
+import java.util.Collection;
+import java.util.Hashtable;
+
+import junit.framework.TestCase;
+
+import org.easymock.MockControl;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceFactory;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.framework.hooks.service.EventHook;
+import org.osgi.framework.hooks.service.FindHook;
+import org.osgi.framework.hooks.service.ListenerHook;
+
+public class ServiceRegistryTest extends TestCase
+{
+    public void testRegisterEventHookService()
+    {
+        MockControl control = MockControl.createNiceControl(Bundle.class);
+        Bundle b = (Bundle) control.getMock();
+        control.replay();
+
+        MockControl controlContext = MockControl.createNiceControl(BundleContext.class);
+        BundleContext c = (BundleContext) controlContext.getMock();
+        controlContext.expectAndReturn(c.getBundle(), b);
+        controlContext.replay();
+
+        ServiceRegistry sr = new ServiceRegistry(new Logger(), null);
+        EventHook hook = new EventHook()
+        {
+            public void event(ServiceEvent event, Collection contexts)
+            {
+            }
+        };
+
+        assertEquals("Precondition failed", 0, sr.getHooks(EventHook.class).size());
+        assertEquals("Precondition failed", 0, sr.getHooks(FindHook.class).size());
+        assertEquals("Precondition failed", 0, sr.getHooks(ListenerHook.class).size());
+        ServiceRegistration reg = sr.registerService(c, new String [] {EventHook.class.getName()}, hook, new Hashtable());
+        assertEquals(1, sr.getHooks(EventHook.class).size());
+        assertTrue(sr.getHooks(EventHook.class).iterator().next() instanceof ServiceReference);
+        assertSame(reg.getReference(), sr.getHooks(EventHook.class).iterator().next());
+        assertSame(hook, ((ServiceRegistrationImpl) reg).getService());
+        assertEquals("Postcondition failed", 0, sr.getHooks(FindHook.class).size());
+        assertEquals("Postcondition failed", 0, sr.getHooks(ListenerHook.class).size());
+
+        sr.unregisterService(b, reg);
+        assertEquals("Should be no hooks left after unregistration", 0, sr.getHooks(EventHook.class).size());
+        assertEquals("Should be no hooks left after unregistration", 0, sr.getHooks(FindHook.class).size());
+        assertEquals("Should be no hooks left after unregistration", 0, sr.getHooks(ListenerHook.class).size());
+    }
+
+    public void testRegisterEventHookServiceFactory()
+    {
+        MockControl control = MockControl.createNiceControl(Bundle.class);
+        Bundle b = (Bundle) control.getMock();
+        control.replay();
+
+        MockControl controlContext = MockControl.createNiceControl(BundleContext.class);
+        BundleContext c = (BundleContext) controlContext.getMock();
+        controlContext.expectAndReturn(c.getBundle(), b);
+        controlContext.replay();
+
+        ServiceRegistry sr = new ServiceRegistry(new Logger(), null);
+        MockControl sfControl = MockControl.createNiceControl(ServiceFactory.class);
+        sfControl.replay();
+        ServiceFactory sf = (ServiceFactory) sfControl.getMock();
+
+        assertEquals("Precondition failed", 0, sr.getHooks(EventHook.class).size());
+        assertEquals("Precondition failed", 0, sr.getHooks(FindHook.class).size());
+        assertEquals("Precondition failed", 0, sr.getHooks(ListenerHook.class).size());
+        ServiceRegistration reg = sr.registerService(c, new String [] {EventHook.class.getName()}, sf, new Hashtable());
+        assertEquals(1, sr.getHooks(EventHook.class).size());
+        assertSame(reg.getReference(), sr.getHooks(EventHook.class).iterator().next());
+        assertSame(sf, ((ServiceRegistrationImpl) reg).getService());
+        assertEquals("Postcondition failed", 0, sr.getHooks(FindHook.class).size());
+        assertEquals("Postcondition failed", 0, sr.getHooks(ListenerHook.class).size());
+
+        sr.unregisterService(b, reg);
+        assertEquals("Should be no hooks left after unregistration", 0, sr.getHooks(EventHook.class).size());
+        assertEquals("Should be no hooks left after unregistration", 0, sr.getHooks(FindHook.class).size());
+        assertEquals("Should be no hooks left after unregistration", 0, sr.getHooks(ListenerHook.class).size());
+    }
+
+    public void testRegisterFindHookService()
+    {
+        MockControl control = MockControl.createNiceControl(Bundle.class);
+        Bundle b = (Bundle) control.getMock();
+        control.replay();
+
+        MockControl controlContext = MockControl.createNiceControl(BundleContext.class);
+        BundleContext c = (BundleContext) controlContext.getMock();
+        controlContext.expectAndReturn(c.getBundle(), b);
+        controlContext.replay();
+
+        ServiceRegistry sr = new ServiceRegistry(new Logger(), null);
+        FindHook hook = new FindHook()
+        {
+            public void find(BundleContext context, String name, String filter,
+                boolean allServices, Collection references)
+            {
+            }
+        };
+
+        assertEquals("Precondition failed", 0, sr.getHooks(EventHook.class).size());
+        assertEquals("Precondition failed", 0, sr.getHooks(FindHook.class).size());
+        assertEquals("Precondition failed", 0, sr.getHooks(ListenerHook.class).size());
+        ServiceRegistration reg = sr.registerService(c, new String [] {FindHook.class.getName()}, hook, new Hashtable());
+        assertEquals(1, sr.getHooks(FindHook.class).size());
+        assertSame(reg.getReference(), sr.getHooks(FindHook.class).iterator().next());
+        assertSame(hook, ((ServiceRegistrationImpl) reg).getService());
+        assertEquals("Postcondition failed", 0, sr.getHooks(EventHook.class).size());
+        assertEquals("Postcondition failed", 0, sr.getHooks(ListenerHook.class).size());
+
+        sr.unregisterService(b, reg);
+        assertEquals("Should be no hooks left after unregistration", 0, sr.getHooks(EventHook.class).size());
+        assertEquals("Should be no hooks left after unregistration", 0, sr.getHooks(FindHook.class).size());
+        assertEquals("Should be no hooks left after unregistration", 0, sr.getHooks(ListenerHook.class).size());
+    }
+
+    public void testRegisterFindHookServiceFactory()
+    {
+        MockControl control = MockControl.createNiceControl(Bundle.class);
+        Bundle b = (Bundle) control.getMock();
+        control.replay();
+
+        MockControl controlContext = MockControl.createNiceControl(BundleContext.class);
+        BundleContext c = (BundleContext) controlContext.getMock();
+        controlContext.expectAndReturn(c.getBundle(), b);
+        controlContext.replay();
+
+        ServiceRegistry sr = new ServiceRegistry(new Logger(), null);
+        MockControl sfControl = MockControl.createNiceControl(ServiceFactory.class);
+        sfControl.replay();
+        ServiceFactory sf = (ServiceFactory) sfControl.getMock();
+
+        assertEquals("Precondition failed", 0, sr.getHooks(EventHook.class).size());
+        assertEquals("Precondition failed", 0, sr.getHooks(FindHook.class).size());
+        assertEquals("Precondition failed", 0, sr.getHooks(ListenerHook.class).size());
+        ServiceRegistration reg = sr.registerService(c, new String [] {FindHook.class.getName()}, sf, new Hashtable());
+        assertEquals(1, sr.getHooks(FindHook.class).size());
+        assertSame(reg.getReference(), sr.getHooks(FindHook.class).iterator().next());
+        assertSame(sf, ((ServiceRegistrationImpl) reg).getService());
+        assertEquals("Postcondition failed", 0, sr.getHooks(EventHook.class).size());
+        assertEquals("Postcondition failed", 0, sr.getHooks(ListenerHook.class).size());
+
+        sr.unregisterService(b, reg);
+        assertEquals("Should be no hooks left after unregistration", 0, sr.getHooks(EventHook.class).size());
+        assertEquals("Should be no hooks left after unregistration", 0, sr.getHooks(FindHook.class).size());
+        assertEquals("Should be no hooks left after unregistration", 0, sr.getHooks(ListenerHook.class).size());
+    }
+
+    public void testRegisterListenerHookService()
+    {
+        MockControl control = MockControl.createNiceControl(Bundle.class);
+        Bundle b = (Bundle) control.getMock();
+        control.replay();
+
+        MockControl controlContext = MockControl.createNiceControl(BundleContext.class);
+        BundleContext c = (BundleContext) controlContext.getMock();
+        controlContext.expectAndReturn(c.getBundle(), b);
+        controlContext.replay();
+
+        ServiceRegistry sr = new ServiceRegistry(new Logger(), null);
+        ListenerHook hook = new ListenerHook()
+        {
+            public void added(Collection listeners)
+            {
+            }
+
+            public void removed(Collection listener)
+            {
+            }
+        };
+
+        assertEquals("Precondition failed", 0, sr.getHooks(EventHook.class).size());
+        assertEquals("Precondition failed", 0, sr.getHooks(FindHook.class).size());
+        assertEquals("Precondition failed", 0, sr.getHooks(ListenerHook.class).size());
+        ServiceRegistration reg = sr.registerService(c, new String [] {ListenerHook.class.getName()}, hook, new Hashtable());
+        assertEquals(1, sr.getHooks(ListenerHook.class).size());
+        assertSame(reg.getReference(), sr.getHooks(ListenerHook.class).iterator().next());
+        assertSame(hook, ((ServiceRegistrationImpl) reg).getService());
+        assertEquals("Postcondition failed", 0, sr.getHooks(EventHook.class).size());
+        assertEquals("Postcondition failed", 0, sr.getHooks(FindHook.class).size());
+
+        sr.unregisterService(b, reg);
+        assertEquals("Should be no hooks left after unregistration", 0, sr.getHooks(EventHook.class).size());
+        assertEquals("Should be no hooks left after unregistration", 0, sr.getHooks(FindHook.class).size());
+        assertEquals("Should be no hooks left after unregistration", 0, sr.getHooks(ListenerHook.class).size());
+    }
+
+    public void testRegisterListenerHookServiceFactory()
+    {
+        MockControl control = MockControl.createNiceControl(Bundle.class);
+        Bundle b = (Bundle) control.getMock();
+        control.replay();
+
+        MockControl controlContext = MockControl.createNiceControl(BundleContext.class);
+        BundleContext c = (BundleContext) controlContext.getMock();
+        controlContext.expectAndReturn(c.getBundle(), b);
+        controlContext.replay();
+
+        ServiceRegistry sr = new ServiceRegistry(new Logger(), null);
+        MockControl sfControl = MockControl.createNiceControl(ServiceFactory.class);
+        sfControl.replay();
+        ServiceFactory sf = (ServiceFactory) sfControl.getMock();
+
+        assertEquals("Precondition failed", 0, sr.getHooks(EventHook.class).size());
+        assertEquals("Precondition failed", 0, sr.getHooks(FindHook.class).size());
+        assertEquals("Precondition failed", 0, sr.getHooks(ListenerHook.class).size());
+        ServiceRegistration reg = sr.registerService(c, new String [] {ListenerHook.class.getName()}, sf, new Hashtable());
+        assertEquals(1, sr.getHooks(ListenerHook.class).size());
+        assertSame(reg.getReference(), sr.getHooks(ListenerHook.class).iterator().next());
+        assertSame(sf, ((ServiceRegistrationImpl) reg).getService());
+        assertEquals("Postcondition failed", 0, sr.getHooks(EventHook.class).size());
+        assertEquals("Postcondition failed", 0, sr.getHooks(FindHook.class).size());
+
+        sr.unregisterService(b, reg);
+        assertEquals("Should be no hooks left after unregistration", 0, sr.getHooks(EventHook.class).size());
+        assertEquals("Should be no hooks left after unregistration", 0, sr.getHooks(FindHook.class).size());
+        assertEquals("Should be no hooks left after unregistration", 0, sr.getHooks(ListenerHook.class).size());
+    }
+
+    public void testRegisterCombinedService()
+    {
+        MockControl control = MockControl.createNiceControl(Bundle.class);
+        Bundle b = (Bundle) control.getMock();
+        control.replay();
+
+        MockControl controlContext = MockControl.createNiceControl(BundleContext.class);
+        BundleContext c = (BundleContext) controlContext.getMock();
+        controlContext.expectAndReturn(c.getBundle(), b);
+        controlContext.replay();
+
+        ServiceRegistry sr = new ServiceRegistry(new Logger(), null);
+        class CombinedService implements ListenerHook, FindHook, EventHook, Runnable
+        {
+            public void added(Collection listeners)
+            {
+            }
+
+            public void removed(Collection listener)
+            {
+            }
+
+            public void find(BundleContext context, String name, String filter,
+                    boolean allServices, Collection references)
+            {
+            }
+
+            public void event(ServiceEvent event, Collection contexts)
+            {
+            }
+
+            public void run()
+            {
+            }
+
+        }
+        CombinedService hook = new CombinedService();
+
+        assertEquals("Precondition failed", 0, sr.getHooks(EventHook.class).size());
+        assertEquals("Precondition failed", 0, sr.getHooks(FindHook.class).size());
+        assertEquals("Precondition failed", 0, sr.getHooks(ListenerHook.class).size());
+        ServiceRegistration reg = sr.registerService(c, new String [] {
+            Runnable.class.getName(),
+            ListenerHook.class.getName(),
+            FindHook.class.getName(),
+            EventHook.class.getName()}, hook, new Hashtable());
+        assertEquals(1, sr.getHooks(ListenerHook.class).size());
+        assertSame(reg.getReference(), sr.getHooks(ListenerHook.class).iterator().next());
+        assertSame(hook, ((ServiceRegistrationImpl) reg).getService());
+        assertEquals(1, sr.getHooks(EventHook.class).size());
+        assertSame(reg.getReference(), sr.getHooks(EventHook.class).iterator().next());
+        assertSame(hook, ((ServiceRegistrationImpl) reg).getService());
+        assertEquals(1, sr.getHooks(FindHook.class).size());
+        assertSame(reg.getReference(), sr.getHooks(FindHook.class).iterator().next());
+        assertSame(hook, ((ServiceRegistrationImpl) reg).getService());
+
+        sr.unregisterService(b, reg);
+        assertEquals("Should be no hooks left after unregistration", 0, sr.getHooks(EventHook.class).size());
+        assertEquals("Should be no hooks left after unregistration", 0, sr.getHooks(FindHook.class).size());
+        assertEquals("Should be no hooks left after unregistration", 0, sr.getHooks(ListenerHook.class).size());
+    }
+
+    public void testRegisterPlainService()
+    {
+        MockControl control = MockControl.createNiceControl(Bundle.class);
+        Bundle b = (Bundle) control.getMock();
+        control.replay();
+
+        MockControl controlContext = MockControl.createNiceControl(BundleContext.class);
+        BundleContext c = (BundleContext) controlContext.getMock();
+        controlContext.expectAndReturn(c.getBundle(), b);
+        controlContext.replay();
+
+        ServiceRegistry sr = new ServiceRegistry(new Logger(), null);
+        String svcObj = "hello";
+        assertEquals("Precondition failed", 0, sr.getHooks(EventHook.class).size());
+        assertEquals("Precondition failed", 0, sr.getHooks(FindHook.class).size());
+        assertEquals("Precondition failed", 0, sr.getHooks(ListenerHook.class).size());
+        ServiceRegistration reg = sr.registerService(c, new String [] {String.class.getName()}, svcObj, new Hashtable());
+        assertEquals("Postcondition failed", 0, sr.getHooks(EventHook.class).size());
+        assertEquals("Postcondition failed", 0, sr.getHooks(FindHook.class).size());
+        assertEquals("Postcondition failed", 0, sr.getHooks(ListenerHook.class).size());
+
+        sr.unregisterService(b, reg);
+        assertEquals("Unregistration should have no effect", 0, sr.getHooks(EventHook.class).size());
+        assertEquals("Unregistration should have no effect", 0, sr.getHooks(FindHook.class).size());
+        assertEquals("Unregistration should have no effect", 0, sr.getHooks(ListenerHook.class).size());
+    }
+}
diff --git a/src/test/java/org/apache/felix/framework/StartStopBundleTest.java b/src/test/java/org/apache/felix/framework/StartStopBundleTest.java
new file mode 100644
index 0000000..315606c
--- /dev/null
+++ b/src/test/java/org/apache/felix/framework/StartStopBundleTest.java
@@ -0,0 +1,171 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.jar.JarOutputStream;
+import java.util.jar.Manifest;
+import java.util.zip.ZipEntry;
+
+import junit.framework.TestCase;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.Constants;
+import org.osgi.framework.launch.Framework;
+
+public class StartStopBundleTest extends TestCase
+{
+    public static final int DELAY = 1000;
+
+    public void testStartStopBundle() throws Exception
+    {
+        Map params = new HashMap();
+        params.put(Constants.FRAMEWORK_SYSTEMPACKAGES,
+            "org.osgi.framework; version=1.4.0,"
+            + "org.osgi.service.packageadmin; version=1.2.0,"
+            + "org.osgi.service.startlevel; version=1.1.0,"
+            + "org.osgi.util.tracker; version=1.3.3,"
+            + "org.osgi.service.url; version=1.0.0");
+        File cacheDir = File.createTempFile("felix-cache", ".dir");
+        cacheDir.delete();
+        cacheDir.mkdirs();
+        String cache = cacheDir.getPath();
+        params.put("felix.cache.profiledir", cache);
+        params.put("felix.cache.dir", cache);
+        params.put(Constants.FRAMEWORK_STORAGE, cache);
+
+        String mf = "Bundle-SymbolicName: boot.test\n"
+            + "Bundle-Version: 1.1.0\n"
+            + "Bundle-ManifestVersion: 2\n"
+            + "Import-Package: org.osgi.framework\n";
+        File bundleFile = createBundle(mf, cacheDir);
+
+        Framework f = new Felix(params);
+        f.init();
+        f.start();
+
+        try {
+            final Bundle bundle = f.getBundleContext().installBundle(bundleFile.toURI().toString());
+
+            new Thread()
+            {
+                public void run()
+                {
+                    try
+                    {
+                        bundle.start();
+                    }
+                    catch (BundleException e)
+                    {
+                        e.printStackTrace();
+                    }
+                }
+            }.start();
+            Thread.sleep(DELAY / 4);
+            long t0 = System.currentTimeMillis();
+            bundle.stop();
+            long t1 = System.currentTimeMillis();
+
+            assertEquals(Bundle.RESOLVED, bundle.getState());
+            assertTrue((t1 - t0) > DELAY / 2);
+
+            bundle.start();
+
+            new Thread()
+            {
+                public void run()
+                {
+                    try
+                    {
+                        bundle.stop();
+                    }
+                    catch (BundleException e)
+                    {
+                        e.printStackTrace();
+                    }
+                }
+            }.start();
+            Thread.sleep(DELAY / 4);
+            t0 = System.currentTimeMillis();
+            bundle.start();
+            t1 = System.currentTimeMillis();
+
+            assertEquals(Bundle.ACTIVE, bundle.getState());
+            assertTrue((t1 - t0) > DELAY / 2);
+        } finally {
+            f.stop();
+            deleteDir(cacheDir);
+        }
+    }
+
+    private static File createBundle(String manifest, File tempDir) throws IOException
+    {
+        File f = File.createTempFile("felix-bundle", ".jar", tempDir);
+
+        Manifest mf = new Manifest(new ByteArrayInputStream(manifest.getBytes("utf-8")));
+        mf.getMainAttributes().putValue("Manifest-Version", "1.0");
+        mf.getMainAttributes().putValue(Constants.BUNDLE_ACTIVATOR, TestBundleActivator.class.getName());
+        JarOutputStream os = new JarOutputStream(new FileOutputStream(f), mf);
+
+        String path = TestBundleActivator.class.getName().replace('.', '/') + ".class";
+        os.putNextEntry(new ZipEntry(path));
+
+        InputStream is = TestBundleActivator.class.getClassLoader().getResourceAsStream(path);
+        byte[] b = new byte[is.available()];
+        is.read(b);
+        is.close();
+        os.write(b);
+
+        os.close();
+        return f;
+    }
+
+    private static void deleteDir(File root) throws IOException
+    {
+        if (root.isDirectory())
+        {
+            for (File file : root.listFiles())
+            {
+                deleteDir(file);
+            }
+        }
+        assertTrue(root.delete());
+    }
+   public static class TestBundleActivator implements BundleActivator
+    {
+        public void start(BundleContext context) throws Exception
+        {
+            Thread.sleep(DELAY);
+        }
+
+        public void stop(BundleContext context) throws Exception
+        {
+            Thread.sleep(DELAY);
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/org/apache/felix/framework/URLHandlersTest.java b/src/test/java/org/apache/felix/framework/URLHandlersTest.java
new file mode 100644
index 0000000..42f45e4
--- /dev/null
+++ b/src/test/java/org/apache/felix/framework/URLHandlersTest.java
@@ -0,0 +1,385 @@
+/*
+ * Copyright 2013 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.framework;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.InetAddress;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.net.URLConnection;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.jar.JarOutputStream;
+import java.util.jar.Manifest;
+import java.util.zip.ZipEntry;
+import junit.framework.TestCase;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.framework.launch.Framework;
+import org.osgi.service.url.URLConstants;
+import org.osgi.service.url.URLStreamHandlerService;
+import org.osgi.service.url.URLStreamHandlerSetter;
+
+/**
+ *
+ * @author pauls
+ */
+public class URLHandlersTest extends TestCase
+{
+    public void testURLHandlers() throws Exception
+    {
+        String mf = "Bundle-SymbolicName: url.test\n"
+            + "Bundle-Version: 1.0.0\n"
+            + "Bundle-ManifestVersion: 2\n"
+            + "Import-Package: org.osgi.framework,org.osgi.service.url\n"
+            + "Manifest-Version: 1.0\n"
+            + Constants.BUNDLE_ACTIVATOR + ": " + TestURLHandlersActivator.class.getName() + "\n\n";
+
+        File bundleFile = createBundle(mf, TestURLHandlersActivator.class);
+
+        Framework f = createFramework();
+        f.init();
+        f.start();
+
+        try
+        {
+            final Bundle bundle = f.getBundleContext().installBundle(bundleFile.toURI().toString());
+            bundle.start();
+        }
+        finally
+        {
+            try
+            {
+                f.stop();
+            }
+            catch (Throwable t)
+            {
+            }
+        }
+    }
+
+    public void testURLHandlersWithClassLoaderIsolation() throws Exception
+    {
+        DelegatingClassLoader cl1 = new DelegatingClassLoader(this.getClass().getClassLoader());
+        DelegatingClassLoader cl2 = new DelegatingClassLoader(this.getClass().getClassLoader());
+
+        Framework f = createFramework();
+        f.init();
+        f.start();
+
+        String mf = "Bundle-SymbolicName: url.test\n"
+            + "Bundle-Version: 1.0.0\n"
+            + "Bundle-ManifestVersion: 2\n"
+            + "Import-Package: org.osgi.framework\n"
+            + "Manifest-Version: 1.0\n"
+            + Constants.BUNDLE_ACTIVATOR + ": " + TestURLHandlersActivator.class.getName();
+
+        File bundleFile = createBundle(mf, TestURLHandlersActivator.class);
+
+        final Bundle bundle = f.getBundleContext().installBundle(bundleFile.toURI().toString());
+        bundle.start();
+
+        Class clazz1 = cl1.loadClass(URLHandlersTest.class.getName());
+
+        clazz1.getMethod("testURLHandlers").invoke(clazz1.newInstance());
+
+        bundle.stop();
+        bundle.start();
+        Class clazz2 = cl2.loadClass(URLHandlersTest.class.getName());
+
+        clazz2.getMethod("testURLHandlers").invoke(clazz2.newInstance());
+        bundle.stop();
+        bundle.start();
+        f.stop();
+    }
+
+    public static class DelegatingClassLoader extends ClassLoader
+    {
+        private final Object m_lock = new Object();
+        private final ClassLoader m_source;
+
+        public DelegatingClassLoader(ClassLoader source)
+        {
+            m_source = source;
+        }
+
+        @Override
+        public Class<?> loadClass(String name) throws ClassNotFoundException
+        {
+            synchronized (m_lock)
+            {
+                Class<?> result = findLoadedClass(name);
+                if (result != null)
+                {
+                    return result;
+                }
+            }
+            if (!name.startsWith("org.apache.felix") && !name.startsWith("org.osgi."))
+            {
+                return m_source.loadClass(name);
+            }
+            byte[] buffer = new byte[8 * 1024];
+            try
+            {
+                InputStream is = m_source.getResourceAsStream(name.replace('.', '/') + ".class");
+                ByteArrayOutputStream os = new ByteArrayOutputStream();
+                for (int i = is.read(buffer); i != -1; i = is.read(buffer))
+                {
+                    os.write(buffer, 0, i);
+
+                }
+                is.close();
+                os.close();
+                buffer = os.toByteArray();
+            }
+            catch (Exception ex)
+            {
+                throw new ClassNotFoundException("Unable to load class: " + name + " with cl: " + System.identityHashCode(this), ex);
+            }
+            return super.defineClass(name, buffer, 0, buffer.length, null);
+        }
+    }
+
+    public static class TestURLHandlersActivator implements BundleActivator, URLStreamHandlerService
+    {
+        private volatile ServiceRegistration m_reg = null;
+
+        public URLConnection openConnection(URL u) throws IOException
+        {
+            return null;//throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        public void parseURL(URLStreamHandlerSetter realHandler, URL u, String spec, int start, int limit)
+        {
+            realHandler.setURL(u, spec, spec, start, spec, spec, spec, spec, spec);
+            //throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        public String toExternalForm(URL u)
+        {
+            return u.toString();//throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        public boolean equals(URL u1, URL u2)
+        {
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        public int getDefaultPort()
+        {
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        public InetAddress getHostAddress(URL u)
+        {
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        public int hashCode(URL u)
+        {
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        public boolean hostsEqual(URL u1, URL u2)
+        {
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        public boolean sameFile(URL u1, URL u2)
+        {
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        public void start(final BundleContext context) throws Exception
+        {
+            try
+            {
+                new URL("test" + System.identityHashCode(TestURLHandlersActivator.this) + ":").openConnection();
+                throw new Exception("Unexpected url resolve");
+            }
+            catch (Exception ex)
+            {
+                // pass
+            }
+
+            Hashtable props = new Hashtable<String, String>();
+            props.put(URLConstants.URL_HANDLER_PROTOCOL, "test" + System.identityHashCode(TestURLHandlersActivator.this));
+
+            ServiceRegistration reg = context.registerService(URLStreamHandlerService.class, this, props);
+
+            new URL("test" + System.identityHashCode(TestURLHandlersActivator.this) + ":").openConnection();
+
+            reg.unregister();
+
+            try
+            {
+                new URL("test" + System.identityHashCode(TestURLHandlersActivator.this) + ":").openConnection();
+                throw new Exception("Unexpected url resolve");
+            }
+            catch (Exception ex)
+            {
+                // pass
+            }
+
+            Bundle bundle2 = null;
+            if (context.getBundle().getSymbolicName().equals("url.test"))
+            {
+
+                String mf = "Bundle-SymbolicName: url.test2\n"
+                    + "Bundle-Version: 1.0.0\n"
+                    + "Bundle-ManifestVersion: 2\n"
+                    + "Import-Package: org.osgi.framework,org.osgi.service.url\n"
+                    + "Manifest-Version: 1.0\n"
+                    + Constants.BUNDLE_ACTIVATOR + ": " + TestURLHandlersActivator.class.getName() + "\n\n";
+
+                File bundleFile = createBundle(mf, TestURLHandlersActivator.class);
+
+                bundle2 = context.installBundle(bundleFile.toURI().toURL().toString());
+            }
+            if (bundle2 != null)
+            {
+                try
+                {
+                    new URL("test" + System.identityHashCode(bundle2) + ":").openConnection();
+                    throw new Exception("Unexpected url2 resolve");
+                }
+                catch (Exception ex)
+                {
+                }
+                bundle2.start();
+                new URL("test" + System.identityHashCode(bundle2) + ":").openConnection();
+                bundle2.stop();
+                try
+                {
+                    new URL("test" + System.identityHashCode(bundle2) + ":").openConnection();
+                    throw new Exception("Unexpected url2 resolve");
+                }
+                catch (Exception ex)
+                {
+                }
+            }
+            else
+            {
+                try
+                {
+                    new URL("test" + System.identityHashCode(context.getBundle()) + ":").openConnection();
+                    throw new Exception("Unexpected url2 resolve");
+                }
+                catch (Exception ex)
+                {
+                }
+                props = new Hashtable();
+                props.put(URLConstants.URL_HANDLER_PROTOCOL, "test" + System.identityHashCode(context.getBundle()));
+                m_reg = context.registerService(URLStreamHandlerService.class, this, props);
+                new URL("test" + System.identityHashCode(context.getBundle()) + ":").openConnection();
+            }
+        }
+
+        private static File createBundle(String manifest, Class... classes) throws IOException
+        {
+            File f = File.createTempFile("felix-bundle", ".jar");
+            f.deleteOnExit();
+
+            Manifest mf = new Manifest(new ByteArrayInputStream(manifest.getBytes("utf-8")));
+            JarOutputStream os = new JarOutputStream(new FileOutputStream(f), mf);
+
+            for (Class clazz : classes)
+            {
+                String path = clazz.getName().replace('.', '/') + ".class";
+                os.putNextEntry(new ZipEntry(path));
+
+                InputStream is = clazz.getClassLoader().getResourceAsStream(path);
+                byte[] buffer = new byte[8 * 1024];
+                for (int i = is.read(buffer); i != -1; i = is.read(buffer))
+                {
+                    os.write(buffer, 0, i);
+                }
+                is.close();
+                os.closeEntry();
+            }
+            os.close();
+            return f;
+        }
+
+        public void stop(BundleContext context) throws Exception
+        {
+            if (m_reg != null)
+            {
+                m_reg.unregister();
+            }
+        }
+    }
+
+    private static File createBundle(String manifest, Class... classes) throws IOException
+    {
+        File f = File.createTempFile("felix-bundle", ".jar");
+        f.deleteOnExit();
+
+        Manifest mf = new Manifest(new ByteArrayInputStream(manifest.getBytes("utf-8")));
+        JarOutputStream os = new JarOutputStream(new FileOutputStream(f), mf);
+
+        for (Class clazz : classes)
+        {
+            String path = clazz.getName().replace('.', '/') + ".class";
+            os.putNextEntry(new ZipEntry(path));
+
+            InputStream is = clazz.getClassLoader().getResourceAsStream(path);
+            byte[] buffer = new byte[8 * 1024];
+            for (int i = is.read(buffer); i != -1; i = is.read(buffer))
+            {
+                os.write(buffer, 0, i);
+            }
+            is.close();
+            os.closeEntry();
+        }
+        os.close();
+        return f;
+    }
+
+    private static Felix createFramework() throws Exception
+    {
+        Map params = new HashMap();
+        params.put(Constants.FRAMEWORK_SYSTEMPACKAGES,
+            "org.osgi.framework; version=1.4.0,"
+            + "org.osgi.service.packageadmin; version=1.2.0,"
+            + "org.osgi.service.startlevel; version=1.1.0,"
+            + "org.osgi.util.tracker; version=1.3.3,"
+            + "org.osgi.service.url; version=1.0.0");
+        File cacheDir = File.createTempFile("felix-cache", ".dir");
+        if (!cacheDir.delete() || !cacheDir.mkdirs())
+        {
+            fail("Unable to set-up cache dir");
+        }
+        String cache = cacheDir.getPath();
+        params.put("felix.cache.profiledir", cache);
+        params.put("felix.cache.dir", cache);
+        params.put(Constants.FRAMEWORK_STORAGE, cache);
+
+        return new Felix(params);
+    }
+}
diff --git a/src/test/java/org/apache/felix/framework/capabilityset/SimpleFilterTest.java b/src/test/java/org/apache/felix/framework/capabilityset/SimpleFilterTest.java
new file mode 100644
index 0000000..b498a73
--- /dev/null
+++ b/src/test/java/org/apache/felix/framework/capabilityset/SimpleFilterTest.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.capabilityset;
+
+import junit.framework.TestCase;
+import java.util.List;
+
+public class SimpleFilterTest extends TestCase
+{
+    public void testSubstringMatching()
+    {
+        List<String> pieces;
+
+        pieces = SimpleFilter.parseSubstring("*");
+        assertTrue("Should match!", SimpleFilter.compareSubstring(pieces, ""));
+
+        pieces = SimpleFilter.parseSubstring("foo");
+        assertFalse("Should not match!", SimpleFilter.compareSubstring(pieces, ""));
+
+        pieces = SimpleFilter.parseSubstring("");
+        assertTrue("Should match!", SimpleFilter.compareSubstring(pieces, ""));
+        assertFalse("Should not match!", SimpleFilter.compareSubstring(pieces, "foo"));
+
+        pieces = SimpleFilter.parseSubstring("foo");
+        assertTrue("Should match!", SimpleFilter.compareSubstring(pieces, "foo"));
+        assertFalse("Should not match!", SimpleFilter.compareSubstring(pieces, "barfoo"));
+        assertFalse("Should not match!", SimpleFilter.compareSubstring(pieces, "foobar"));
+
+        pieces = SimpleFilter.parseSubstring("foo*");
+        assertTrue("Should match!", SimpleFilter.compareSubstring(pieces, "foo"));
+        assertFalse("Should not match!", SimpleFilter.compareSubstring(pieces, "barfoo"));
+        assertTrue("Should match!", SimpleFilter.compareSubstring(pieces, "foobar"));
+
+        pieces = SimpleFilter.parseSubstring("*foo");
+        assertTrue("Should match!", SimpleFilter.compareSubstring(pieces, "foo"));
+        assertTrue("Should match!", SimpleFilter.compareSubstring(pieces, "barfoo"));
+        assertFalse("Should match!", SimpleFilter.compareSubstring(pieces, "foobar"));
+
+        pieces = SimpleFilter.parseSubstring("foo*bar");
+        assertTrue("Should match!", SimpleFilter.compareSubstring(pieces, "foobar"));
+        assertFalse("Should not match!", SimpleFilter.compareSubstring(pieces, "barfoo"));
+        assertTrue("Should match!", SimpleFilter.compareSubstring(pieces, "foosldfjbar"));
+
+        pieces = SimpleFilter.parseSubstring("*foo*bar");
+        assertTrue("Should match!", SimpleFilter.compareSubstring(pieces, "foobar"));
+        assertFalse("Should not match!", SimpleFilter.compareSubstring(pieces, "foobarfoo"));
+        assertTrue("Should match!", SimpleFilter.compareSubstring(pieces, "barfoobar"));
+        assertTrue("Should match!", SimpleFilter.compareSubstring(pieces, "sdffoobsdfbar"));
+
+        pieces = SimpleFilter.parseSubstring("*foo*bar*");
+        assertTrue("Should match!", SimpleFilter.compareSubstring(pieces, "foobar"));
+        assertTrue("Should match!", SimpleFilter.compareSubstring(pieces, "foobarfoo"));
+        assertTrue("Should match!", SimpleFilter.compareSubstring(pieces, "barfoobar"));
+        assertTrue("Should match!", SimpleFilter.compareSubstring(pieces, "sdffoobsdfbar"));
+        assertTrue("Should match!", SimpleFilter.compareSubstring(pieces, "sdffoobsdfbarlj"));
+        assertFalse("Should not match!", SimpleFilter.compareSubstring(pieces, "sdffobsdfbarlj"));
+
+        pieces = SimpleFilter.parseSubstring("*foo(*bar*");
+        assertTrue("Should match!", SimpleFilter.compareSubstring(pieces, "foo()bar"));
+
+        pieces = SimpleFilter.parseSubstring("*foo*bar*bar");
+        assertFalse("Should not match!", SimpleFilter.compareSubstring(pieces, "foobar"));
+
+        pieces = SimpleFilter.parseSubstring("aaaa*aaaa");
+        assertFalse("Should not match!", SimpleFilter.compareSubstring(pieces, "aaaaaaa"));
+
+        pieces = SimpleFilter.parseSubstring("aaa**aaa");
+        assertTrue("Should match!", SimpleFilter.compareSubstring(pieces, "aaaaaa"));
+    }
+}
diff --git a/src/test/java/org/apache/felix/framework/util/EventDispatcherTest.java b/src/test/java/org/apache/felix/framework/util/EventDispatcherTest.java
new file mode 100644
index 0000000..a6eda11
--- /dev/null
+++ b/src/test/java/org/apache/felix/framework/util/EventDispatcherTest.java
@@ -0,0 +1,171 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.util;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import junit.framework.TestCase;
+
+import org.apache.felix.framework.Logger;
+import org.apache.felix.framework.ServiceRegistry;
+import org.easymock.EasyMock;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.hooks.service.EventHook;
+import org.osgi.framework.launch.Framework;
+
+public class EventDispatcherTest extends TestCase
+{
+    public void testFireServiceEvent()
+    {
+        final Bundle b1 = getMockBundle();
+        final Bundle b2 = getMockBundle();
+        final Bundle b3 = getMockBundle();
+        final Bundle b4 = getMockBundle();
+
+        final Set calledHooks = new HashSet();
+        final EventHook eh1 = new EventHook()
+        {
+            public void event(ServiceEvent event, Collection contexts)
+            {
+                calledHooks.add(this);
+            }
+        };
+        final EventHook eh2 = new EventHook()
+        {
+            public void event(ServiceEvent event, Collection contexts)
+            {
+                calledHooks.add(this);
+                for (Iterator it = contexts.iterator(); it.hasNext();)
+                {
+                    BundleContext bc = (BundleContext) it.next();
+                    if (bc.getBundle() == b1)
+                    {
+                        it.remove();
+                    }
+                    if (bc.getBundle() == b2)
+                    {
+                        it.remove();
+                    }
+                }
+            }
+        };
+
+        Logger logger = new Logger();
+        ServiceRegistry registry = new ServiceRegistry(logger, null);
+        registry.registerService(b4.getBundleContext(), new String [] {EventHook.class.getName()}, eh1, new Hashtable());
+        registry.registerService(b4.getBundleContext(), new String [] {EventHook.class.getName()}, eh2, new Hashtable());
+
+        // -- Set up event dispatcher
+        EventDispatcher ed = new EventDispatcher(logger, registry);
+
+        // -- Register some listeners
+        final List fired = Collections.synchronizedList(new ArrayList());
+        ServiceListener sl1 = new ServiceListener()
+        {
+            public void serviceChanged(ServiceEvent arg0)
+            {
+                fired.add(this);
+            }
+        };
+        ed.addListener(b1.getBundleContext(), ServiceListener.class, sl1, null);
+
+        ServiceListener sl2 = new ServiceListener()
+        {
+            public void serviceChanged(ServiceEvent arg0)
+            {
+                fired.add(this);
+            }
+        };
+        ed.addListener(b2.getBundleContext(), ServiceListener.class, sl2, null);
+
+        ServiceListener sl3 = new ServiceListener()
+        {
+            public void serviceChanged(ServiceEvent arg0)
+            {
+                fired.add(this);
+            }
+        };
+        ed.addListener(b3.getBundleContext(), ServiceListener.class, sl3, null);
+
+        // --- make the invocation
+        ServiceReference sr = EasyMock.createNiceMock(ServiceReference.class);
+        EasyMock.expect(sr.getProperty(Constants.OBJECTCLASS)).andReturn(new String[]
+            {
+                "java.lang.String"
+            }).anyTimes();
+        sr.isAssignableTo(b1, String.class.getName());
+        EasyMock.expectLastCall().andReturn(Boolean.TRUE).anyTimes();
+        sr.isAssignableTo(b2, String.class.getName());
+        EasyMock.expectLastCall().andReturn(Boolean.TRUE).anyTimes();
+        sr.isAssignableTo(b3, String.class.getName());
+        EasyMock.expectLastCall().andReturn(Boolean.TRUE).anyTimes();
+        EasyMock.replay(new Object[]
+            {
+                sr
+            });
+
+        ServiceEvent event = new ServiceEvent(ServiceEvent.REGISTERED, sr);
+
+        assertEquals("Precondition failed", 0, fired.size());
+
+        Framework framework = EasyMock.createNiceMock(Framework.class);
+        EasyMock.replay(new Object[]
+            {
+                framework
+            });
+
+        ed.fireServiceEvent(event, null, framework);
+        assertEquals(1, fired.size());
+        assertSame(sl3, fired.iterator().next());
+
+        assertEquals(2, calledHooks.size());
+        assertTrue(calledHooks.contains(eh1));
+        assertTrue(calledHooks.contains(eh2));
+    }
+
+    private Bundle getMockBundle()
+    {
+        BundleContext bc = EasyMock.createNiceMock(BundleContext.class);
+        Bundle b = EasyMock.createNiceMock(Bundle.class);
+        EasyMock.expect(b.getBundleContext()).andReturn(bc).anyTimes();
+        b.getState();
+        EasyMock.expectLastCall().andReturn(Integer.valueOf(Bundle.ACTIVE)).anyTimes();
+
+        EasyMock.expect(bc.getBundle()).andReturn(b).anyTimes();
+
+        EasyMock.replay(new Object[]
+            {
+                bc, b
+            });
+
+        return b;
+    }
+}
diff --git a/src/test/java/org/apache/felix/framework/util/UtilTest.java b/src/test/java/org/apache/felix/framework/util/UtilTest.java
new file mode 100644
index 0000000..f3516b8
--- /dev/null
+++ b/src/test/java/org/apache/felix/framework/util/UtilTest.java
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.util;
+
+import java.util.Properties;
+import junit.framework.TestCase;
+
+public class UtilTest extends TestCase
+{
+    public void testVariableSubstitution()
+    {
+        Properties props = new Properties();
+        props.setProperty("one", "${two}");
+        props.setProperty("two", "2");
+        String v = Util.substVars(props.getProperty("one"), "one", null, props);
+        assertEquals("2", v);
+
+        props.clear();
+        props.setProperty("one", "${two}${three}");
+        props.setProperty("two", "2");
+        props.setProperty("three", "3");
+        v = Util.substVars(props.getProperty("one"), "one", null, props);
+        assertEquals("23", v);
+
+        props.clear();
+        props.setProperty("one", "${two${three}}");
+        props.setProperty("two3", "2");
+        props.setProperty("three", "3");
+        v = Util.substVars(props.getProperty("one"), "one", null, props);
+        assertEquals("2", v);
+
+        props.clear();
+        props.setProperty("one", "${two${three}}");
+        props.setProperty("two3", "2");
+        System.setProperty("three", "3");
+        v = Util.substVars(props.getProperty("one"), "one", null, props);
+        System.getProperties().remove("three");
+        assertEquals("2", v);
+
+        props.clear();
+        props.setProperty("one", "${two}");
+        v = Util.substVars(props.getProperty("one"), "one", null, props);
+        assertEquals("", v);
+
+        props.clear();
+        props.setProperty("one", "{two");
+        v = Util.substVars(props.getProperty("one"), "one", null, props);
+        assertEquals("{two", v);
+
+        props.clear();
+        props.setProperty("one", "{two}");
+        v = Util.substVars(props.getProperty("one"), "one", null, props);
+        assertEquals("{two}", v);
+
+        props.clear();
+        props.setProperty("one", "${two");
+        v = Util.substVars(props.getProperty("one"), "one", null, props);
+        assertEquals("${two", v);
+
+        props.clear();
+        props.setProperty("one", "${two${two}");
+        props.setProperty("two", "2");
+        v = Util.substVars(props.getProperty("one"), "one", null, props);
+        assertEquals("${two2", v);
+
+        props.clear();
+        props.setProperty("one", "{two${two}}");
+        props.setProperty("two", "2");
+        v = Util.substVars(props.getProperty("one"), "one", null, props);
+        assertEquals("{two2}", v);
+
+        props.clear();
+        props.setProperty("one", "{two}${two");
+        v = Util.substVars(props.getProperty("one"), "one", null, props);
+        assertEquals("{two}${two", v);
+
+        props.clear();
+        props.setProperty("one", "leading text ${two}");
+        props.setProperty("two", "2");
+        v = Util.substVars(props.getProperty("one"), "one", null, props);
+        assertEquals("leading text 2", v);
+
+        props.clear();
+        props.setProperty("one", "${two} trailing text");
+        props.setProperty("two", "2");
+        v = Util.substVars(props.getProperty("one"), "one", null, props);
+        assertEquals("2 trailing text", v);
+
+        props.clear();
+        props.setProperty("one", "${two} middle text ${three}");
+        props.setProperty("two", "2");
+        props.setProperty("three", "3");
+        v = Util.substVars(props.getProperty("one"), "one", null, props);
+        assertEquals("2 middle text 3", v);
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/org/apache/felix/framework/util/WeakZipFileTest.java b/src/test/java/org/apache/felix/framework/util/WeakZipFileTest.java
new file mode 100644
index 0000000..1847056
--- /dev/null
+++ b/src/test/java/org/apache/felix/framework/util/WeakZipFileTest.java
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.util;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+import junit.framework.TestCase;
+import org.apache.felix.framework.util.WeakZipFileFactory.WeakZipFile;
+
+public class WeakZipFileTest extends TestCase
+{
+    private static final String ENTRY_NAME = "entry.txt";
+
+    public void testWeakClose()
+    {
+        // Create a reasonably big random string.
+        byte[] contentBytes = new byte[16384];
+        for (int i = 0; i < contentBytes.length; i++)
+        {
+            contentBytes[i] = (byte) ((i % 65) + 65);
+        }
+        String contentString = new String(contentBytes);
+
+        // Create a temporary zip file.
+        File tmpZip = null;
+        try
+        {
+            tmpZip = File.createTempFile("felix.test", ".zip");
+            tmpZip.deleteOnExit();
+            ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(tmpZip));
+            ZipEntry ze = new ZipEntry(ENTRY_NAME);
+            zos.putNextEntry(ze);
+            zos.write(contentBytes, 0, contentBytes.length);
+            zos.close();
+        }
+        catch (IOException ex)
+        {
+            fail("Unable to create temporary zip file: " + ex);
+        }
+
+        try
+        {
+            WeakZipFileFactory factory = new WeakZipFileFactory(1);
+            WeakZipFile zipFile = factory.create(tmpZip);
+            assertTrue("Zip file not recorded.",
+                factory.getZipZiles().contains(zipFile));
+            assertTrue("Open zip file not recorded.",
+                factory.getOpenZipZiles().contains(zipFile));
+            ZipEntry ze = zipFile.getEntry(ENTRY_NAME);
+            assertNotNull("Zip entry not found", ze);
+            byte[] firstHalf = new byte[contentBytes.length / 2];
+            byte[] secondHalf = new byte[contentBytes.length - firstHalf.length];
+            InputStream is = zipFile.getInputStream(ze);
+            is.read(firstHalf);
+            zipFile.closeWeakly();
+            assertTrue("Zip file not recorded.",
+                factory.getZipZiles().contains(zipFile));
+            assertFalse("Open zip file still recorded.",
+                factory.getOpenZipZiles().contains(zipFile));
+            is.read(secondHalf);
+            assertTrue("Zip file not recorded.",
+                factory.getZipZiles().contains(zipFile));
+            assertTrue("Open zip file not recorded.",
+                factory.getOpenZipZiles().contains(zipFile));
+            byte[] complete = new byte[firstHalf.length + secondHalf.length];
+            System.arraycopy(firstHalf, 0, complete, 0, firstHalf.length);
+            System.arraycopy(secondHalf, 0, complete, firstHalf.length, secondHalf.length);
+            String completeString = new String(complete);
+            assertEquals(contentString, completeString);
+            zipFile.close();
+        }
+        catch (IOException ex)
+        {
+            fail("Unable to read zip file entry: " + ex);
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/org/apache/felix/framework/util/manifestparser/ManifestParserTest.java b/src/test/java/org/apache/felix/framework/util/manifestparser/ManifestParserTest.java
new file mode 100644
index 0000000..1373f8c
--- /dev/null
+++ b/src/test/java/org/apache/felix/framework/util/manifestparser/ManifestParserTest.java
@@ -0,0 +1,88 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.util.manifestparser;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+import org.osgi.framework.BundleException;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Version;
+import org.osgi.framework.namespace.IdentityNamespace;
+import org.osgi.framework.wiring.BundleCapability;
+
+public class ManifestParserTest extends TestCase
+{
+    public void testIdentityCapabilityMinimal() throws BundleException
+    {
+        Map<String, String> headers = new HashMap<String, String>();
+        headers.put(Constants.BUNDLE_MANIFESTVERSION, "2");
+        headers.put(Constants.BUNDLE_SYMBOLICNAME, "foo.bar");
+        ManifestParser mp = new ManifestParser(null, null, null, headers);
+
+        BundleCapability ic = findCapability(mp.getCapabilities(), IdentityNamespace.IDENTITY_NAMESPACE);
+        assertEquals("foo.bar", ic.getAttributes().get(IdentityNamespace.IDENTITY_NAMESPACE));
+        assertEquals(IdentityNamespace.TYPE_BUNDLE, ic.getAttributes().get(IdentityNamespace.CAPABILITY_TYPE_ATTRIBUTE));
+        assertEquals(0, ic.getDirectives().size());
+    }
+
+    public void testIdentityCapabilityFull() throws BundleException
+    {
+        Map<String, String> headers = new HashMap<String, String>();
+        headers.put(Constants.BUNDLE_MANIFESTVERSION, "2");
+        headers.put(Constants.BUNDLE_SYMBOLICNAME, "abc;singleton:=true");
+        headers.put(Constants.BUNDLE_VERSION, "1.2.3.something");
+        String copyright = "(c) 2013 Apache Software Foundation";
+        headers.put(Constants.BUNDLE_COPYRIGHT, copyright);
+        String description = "A bundle description";
+        headers.put(Constants.BUNDLE_DESCRIPTION, description);
+        String docurl = "http://felix.apache.org/";
+        headers.put(Constants.BUNDLE_DOCURL, docurl);
+        String license = "http://www.apache.org/licenses/LICENSE-2.0";
+        headers.put("Bundle-License", license);
+        ManifestParser mp = new ManifestParser(null, null, null, headers);
+
+        BundleCapability ic = findCapability(mp.getCapabilities(), IdentityNamespace.IDENTITY_NAMESPACE);
+        assertEquals("abc", ic.getAttributes().get(IdentityNamespace.IDENTITY_NAMESPACE));
+        assertEquals(new Version("1.2.3.something"), ic.getAttributes().get(IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE));
+        assertEquals(IdentityNamespace.TYPE_BUNDLE, ic.getAttributes().get(IdentityNamespace.CAPABILITY_TYPE_ATTRIBUTE));
+        assertEquals(copyright, ic.getAttributes().get(IdentityNamespace.CAPABILITY_COPYRIGHT_ATTRIBUTE));
+        assertEquals(description, ic.getAttributes().get(IdentityNamespace.CAPABILITY_DESCRIPTION_ATTRIBUTE));
+        assertEquals(docurl, ic.getAttributes().get(IdentityNamespace.CAPABILITY_DOCUMENTATION_ATTRIBUTE));
+        assertEquals(license, ic.getAttributes().get(IdentityNamespace.CAPABILITY_LICENSE_ATTRIBUTE));
+
+        assertEquals(1, ic.getDirectives().size());
+        assertEquals("true", ic.getDirectives().get(IdentityNamespace.CAPABILITY_SINGLETON_DIRECTIVE));
+    }
+
+    private BundleCapability findCapability(Collection<BundleCapability> capabilities, String namespace)
+    {
+        for (BundleCapability capability : capabilities)
+        {
+            if (namespace.equals(capability.getNamespace()))
+            {
+                return capability;
+            }
+        }
+        return null;
+    }
+}

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-java/felix-framework.git



More information about the pkg-java-commits mailing list