SLING-3751 - Create a JUnit rule to inject a service reference into a test

* applied patch from Francesco Mari (thx!)

git-svn-id: https://svn.apache.org/repos/asf/sling/trunk@1609055 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/pom.xml b/pom.xml
index 60c0222..f06bbeb 100644
--- a/pom.xml
+++ b/pom.xml
@@ -35,7 +35,8 @@
     <description>Runs JUnit tests in an OSGi framework and provides the JUnit libraries</description>
     
     <properties>
-        <junit.version>4.8.2</junit.version>
+        <junit.version>4.11</junit.version>
+        <hamcrest.version>1.3</hamcrest.version>
         <jacoco.version>0.6.2.201302030002</jacoco.version>
     </properties>
 
@@ -60,6 +61,7 @@
                         <Bundle-Activator>org.apache.sling.junit.Activator</Bundle-Activator>
                         <Export-Package>
                             org.apache.sling.junit;version=1.2.0,
+                            org.apache.sling.junit.rules;version=1.0.10,
                             org.apache.sling.junit.annotations;version=1.0.8,
                         </Export-Package>
                         <_exportcontents>
@@ -68,10 +70,16 @@
                             org.junit.matchers.*;version=${junit.version},
                             org.junit.rules.*;version=${junit.version},
                             org.junit.runner.*;version=${junit.version},
-                            org.junit.runners.*;version=${junit.version}
+                            org.junit.runners.*;version=${junit.version},
+                            org.hamcrest.*;version=${hamcrest.version},
+                            org.hamcrest.core.*;version=${hamcrest.version}
                         </_exportcontents>
                         <Private-Package>org.apache.sling.junit.impl.*</Private-Package>
-                        <Embed-Dependency>org.jacoco.agent;classifier=runtime;inline=org/jacoco/agent/rt/IAgent.class,*;artifactId=junit</Embed-Dependency>
+                        <Embed-Dependency>
+                            org.jacoco.agent;classifier=runtime;inline=org/jacoco/agent/rt/IAgent.class,
+                            *;artifactId=junit,
+                            *;artifactId=hamcrest-core
+                        </Embed-Dependency>
                     </instructions>
                 </configuration>
             </plugin>
@@ -152,6 +160,11 @@
             <version>${jacoco.version}</version>
             <scope>provided</scope>
         </dependency>
-
+ 		<dependency>
+            <groupId>org.hamcrest</groupId>
+            <artifactId>hamcrest-core</artifactId>
+            <version>${hamcrest.version}</version>
+            <scope>provided</scope>
+        </dependency>
     </dependencies>
 </project>
diff --git a/src/main/java/org/apache/sling/junit/rules/Service.java b/src/main/java/org/apache/sling/junit/rules/Service.java
new file mode 100644
index 0000000..d580149
--- /dev/null
+++ b/src/main/java/org/apache/sling/junit/rules/Service.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.sling.junit.rules;
+
+import org.apache.sling.junit.Activator;
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * Allows a test class to obtain a reference to an OSGi service. This rule embodies the logic to get a bundle context,
+ * obtain a service reference, fetch the reference to the object and perform the proper cleanup after the test has run.
+ */
+public class Service implements TestRule {
+
+    private final Class<?> serviceClass;
+
+    private Object service;
+
+    public Service(Class<?> serviceClass) {
+        this.serviceClass = serviceClass;
+    }
+
+    public Statement apply(final Statement base, final Description description) {
+        return new Statement() {
+
+            @Override
+            public void evaluate() throws Throwable {
+                final BundleContext bundleContext = Activator.getBundleContext();
+
+                if (bundleContext == null) {
+                    throw new IllegalStateException("unable to obtain a bundle context");
+                }
+
+                final ServiceReference serviceReference = bundleContext.getServiceReference(serviceClass.getName());
+
+                if (serviceReference == null) {
+                    throw new IllegalStateException("unable to get a service reference");
+                }
+
+                final Object service = bundleContext.getService(serviceReference);
+
+                if (service == null) {
+                    throw new IllegalStateException("unable to get an instance of the service");
+                }
+
+                Service.this.service = serviceClass.cast(service);
+
+                try {
+                    base.evaluate();
+                } finally {
+                    Service.this.service = null;
+                    bundleContext.ungetService(serviceReference);
+                }
+            }
+
+        };
+    }
+
+    /**
+     * Return the service object.
+     *
+     * @param serviceClass Use this class to perform a cast before the object is returned.
+     * @param <T>          The type of the service.
+     * @return The service object.
+     */
+    public <T> T getService(Class<T> serviceClass) {
+        return serviceClass.cast(service);
+    }
+
+}