This is an automated email from the ASF dual-hosted git repository.

radu pushed a commit to branch master
in repository 
https://gitbox.apache.org/repos/asf/sling-org-apache-sling-servlets-resolver-api.git

commit 21b1f63b9c92bff83e543fc037030a61cf171e9b
Author: Radu Cotescu <[email protected]>
AuthorDate: Fri Jan 29 10:33:30 2021 +0100

    SLING-9999 - Remove cyclic dependency between scripting and servlets 
features
    
    * extracted the o.a.s.servlets.resolver.bundle.tracker API into a dedicated
    API bundle
---
 .gitignore                                         |  19 ++
 CODE_OF_CONDUCT.md                                 |  22 +++
 CONTRIBUTING.md                                    |  24 +++
 Jenkinsfile                                        |  20 ++
 LICENSE                                            | 202 +++++++++++++++++++++
 README.md                                          |  76 ++++++++
 asf.yaml                                           |   6 +
 pom.xml                                            | 127 +++++++++++++
 .../resolver/bundle/tracker/BundledRenderUnit.java | 145 +++++++++++++++
 .../tracker/BundledRenderUnitCapability.java       |  91 ++++++++++
 .../bundle/tracker/BundledRenderUnitFinder.java    |  62 +++++++
 .../resolver/bundle/tracker/ResourceType.java      | 161 ++++++++++++++++
 .../resolver/bundle/tracker/TypeProvider.java      |  45 +++++
 .../resolver/bundle/tracker/package-info.java      |  22 +++
 .../resolver/bundle/tracker/ResourceTypeTest.java  |  87 +++++++++
 15 files changed, 1109 insertions(+)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..0a3f3d1
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,19 @@
+/target
+.vscode
+.idea
+.classpath
+.metadata
+.project
+.settings
+.externalToolBuilders
+maven-eclipse.xml
+*.swp
+*.iml
+*.ipr
+*.iws
+*.bak
+.vlt
+.DS_Store
+jcr.log
+atlassian-ide-plugin.xml
+dependency-reduced-pom.xml
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
new file mode 100644
index 0000000..0fa18e5
--- /dev/null
+++ b/CODE_OF_CONDUCT.md
@@ -0,0 +1,22 @@
+<!--/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  ~ 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.
+  
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/-->
+Apache Software Foundation Code of Conduct
+====
+
+Being an Apache project, Apache Sling adheres to the Apache Software 
Foundation's [Code of 
Conduct](https://www.apache.org/foundation/policies/conduct.html).
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..ac82a1a
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,24 @@
+<!--/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  ~ 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.
+  
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/-->
+Contributing
+====
+
+Thanks for choosing to contribute!
+
+You will find all the necessary details about how you can do this at 
https://sling.apache.org/contributing.html.
diff --git a/Jenkinsfile b/Jenkinsfile
new file mode 100644
index 0000000..f582519
--- /dev/null
+++ b/Jenkinsfile
@@ -0,0 +1,20 @@
+/**
+ * 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.
+ */
+
+slingOsgiBundleBuild()
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/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/README.md b/README.md
new file mode 100644
index 0000000..2726a91
--- /dev/null
+++ b/README.md
@@ -0,0 +1,76 @@
+[![Apache 
Sling](https://sling.apache.org/res/logos/sling.png)](https://sling.apache.org)
+
+&#32;[![Build 
Status](https://ci-builds.apache.org/job/Sling/job/modules/job/sling-org-apache-sling-servlets-resolver/job/master/badge/icon)](https://ci-builds.apache.org/job/Sling/job/modules/job/sling-org-apache-sling-servlets-resolver/job/master/)&#32;[![Test
 
Status](https://img.shields.io/jenkins/tests.svg?jobUrl=https://ci-builds.apache.org/job/Sling/job/modules/job/sling-org-apache-sling-servlets-resolver/job/master/)](https://ci-builds.apache.org/job/Sling/job/modules/job/sling-or
 [...]
+
+# Apache Sling Servlet Resolver
+
+This module is part of the [Apache Sling](https://sling.apache.org) project.
+
+Bundle implementing the Sling API ServletResolver.
+
+## Bundled scripts
+Version 2.7.0 of this bundle has added support for executing bundled scripts 
(precompiled or not), through the
+`org.apache.sling.servlets.resolver.bundle.tracker` API.
+
+Although traditionally scripts are deployed as content stored in the search 
paths of a Sling instance, this leaves very little
+room for script evolution in a backwards compatible way. Furthermore, 
versioning scripts is a difficult process if the only
+mechanism to do this is the `sling:resourceType` property, since consumers 
(content nodes or other resource types) have then to
+explicitly mention the version expected to be executed.
+
+Scripts should not be considered content, since their only purpose is to 
actually generate the rendering for a certain content
+structure. They are not consumed by users, but rather by the Sling Engine 
itself and have very little meaning outside this
+context. As such, scripts should be handled like code:
+
+  1. they _provide an HTTP API_;
+  2. they can evolve in a _semantical_ [1] way;
+  3. they have a _developer audience_.
+
+### How
+Being built around a `BundleTrackerCustomizer` [2], the 
`org.apache.sling.servlets.resolver.bundle.tracker.internal.BundledScriptTracker`
+monitors the instance's bundles wired to the 
`org.apache.sling.servlets.resolver` bundle and scans the ones providing a 
`sling.servlet`
+capability [3]. The wiring is created by placing a `Require-Capability` header 
in the bundles that provide the `sling.servlet` capability:
+
+```
+osgi.extender;filter:="(&(osgi.extender=sling.scripting)(version>=1.0.0)(!(version>=2.0.0)))"
+```
+
+A `sling.servlet` capability has almost the same attributes as the properties 
required to register a servlet on the Sling platform [4]:
+
+  1. `sling.servlet.resourceTypes:List` - mandatory; defines the provided 
resource type; its value is a list of resource types
+  2. `sling.servlet.selectors:List` - optional; defines the list of selectors 
that this resource type can handle;
+  3. `sling.servlet.extensions:List` - optional; defines the list of 
extensions that this resource type can handle;
+  4. `sling.servlet.methods:List` - optional; defines the list of HTTP methods 
that this resource type can handle;
+  5. `version:Version` - optional; defines the version of the provided 
`resourceType`;
+  6. `extends:String` - optional; defines which resource type it extends; the 
version range of the extended resource type is defined in a
+    `Require-Capability`.
+
+The `BundledScriptTracker` will register a Sling Servlet with the appropriate 
properties for each `sling.servlet` capability. The
+servlets will be registered using the bundle context of the bundle providing 
the `sling.servlet` capability, making
+sure to expose the different versions of a resource type as part of the 
registered servlet's properties. On top of this, a plain resource
+type bound servlet will also be registered, which will be automatically wired 
to the highest version of the `resourceType`. All the
+mentioned service registrations are managed automatically by the 
`BundledScriptTracker`.
+
+### So how do I deploy my scripts?
+Short answer: exactly like you deploy your code, preferably right next to it. 
Pack your scripts using the following conventions:
+
+  1. create a `src/main/resources/javax.script` folder in your bundle (if you 
want to embed the scripts as they are) or just put the
+   scripts in `src/main/scripts` if you want to precompiled them (e.g. JSP and 
HTL);
+  2. each folder under the above folders will identify a `resourceType`;
+  3. inside each `resourceType` folder you can optionally create a `Version` 
folder; this has to follow the Semantic Versioning
+   constraints described at [1];
+  4. add your scripts, using the same naming conventions that you were used to 
from before [5];
+  5. manually define your provide and require capabilities; just kidding; add 
the
+  
[`scriptingbundle-maven-plugin`](https://github.com/apache/sling-scriptingbundle-maven-plugin)
 to your build section and add its required
+  properties in the `maven-bundle-plugin`'s instructions (check [these 
examples](https://github.com/apache/sling-org-apache-sling-scripting-bundle-tracker-it/tree/master/examples/));
+  6. `mvn clean sling:install`.
+
+### Integration Tests
+
+The integration tests for bundled scripts are provided by the 
[`org.apache.sling.scripting.bundle.tracker.it`](https://github.com/apache/sling-org-apache-sling-scripting-bundle-tracker-it)
 project.
+
+## Resources
+[1] - https://semver.org/  
+[2] - 
https://osgi.org/javadoc/r6/core/org/osgi/util/tracker/BundleTrackerCustomizer.html
  
+[3] - https://osgi.org/download/r6/osgi.core-6.0.0.pdf, Page 41, section 3.3.3 
"Bundle Capabilities"  
+[4] - 
https://sling.apache.org/documentation/the-sling-engine/servlets.html#servlet-registration-1
  
+[5] - 
https://sling.apache.org/documentation/the-sling-engine/url-to-script-resolution.html
diff --git a/asf.yaml b/asf.yaml
new file mode 100644
index 0000000..b9dcfc4
--- /dev/null
+++ b/asf.yaml
@@ -0,0 +1,6 @@
+github:
+  description: This bundle defines the Apache Sling Servlets Resolver's API
+  homepage: https://sling.apache.org
+  labels:
+    - sling
+    - java
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..d1afed9
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+    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";>
+
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.sling</groupId>
+        <artifactId>sling-bundle-parent</artifactId>
+        <version>40</version>
+        <relativePath />
+    </parent>
+
+    <artifactId>org.apache.sling.servlets.resolver.api</artifactId>
+    <version>1.0.0-SNAPSHOT</version>
+
+    <name>Apache Sling Servlets Resolver API</name>
+    <description>
+        This bundle defines the Apache Sling Servlets Resolver's API
+    </description>
+
+    <scm>
+        
<connection>scm:git:https://gitbox.apache.org/repos/asf/sling-org-apache-sling-servlets-resolver-api.git</connection>
+        
<developerConnection>scm:git:https://gitbox.apache.org/repos/asf/sling-org-apache-sling-servlets-resolver-api.git</developerConnection>
+        
<url>https://gitbox.apache.org/repos/asf?p=sling-org-apache-sling-servlets-resolver-api.git</url>
+      <tag>HEAD</tag>
+  </scm>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>biz.aQute.bnd</groupId>
+                <artifactId>bnd-maven-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-javadoc-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>com.github.spotbugs</groupId>
+                <artifactId>spotbugs-maven-plugin</artifactId>
+                <version>3.1.11</version>
+                <configuration>
+                    <effort>Max</effort>
+                    <xmlOutput>true</xmlOutput>
+                </configuration>
+                <executions>
+                    <execution>
+                        <id>find-bugs</id>
+                        <phase>process-classes</phase>
+                        <goals>
+                            <goal>check</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.annotation.versioning</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.service.component.annotations</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.service.metatype.annotations</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>osgi.core</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>javax.servlet-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.api</artifactId>
+            <version>2.22.0</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>commons-io</groupId>
+            <artifactId>commons-io</artifactId>
+            <version>2.6</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+            <version>3.4</version>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+</project>
diff --git 
a/src/main/java/org/apache/sling/servlets/resolver/bundle/tracker/BundledRenderUnit.java
 
b/src/main/java/org/apache/sling/servlets/resolver/bundle/tracker/BundledRenderUnit.java
new file mode 100644
index 0000000..2c89309
--- /dev/null
+++ 
b/src/main/java/org/apache/sling/servlets/resolver/bundle/tracker/BundledRenderUnit.java
@@ -0,0 +1,145 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ 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.sling.servlets.resolver.bundle.tracker;
+
+import java.io.InputStream;
+import java.util.Set;
+
+import javax.script.ScriptException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.osgi.annotation.versioning.ConsumerType;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+
+/**
+ * <p>
+ * A {@code BundledRenderUnit} represents a pre-packaged script or precompiled 
script (Java class) that will be executed in order to
+ * render a {@link org.apache.sling.api.SlingHttpServletRequest}.
+ * </p>
+ * <p>
+ * The {@code BundledRenderUnit} provider module is responsible for defining 
how a unit is executed. However, when executing the unit in the
+ * context of a {@link javax.script.ScriptEngine}, the provider module should 
add the current executing unit into the {@link
+ * javax.script.ScriptEngine}'s {@link javax.script.ScriptContext} using the 
{@link #VARIABLE} key.
+ * </p>
+ */
+@ConsumerType
+public interface BundledRenderUnit {
+
+    /**
+     * The variable available in the {@link javax.script.Bindings} associated 
to a {@link org.apache.sling.api.SlingHttpServletRequest} if
+     * that request is served by a {@code BundledRenderUnit}.
+     */
+    String VARIABLE = BundledRenderUnit.class.getName();
+
+    /**
+     * In case this {@code BundledRenderUnit} wraps a precompiled script, this 
method will return an instance of that object.
+     *
+     * @return a precompiled unit, if {@code this} unit wraps a precompiled 
script; {@code null} otherwise
+     */
+    @Nullable
+    default Object getUnit() {
+        return null;
+    }
+
+    /**
+     * Returns the name of {@code this BundledRenderUnit}. This can be the 
name of the wrapped script or precompiled script.
+     *
+     * @return the name {@code this BundledRenderUnit}
+     */
+    @NotNull String getName();
+
+    /**
+     * Returns the {@link Bundle} the publishing bundle of this unit (not to 
be confused with the provider module, which is the module that
+     * instantiates a {@link BundledRenderUnit}). This method can be useful 
for getting an instance of the bundle's classloader, when
+     * needed to load dependencies at run-time. To do so the following code 
example can help:
+     *
+     * <pre>
+     * Bundle bundle = bundledRenderUnit.getBundle();
+     * Classloader bundleClassloader = 
bundle.adapt(BundleWiring.class).getClassLoader();
+     * </pre>
+     */
+    @NotNull Bundle getBundle();
+
+    /**
+     * Returns the {@link BundleContext} to use for this unit. This method can 
be useful for getting an instance of the publishing bundle's
+     * context, when needed to load dependencies at run-time.
+     *
+     * @return the bundle context of the bundle publishing this unit
+     */
+    @NotNull BundleContext getBundleContext();
+
+    /**
+     * Returns the {@code Set} of {@link TypeProvider}s which are related to 
this unit.
+     *
+     * @return the set of providers; if the unit doesn't have any inheritance 
chains, then the set will contain only one {@link
+     * TypeProvider}
+     */
+    @NotNull Set<TypeProvider> getTypeProviders();
+
+    /**
+     * Retrieves an OSGi runtime dependency of the wrapped script identified 
by the passed {@code className} parameter.
+     *
+     * @param className the fully qualified class name
+     * @param <T>       the expected service type
+     * @return an instance of the {@link T} or {@code null}
+     */
+    @Nullable <T> T getService(@NotNull String className);
+
+    /**
+     * Retrieves multiple instances of an OSGi runtime dependency of the 
wrapped script identified by the passed {@code className}
+     * parameter, filtered according to the passed {@code filter}.
+     *
+     * @param className the fully qualified class name
+     * @param filter    a filter expression or {@code null} if all the 
instances should be returned; for more details about the {@code
+     *                  filter}'s syntax check {@link 
org.osgi.framework.BundleContext#getServiceReferences(String, String)}
+     * @param <T>       the expected service type
+     * @return an instance of the {@link T} or {@code null}
+     */
+    @Nullable <T> T[] getServices(@NotNull String className, @Nullable String 
filter);
+
+    /**
+     * Returns the path of this executable in the resource type hierarchy. The 
path can be relative to the search paths or absolute.
+     *
+     * @return the path of this executable in the resource type hierarchy
+     */
+    @NotNull
+    String getPath();
+
+    /**
+     * This method will execute / evaluate the wrapped script or precompiled 
script with the given request.
+     *
+     * @throws ScriptException if the execution leads to an error
+     */
+    void eval(@NotNull HttpServletRequest request, @NotNull 
HttpServletResponse response) throws ScriptException;
+
+    /**
+     * This method will return an input stream if {@code this} unit is backed 
by a script that can be interpreted.
+     *
+     * @return an {@link InputStream} providing the source code of the backing 
script; if {@code this} unit is backed by a precompiled
+     * script (essentially a Java class), then this method will return {@code 
null}
+     */
+    @Nullable
+    default InputStream getInputStream() {
+        return null;
+    }
+}
diff --git 
a/src/main/java/org/apache/sling/servlets/resolver/bundle/tracker/BundledRenderUnitCapability.java
 
b/src/main/java/org/apache/sling/servlets/resolver/bundle/tracker/BundledRenderUnitCapability.java
new file mode 100644
index 0000000..e0693cb
--- /dev/null
+++ 
b/src/main/java/org/apache/sling/servlets/resolver/bundle/tracker/BundledRenderUnitCapability.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.sling.servlets.resolver.bundle.tracker;
+
+import java.util.List;
+import java.util.Set;
+
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.osgi.annotation.versioning.ProviderType;
+
+/**
+ * A {@code BundledRenderUnitCapability} encapsulates the values of a {@code 
Provided-Capability}, based on which {@link BundledRenderUnit}s
+ * are generated.
+ */
+@ProviderType
+public interface BundledRenderUnitCapability {
+
+    /**
+     * Returns the resource types to which a {@link BundledRenderUnit} 
described by this capability will be bound to.
+     *
+     * @return the resource types to which a {@link BundledRenderUnit} 
described by this capability will be bound to
+     */
+    @NotNull Set<ResourceType> getResourceTypes();
+
+    /**
+     * Returns the path to which a {@link BundledRenderUnit} described by this 
capability will be bound to.
+     *
+     * @return the path to which a {@link BundledRenderUnit} described by this 
capability will be bound to; this can be {@code null} if the
+     * {@link #getResourceTypes()} doesn't return an empty set
+     */
+    @Nullable String getPath();
+
+    /**
+     * Returns the selectors to which a {@link BundledRenderUnit} described by 
this capability will be bound to.
+     *
+     * @return the selectors to which a {@link BundledRenderUnit} described by 
this capability will be bound to
+     */
+    @NotNull List<String> getSelectors();
+
+    /**
+     * Returns the extension to which a {@link BundledRenderUnit} described by 
this capability will be bound to.
+     *
+     * @return the extension to which a {@link BundledRenderUnit} described by 
this capability will be bound to
+     */
+    @Nullable String getExtension();
+
+    /**
+     * Returns the resource type extended by this capability.
+     *
+     * @return the extended resource type or {@code null}
+     */
+    @Nullable String getExtendedResourceType();
+
+    /**
+     * Returns the request method to which a {@link BundledRenderUnit} 
described by this capability will be bound to.
+     *
+     * @return the request method to which a {@link BundledRenderUnit} 
described by this capability will be bound to
+     */
+    @Nullable String getMethod();
+
+    /**
+     * Returns the script engine short name which can be used to evaluate the 
{@link BundledRenderUnit} described by this capability.
+     *
+     * @return the script engine short name which can be used to evaluate the 
{@link BundledRenderUnit} described by this capability.
+     */
+    @Nullable String getScriptEngineName();
+
+    /**
+     * Returns the original's script extension that was used to generate this 
capability.
+     *
+     * @return the original's script extension that was used to generate this 
capability.
+     */
+    @Nullable String getScriptExtension();
+}
diff --git 
a/src/main/java/org/apache/sling/servlets/resolver/bundle/tracker/BundledRenderUnitFinder.java
 
b/src/main/java/org/apache/sling/servlets/resolver/bundle/tracker/BundledRenderUnitFinder.java
new file mode 100644
index 0000000..d632ba8
--- /dev/null
+++ 
b/src/main/java/org/apache/sling/servlets/resolver/bundle/tracker/BundledRenderUnitFinder.java
@@ -0,0 +1,62 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ 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.sling.servlets.resolver.bundle.tracker;
+
+import java.util.Set;
+
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.osgi.annotation.versioning.ConsumerType;
+import org.osgi.framework.BundleContext;
+
+/**
+ * The {@code BundledScriptFinder} finds the {@link BundledRenderUnit} 
corresponding to a certain chain of {@link TypeProvider}s or
+ * corresponding to a certain path-based {@link BundledRenderUnitCapability}.
+ */
+@ConsumerType
+public interface BundledRenderUnitFinder {
+
+    /**
+     * Retrieves the best matching {@link BundledRenderUnit} for the provided 
{@code inheritanceChain}, by scanning all {@link TypeProvider}
+     * bundles for the class or script capable of providing a rendering for 
resource type chain.
+     *
+     * @param context             the bundle context to use.
+     * @param inheritanceChain    the resource type chain; the set is ordered 
from the most specific resource type to the most generic one
+     * @param allRelatedProviders this is a super set, containing both the 
{@code inheritanceChain} but also all the required providers; a
+     *                            required provider is a provider that's 
needed by a {@link ResourceType} in order to delegate rendering to
+     *                            it, but it's not extended by the same {@link 
ResourceType}
+     * @return a {@link BundledRenderUnit} if one was found, {@code null} 
otherwise
+     */
+    @Nullable
+    BundledRenderUnit findUnit(@NotNull BundleContext context, @NotNull 
Set<TypeProvider> inheritanceChain, @NotNull Set<TypeProvider> 
allRelatedProviders);
+
+    /**
+     * Retrieves a path-based {@link BundledRenderUnit} from the passed {@code 
provider}.
+     *
+     * @param context             the bundle context to use.
+     * @param provider            the provider from which to retrieve the unit
+     * @param allRelatedProviders this is a super set, containing both the 
providers connected through an inheritance relationship but also
+     *                            all the required providers; a required 
provider is a provider that's needed by a {@link ResourceType} in
+     *                            order to delegate rendering to it, but it's 
not extended by the same {@link ResourceType}
+     * @return a {@link BundledRenderUnit} if one was found, {@code null} 
otherwise
+     * @see BundledRenderUnitCapability#getPath()
+     */
+    @Nullable
+    BundledRenderUnit findUnit(@NotNull BundleContext context, @NotNull 
TypeProvider provider, @NotNull Set<TypeProvider> allRelatedProviders);
+}
diff --git 
a/src/main/java/org/apache/sling/servlets/resolver/bundle/tracker/ResourceType.java
 
b/src/main/java/org/apache/sling/servlets/resolver/bundle/tracker/ResourceType.java
new file mode 100644
index 0000000..0f2929e
--- /dev/null
+++ 
b/src/main/java/org/apache/sling/servlets/resolver/bundle/tracker/ResourceType.java
@@ -0,0 +1,161 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ 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.sling.servlets.resolver.bundle.tracker;
+
+import java.util.Objects;
+import java.util.regex.Pattern;
+
+import org.apache.commons.lang3.StringUtils;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.osgi.framework.Version;
+
+/**
+ * The {@code ResourceType} encapsulates details about a Sling resource type 
and provides methods for parsing resource type strings.
+ *
+ * <p>The following patterns are supported for parsing:</p>
+ * <ol>
+ * <li><tt>a/b/c</tt> - path-based</li>
+ * <li><tt>a/b/c/1.0.0</tt> - path-based, versioned</li>
+ * <li><tt>a.b.c</tt> - Java package name</li>
+ * <li><tt>a.b.c/1.0.0</tt> - Java package name, versioned</li>
+ * <li><tt>a</tt> - flat (sub-set of path-based)</li>
+ * </ol>
+ */
+public final class ResourceType {
+
+    private static final Pattern versionPattern = 
Pattern.compile("[\\d\\.]+(-.*)*$");
+
+    private final String type;
+    private final String version;
+    private final String resourceLabel;
+    private final String toString;
+
+    private ResourceType(@NotNull String type, @Nullable String version) {
+        this.type = type;
+        this.version = version;
+        if (type.lastIndexOf('/') != -1) {
+            resourceLabel = type.substring(type.lastIndexOf('/') + 1);
+        } else if (type.lastIndexOf('.') != -1) {
+            resourceLabel = type.substring(type.lastIndexOf('.') + 1);
+        } else {
+            resourceLabel = type;
+        }
+        toString = type + (version == null ? "" : "/" + version);
+    }
+
+    /**
+     * Returns a resource type's label. The label is important for script 
selection, since it will provide the name of the main script
+     * for this resource type. For more details check the Apache Sling
+     * <a 
href="https://sling.apache.org/documentation/the-sling-engine/url-to-script-resolution.html#scripts-for-get-requests";>URL
 to
+     * Script Resolution</a> page
+     *
+     * @return the resource type label
+     */
+    @NotNull
+    public String getResourceLabel() {
+        return resourceLabel;
+    }
+
+    /**
+     * Returns the resource type string, without any version information.
+     *
+     * @return the resource type string
+     */
+    @NotNull
+    public String getType() {
+        return type;
+    }
+
+    /**
+     * Returns the version, if available.
+     *
+     * @return the version, if available; {@code null} otherwise
+     */
+    @Nullable
+    public String getVersion() {
+        return version;
+    }
+
+    @Override
+    public String toString() {
+        return toString;
+    }
+
+    /**
+     * Given a {@code resourceTypeString}, this method will extract a {@link 
ResourceType} object.
+     * <p>The accepted patterns are:</p>
+     * <ol>
+     * <li><tt>a/b/c</tt> - path-based</li>
+     * <li><tt>a/b/c/1.0.0</tt> - path-based, versioned</li>
+     * <li><tt>a.b.c</tt> - Java package name</li>
+     * <li><tt>a.b.c/1.0.0</tt> - Java package name, versioned</li>
+     * <li><tt>a</tt> - flat (sub-set of path-based)</li>
+     * </ol>
+     *
+     * @param resourceTypeString the resource type string to parse
+     * @return a {@link ResourceType} object
+     * @throws IllegalArgumentException if the {@code resourceTypeString} 
cannot be parsed
+     */
+    @NotNull
+    public static ResourceType parseResourceType(@NotNull String 
resourceTypeString) {
+        String type = StringUtils.EMPTY;
+        String version = null;
+        if (StringUtils.isNotEmpty(resourceTypeString)) {
+            int lastSlash = resourceTypeString.lastIndexOf('/');
+            if (lastSlash != -1 && !resourceTypeString.endsWith("/")) {
+                String versionString = resourceTypeString.substring(lastSlash 
+ 1);
+                if (versionPattern.matcher(versionString).matches()) {
+                    try {
+                        version = 
Version.parseVersion(versionString).toString();
+                        type = resourceTypeString.substring(0, lastSlash);
+                    } catch (IllegalArgumentException e) {
+                        type = resourceTypeString;
+                    }
+                } else {
+                    type = resourceTypeString;
+                }
+            } else {
+                type = resourceTypeString;
+            }
+        }
+        if (StringUtils.isEmpty(type)) {
+            throw new IllegalArgumentException(String.format("Cannot extract a 
type for the resourceTypeString %s.", resourceTypeString));
+        }
+        return new ResourceType(type, version);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(type, version, resourceLabel);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj instanceof ResourceType) {
+            ResourceType other = (ResourceType) obj;
+            return Objects.equals(type, other.type) && Objects.equals(version, 
other.version) && Objects.equals(resourceLabel,
+                    other.resourceLabel);
+        }
+        return false;
+    }
+}
diff --git 
a/src/main/java/org/apache/sling/servlets/resolver/bundle/tracker/TypeProvider.java
 
b/src/main/java/org/apache/sling/servlets/resolver/bundle/tracker/TypeProvider.java
new file mode 100644
index 0000000..7e36d7d
--- /dev/null
+++ 
b/src/main/java/org/apache/sling/servlets/resolver/bundle/tracker/TypeProvider.java
@@ -0,0 +1,45 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ 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.sling.servlets.resolver.bundle.tracker;
+
+import org.jetbrains.annotations.NotNull;
+import org.osgi.annotation.versioning.ProviderType;
+import org.osgi.framework.Bundle;
+
+/**
+ * A {@code TypeProvider} keeps an association between a {@link 
BundledRenderUnitCapability} and the bundle that provides it.
+ */
+@ProviderType
+public interface TypeProvider {
+
+    /**
+     * Returns the {@link BundledRenderUnitCapability}.
+     *
+     * @return the {@link BundledRenderUnitCapability}
+     */
+    @NotNull BundledRenderUnitCapability getBundledRenderUnitCapability();
+
+    /**
+     * Returns the providing bundle.
+     *
+     * @return the providing bundle
+     */
+    @NotNull Bundle getBundle();
+
+}
diff --git 
a/src/main/java/org/apache/sling/servlets/resolver/bundle/tracker/package-info.java
 
b/src/main/java/org/apache/sling/servlets/resolver/bundle/tracker/package-info.java
new file mode 100644
index 0000000..91b289b
--- /dev/null
+++ 
b/src/main/java/org/apache/sling/servlets/resolver/bundle/tracker/package-info.java
@@ -0,0 +1,22 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ 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.
+ 
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+@Version("0.2.0")
+package org.apache.sling.servlets.resolver.bundle.tracker;
+
+import org.osgi.annotation.versioning.Version;
diff --git 
a/src/test/java/org/apache/sling/servlets/resolver/bundle/tracker/ResourceTypeTest.java
 
b/src/test/java/org/apache/sling/servlets/resolver/bundle/tracker/ResourceTypeTest.java
new file mode 100644
index 0000000..5464bd1
--- /dev/null
+++ 
b/src/test/java/org/apache/sling/servlets/resolver/bundle/tracker/ResourceTypeTest.java
@@ -0,0 +1,87 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ 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.sling.servlets.resolver.bundle.tracker;
+
+import org.apache.commons.lang3.StringUtils;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+public class ResourceTypeTest {
+
+    @Test
+    public void testSlashNoVersion() {
+        ResourceType t1 = ResourceType.parseResourceType("a/b/c");
+        assertEquals("a/b/c", t1.getType());
+        assertEquals("c", t1.getResourceLabel());
+        assertNull(t1.getVersion());
+    }
+
+    @Test
+    public void testSlashVersion() {
+        ResourceType t1 = ResourceType.parseResourceType("a/b/c/1.0.0");
+        assertEquals("a/b/c", t1.getType());
+        assertEquals("c", t1.getResourceLabel());
+        assertEquals("1.0.0", t1.getVersion());
+    }
+
+    @Test
+    public void testOneSegmentNoVersion() {
+        ResourceType t1 = ResourceType.parseResourceType("a");
+        assertEquals("a", t1.getType());
+        assertEquals("a", t1.getResourceLabel());
+        assertNull(t1.getVersion());
+    }
+
+    @Test
+    public void testOneSegmentVersion() {
+        ResourceType t1 = ResourceType.parseResourceType("a/1.2.3");
+        assertEquals("a", t1.getType());
+        assertEquals("a", t1.getResourceLabel());
+        assertEquals("1.2.3", t1.getVersion());
+    }
+
+    @Test
+    public void testDotNoVersion() {
+        ResourceType t1 = ResourceType.parseResourceType("a.b.c");
+        assertEquals("a.b.c", t1.getType());
+        assertEquals("c", t1.getResourceLabel());
+        assertNull(t1.getVersion());
+    }
+
+    @Test
+    public void testDotVersion() {
+        ResourceType t1 = ResourceType.parseResourceType("a.b.c/42.0.0");
+        assertEquals("a.b.c", t1.getType());
+        assertEquals("c", t1.getResourceLabel());
+        assertEquals("42.0.0", t1.getVersion());
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testEmptyString() {
+        ResourceType t1 = ResourceType.parseResourceType(StringUtils.EMPTY);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testNull() {
+        ResourceType t1 = ResourceType.parseResourceType(null);
+    }
+
+}

Reply via email to