You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ro...@apache.org on 2017/11/07 10:20:23 UTC

[sling-org-apache-sling-testing-osgi-mock] 05/09: SLING-6359 context plugin support

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

rombert pushed a commit to annotated tag org.apache.sling.testing.osgi-mock-1.9.0
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-testing-osgi-mock.git

commit 439baa2f73d6adbb561e695e6474be314a28f846
Author: Stefan Seifert <ss...@apache.org>
AuthorDate: Sat Dec 3 13:14:34 2016 +0000

    SLING-6359 context plugin support
    
    git-svn-id: https://svn.apache.org/repos/asf/sling/branches/testing/mocks/osgi-mock-1.x@1772466 13f79535-47bb-0310-9956-ffa450edef68
---
 .../AbstractContextPlugin.java}                    |  41 ++--
 .../osgi/{junit => context}/ContextCallback.java   |  10 +-
 .../testing/mock/osgi/context/ContextPlugin.java   |  59 ++++++
 .../testing/mock/osgi/context/ContextPlugins.java  | 236 +++++++++++++++++++++
 .../testing/mock/osgi/context/package-info.java    |   2 +-
 .../sling/testing/mock/osgi/junit/OsgiContext.java |  86 ++------
 .../mock/osgi/junit/OsgiContextBuilder.java        |  53 +++--
 .../mock/osgi/junit/OsgiContextCallback.java       |   7 +-
 .../mock/osgi/context/ContextPluginsTest.java      | 175 +++++++++++++++
 .../testing/mock/osgi/junit/OsgiContextTest.java   |   1 +
 10 files changed, 562 insertions(+), 108 deletions(-)

diff --git a/src/main/java/org/apache/sling/testing/mock/osgi/junit/CallbackParams.java b/src/main/java/org/apache/sling/testing/mock/osgi/context/AbstractContextPlugin.java
similarity index 52%
rename from src/main/java/org/apache/sling/testing/mock/osgi/junit/CallbackParams.java
rename to src/main/java/org/apache/sling/testing/mock/osgi/context/AbstractContextPlugin.java
index a7d69a1..594f05d 100644
--- a/src/main/java/org/apache/sling/testing/mock/osgi/junit/CallbackParams.java
+++ b/src/main/java/org/apache/sling/testing/mock/osgi/context/AbstractContextPlugin.java
@@ -16,26 +16,35 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.sling.testing.mock.osgi.junit;
+package org.apache.sling.testing.mock.osgi.context;
 
-final class CallbackParams {
+import aQute.bnd.annotation.ConsumerType;
 
-    ContextCallback[] beforeSetUpCallback;
-    ContextCallback[] afterSetUpCallback;
-    ContextCallback[] beforeTearDownCallback;
-    ContextCallback[] afterTearDownCallback;
-    
-    CallbackParams() {
-        // no callbacks
+/**
+ * Default implementation of {@link ContextPlugin}.
+ * @param <T> Context
+ */
+@ConsumerType
+public abstract class AbstractContextPlugin<T extends OsgiContextImpl> implements ContextPlugin<T> {
+
+    @Override
+    public void beforeSetUp(T context) throws Exception {
+        // can be overridden by subclasses
     }
-    
-    CallbackParams(ContextCallback afterSetUpCallback) {
-        this.afterSetUpCallback = new ContextCallback[] { afterSetUpCallback }; 
+
+    @Override
+    public void afterSetUp(T context) throws Exception {
+        // can be overridden by subclasses
     }
-    
-    CallbackParams(ContextCallback afterSetUpCallback, ContextCallback beforeTearDownCallback) {
-        this.afterSetUpCallback = new ContextCallback[] { afterSetUpCallback }; 
-        this.beforeTearDownCallback = new ContextCallback[] { beforeTearDownCallback }; 
+
+    @Override
+    public void beforeTearDown(T context) throws Exception {
+        // can be overridden by subclasses
+    }
+
+    @Override
+    public void afterTearDown(T context) throws Exception {
+        // can be overridden by subclasses
     }
     
 }
diff --git a/src/main/java/org/apache/sling/testing/mock/osgi/junit/ContextCallback.java b/src/main/java/org/apache/sling/testing/mock/osgi/context/ContextCallback.java
similarity index 82%
rename from src/main/java/org/apache/sling/testing/mock/osgi/junit/ContextCallback.java
rename to src/main/java/org/apache/sling/testing/mock/osgi/context/ContextCallback.java
index fee823e..e50514d 100644
--- a/src/main/java/org/apache/sling/testing/mock/osgi/junit/ContextCallback.java
+++ b/src/main/java/org/apache/sling/testing/mock/osgi/context/ContextCallback.java
@@ -16,14 +16,16 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.sling.testing.mock.osgi.junit;
+package org.apache.sling.testing.mock.osgi.context;
 
-import org.apache.sling.testing.mock.osgi.context.OsgiContextImpl;
+import aQute.bnd.annotation.ConsumerType;
 
 /**
- * Callback-interface for application-specific setup and teardown operations to
- * customize the {@link OsgiContext} JUnit rule.
+ * Callback interface for application-specific setup and teardown operations to
+ * customize the mock context.
+ * @param <T> Context
  */
+@ConsumerType
 public interface ContextCallback<T extends OsgiContextImpl> {
 
     /**
diff --git a/src/main/java/org/apache/sling/testing/mock/osgi/context/ContextPlugin.java b/src/main/java/org/apache/sling/testing/mock/osgi/context/ContextPlugin.java
new file mode 100644
index 0000000..df7e458
--- /dev/null
+++ b/src/main/java/org/apache/sling/testing/mock/osgi/context/ContextPlugin.java
@@ -0,0 +1,59 @@
+/*
+ * 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.testing.mock.osgi.context;
+
+import aQute.bnd.annotation.ConsumerType;
+
+/**
+ * Callback plugin for application-specific setup and teardown operations to
+ * customize the mock context.
+ * @param <T> Context
+ */
+@ConsumerType
+public interface ContextPlugin<T extends OsgiContextImpl> {
+
+    /**
+     * Is executed before the built-in setup rules are executed.
+     * @param context OSGi context
+     * @throws Exception exception
+     */
+    void beforeSetUp(T context) throws Exception;
+
+    /**
+     * Is executed after the built-in setup rules are executed.
+     * @param context OSGi context
+     * @throws Exception exception
+     */
+    void afterSetUp(T context) throws Exception;
+
+    /**
+     * Is executed before the built-in teardown rules are executed.
+     * @param context OSGi context
+     * @throws Exception exception
+     */
+    void beforeTearDown(T context) throws Exception;
+
+    /**
+     * Is executed after the built-in teardown rules are executed.
+     * @param context OSGi context
+     * @throws Exception exception
+     */
+    void afterTearDown(T context) throws Exception;
+
+}
diff --git a/src/main/java/org/apache/sling/testing/mock/osgi/context/ContextPlugins.java b/src/main/java/org/apache/sling/testing/mock/osgi/context/ContextPlugins.java
new file mode 100644
index 0000000..553c6dd
--- /dev/null
+++ b/src/main/java/org/apache/sling/testing/mock/osgi/context/ContextPlugins.java
@@ -0,0 +1,236 @@
+/*
+ * 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.testing.mock.osgi.context;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import aQute.bnd.annotation.ProviderType;
+
+/**
+ * Collects list of context plugins.
+ */
+@ProviderType
+public final class ContextPlugins {
+    
+    private List<ContextPlugin<? extends OsgiContextImpl>> plugins = new ArrayList<ContextPlugin<? extends OsgiContextImpl>>();
+
+    /**
+     * Start with empty list.
+     */
+    public ContextPlugins() {
+        // empty list
+    }
+    
+    /**
+     * Start with some callbacks.
+     * @param <T> context type
+     * @param afterSetUpCallback Allows the application to register an own callback function that is called after the built-in setup rules are executed.
+     */
+    @SuppressWarnings("unchecked")
+    public <T extends OsgiContextImpl> ContextPlugins(final ContextCallback<T> afterSetUpCallback) {
+        addAfterSetUpCallback(afterSetUpCallback);
+    }
+    
+    /**
+     * Start with some callbacks.
+     * @param <U> context type
+     * @param <V> context type
+     * @param afterSetUpCallback Allows the application to register an own callback function that is called after the built-in setup rules are executed.
+     * @param beforeTearDownCallback Allows the application to register an own callback function that is called before the built-in teardown rules are executed.
+     */
+    @SuppressWarnings("unchecked")
+    public <U extends OsgiContextImpl, V extends OsgiContextImpl> ContextPlugins(final ContextCallback<U> afterSetUpCallback, final ContextCallback<V> beforeTearDownCallback) {
+        addAfterSetUpCallback(afterSetUpCallback);
+        addBeforeTearDownCallback(beforeTearDownCallback);
+    }
+    
+    /**
+     * Add plugin
+     * @param <T> context type
+     * @param plugin Plugin
+     */
+    @SafeVarargs
+    public final <T extends OsgiContextImpl> void addPlugin(ContextPlugin<T>... plugin) {
+        for (final ContextPlugin<T> item : plugin) {
+            plugins.add(item);
+        }
+    }
+    
+    /**
+     * Add callback
+     * @param <T> context type
+     * @param beforeSetUpCallback Allows the application to register an own callback function that is called before the built-in setup rules are executed.
+     */
+    @SafeVarargs
+    public final <T extends OsgiContextImpl> void addBeforeSetUpCallback(final ContextCallback<T>... beforeSetUpCallback) {
+        for (final ContextCallback<T> item : beforeSetUpCallback) {
+            plugins.add(new AbstractContextPlugin<T>() {
+                @Override
+                public void beforeSetUp(T context) throws Exception {
+                    item.execute(context);
+                }
+                @Override
+                public String toString() {
+                    return item.toString();
+                }
+            });
+        }
+    }
+    
+    /**
+     * Add callback
+     * @param <T> context type
+     * @param afterSetUpCallback Allows the application to register an own callback function that is called after the built-in setup rules are executed.
+     */
+    @SafeVarargs
+    public final <T extends OsgiContextImpl> void addAfterSetUpCallback(final ContextCallback<T>... afterSetUpCallback) {
+        for (final ContextCallback<T> item : afterSetUpCallback) {
+            plugins.add(new AbstractContextPlugin<T>() {
+                @Override
+                public void afterSetUp(T context) throws Exception {
+                    item.execute(context);
+                }
+                @Override
+                public String toString() {
+                    return item.toString();
+                }
+            });
+        }
+    }
+    
+    /**
+     * Add callback
+     * @param <T> context type
+     * @param beforeTearDownCallback Allows the application to register an own callback function that is called before the built-in teardown rules are executed.
+     */
+    @SafeVarargs
+    public final <T extends OsgiContextImpl> void addBeforeTearDownCallback(final ContextCallback<T>... beforeTearDownCallback) {
+        for (final ContextCallback<T> item : beforeTearDownCallback) {
+            plugins.add(new AbstractContextPlugin<T>() {
+                @Override
+                public void beforeTearDown(T context) throws Exception {
+                    item.execute(context);
+                }
+                @Override
+                public String toString() {
+                    return item.toString();
+                }
+            });
+        }
+    }
+    
+    /**
+     * Add callback
+     * @param <T> context type
+     * @param afterTearDownCallback Allows the application to register an own callback function that is after before the built-in teardown rules are executed.
+     */
+    @SafeVarargs
+    public final <T extends OsgiContextImpl> void addAfterTearDownCallback(final ContextCallback<T>... afterTearDownCallback) {
+        for (final ContextCallback<T> item : afterTearDownCallback) {
+            plugins.add(new AbstractContextPlugin<T>() {
+                @Override
+                public void afterTearDown(T context) throws Exception {
+                    item.execute(context);
+                }
+                @Override
+                public String toString() {
+                    return item.toString();
+                }
+            });
+        }
+    }
+    
+    /**
+     * @return All plugins
+     */
+    public Collection<ContextPlugin<? extends OsgiContextImpl>> getPlugins() {
+        return plugins;
+    }
+    
+    /**
+     * Execute all before setup callbacks.
+     * @param <T> context type
+     * @param context Context
+     */
+    @SuppressWarnings("unchecked")
+    public <T extends OsgiContextImpl> void executeBeforeSetUpCallback(final T context) {
+        for (ContextPlugin plugin : plugins) {
+            try {
+                plugin.beforeSetUp(context);
+            }
+            catch (Throwable ex) {
+                throw new RuntimeException("Before setup failed (" + plugin.toString() + "): " + ex.getMessage(), ex);
+            }
+        }
+    }
+
+    /**
+     * Execute all after setup callbacks.
+     * @param <T> context type
+     * @param context Context
+     */
+    @SuppressWarnings("unchecked")
+    public <T extends OsgiContextImpl> void executeAfterSetUpCallback(final T context) {
+        for (ContextPlugin plugin : plugins) {
+            try {
+                plugin.afterSetUp(context);
+            }
+            catch (Throwable ex) {
+                throw new RuntimeException("After setup failed (" + plugin.toString() + "): " + ex.getMessage(), ex);
+            }
+        }
+    }
+
+    /**
+     * Execute all before teardown callbacks.
+     * @param <T> context type
+     * @param context Context
+     */
+    @SuppressWarnings("unchecked")
+    public <T extends OsgiContextImpl> void executeBeforeTearDownCallback(final T context) {
+        for (ContextPlugin plugin : plugins) {
+            try {
+                plugin.beforeTearDown(context);
+            }
+            catch (Throwable ex) {
+                throw new RuntimeException("Before teardown failed (" + plugin.toString() + "): " + ex.getMessage(), ex);
+            }
+        }
+    }
+
+    /**
+     * Execute all after teardown callbacks.
+     * @param <T> context type
+     * @param context Context
+     */
+    @SuppressWarnings("unchecked")
+    public <T extends OsgiContextImpl> void executeAfterTearDownCallback(final T context) {
+        for (ContextPlugin plugin : plugins) {
+            try {
+                plugin.afterTearDown(context);
+            }
+            catch (Throwable ex) {
+                throw new RuntimeException("After teardown failed (" + plugin.toString() + "): " + ex.getMessage(), ex);
+            }
+        }
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/testing/mock/osgi/context/package-info.java b/src/main/java/org/apache/sling/testing/mock/osgi/context/package-info.java
index debaa88..fbd1a7b 100644
--- a/src/main/java/org/apache/sling/testing/mock/osgi/context/package-info.java
+++ b/src/main/java/org/apache/sling/testing/mock/osgi/context/package-info.java
@@ -19,5 +19,5 @@
 /**
  * OSGi context implementation for unit tests.
  */
-@aQute.bnd.annotation.Version("1.1")
+@aQute.bnd.annotation.Version("1.2")
 package org.apache.sling.testing.mock.osgi.context;
diff --git a/src/main/java/org/apache/sling/testing/mock/osgi/junit/OsgiContext.java b/src/main/java/org/apache/sling/testing/mock/osgi/junit/OsgiContext.java
index d64876d..f9718e3 100644
--- a/src/main/java/org/apache/sling/testing/mock/osgi/junit/OsgiContext.java
+++ b/src/main/java/org/apache/sling/testing/mock/osgi/junit/OsgiContext.java
@@ -18,65 +18,73 @@
  */
 package org.apache.sling.testing.mock.osgi.junit;
 
+import org.apache.sling.testing.mock.osgi.context.ContextCallback;
+import org.apache.sling.testing.mock.osgi.context.ContextPlugins;
 import org.apache.sling.testing.mock.osgi.context.OsgiContextImpl;
 import org.junit.rules.ExternalResource;
 import org.junit.rules.TestRule;
 import org.junit.runner.Description;
 import org.junit.runners.model.Statement;
 
+import aQute.bnd.annotation.ProviderType;
+
 /**
  * JUnit rule for setting up and tearing down OSGi context for unit tests.
  */
+@ProviderType
 public final class OsgiContext extends OsgiContextImpl implements TestRule {
 
-    private final CallbackParams callbackParams;
+    private final ContextPlugins plugins;
     private final TestRule delegate;
 
     /**
      * Initialize OSGi context.
      */
     public OsgiContext() {
-        this(new CallbackParams());
+        this(new ContextPlugins());
     }
 
     /**
      * Initialize OSGi context.
+     * @param <T> context type
      * @param afterSetUpCallback Allows the application to register an own callback function that is called after the built-in setup rules are executed.
      */
-    public OsgiContext(final ContextCallback afterSetUpCallback) {
-        this(new CallbackParams(afterSetUpCallback));
+    public <T extends OsgiContextImpl> OsgiContext(final ContextCallback<T> afterSetUpCallback) {
+        this(new ContextPlugins(afterSetUpCallback));
     }
 
     /**
      * Initialize OSGi context.
+     * @param <U> context type
+     * @param <V> context type
      * @param afterSetUpCallback Allows the application to register an own callback function that is called after the built-in setup rules are executed.
      * @param beforeTearDownCallback Allows the application to register an own callback function that is called before the built-in teardown rules are executed.
      */
-    public OsgiContext(final ContextCallback afterSetUpCallback, final ContextCallback beforeTearDownCallback) {
-        this(new CallbackParams(afterSetUpCallback, beforeTearDownCallback));
+    public <U extends OsgiContextImpl, V extends OsgiContextImpl> OsgiContext(final ContextCallback<U> afterSetUpCallback, final ContextCallback<V> beforeTearDownCallback) {
+        this(new ContextPlugins(afterSetUpCallback, beforeTearDownCallback));
     }
 
     /**
      * Initialize OSGi context with resource resolver type.
-     * @param callbackParams Callback parameters
+     * @param contextPlugins Context plugins
      */
-    OsgiContext(final CallbackParams callbackParams) {
-        this.callbackParams = callbackParams;
+    OsgiContext(final ContextPlugins contextPlugins) {
+        this.plugins = contextPlugins;
 
         // wrap {@link ExternalResource} rule executes each test method once
         this.delegate = new ExternalResource() {
             @Override
             protected void before() {
-                OsgiContext.this.executeBeforeSetUpCallback();
+                plugins.executeBeforeSetUpCallback(OsgiContext.this);
                 OsgiContext.this.setUp();
-                OsgiContext.this.executeAfterSetUpCallback();
+                plugins.executeAfterSetUpCallback(OsgiContext.this);
             }
 
             @Override
             protected void after() {
-                OsgiContext.this.executeBeforeTearDownCallback();
+                plugins.executeBeforeTearDownCallback(OsgiContext.this);
                 OsgiContext.this.tearDown();
-                OsgiContext.this.executeAfterTearDownCallback();
+                plugins.executeAfterTearDownCallback(OsgiContext.this);
             }
         };
     }
@@ -86,56 +94,4 @@ public final class OsgiContext extends OsgiContextImpl implements TestRule {
         return this.delegate.apply(base, description);
     }
 
-    @SuppressWarnings("unchecked")
-    private void executeBeforeSetUpCallback() {
-        if (callbackParams.beforeSetUpCallback != null) {
-            try {
-                for (ContextCallback callback : callbackParams.beforeSetUpCallback) {
-                    callback.execute(this);
-                }
-            } catch (Throwable ex) {
-                throw new RuntimeException("Before setup failed: " + ex.getMessage(), ex);
-            }
-        }
-    }
-
-    @SuppressWarnings("unchecked")
-    private void executeAfterSetUpCallback() {
-        if (callbackParams.afterSetUpCallback != null) {
-            try {
-                for (ContextCallback callback : callbackParams.afterSetUpCallback) {
-                    callback.execute(this);
-                }
-            } catch (Throwable ex) {
-                throw new RuntimeException("After setup failed: " + ex.getMessage(), ex);
-            }
-        }
-    }
-
-    @SuppressWarnings("unchecked")
-    private void executeBeforeTearDownCallback() {
-        if (callbackParams.beforeTearDownCallback != null) {
-            try {
-                for (ContextCallback callback : callbackParams.beforeTearDownCallback) {
-                    callback.execute(this);
-                }
-            } catch (Throwable ex) {
-                throw new RuntimeException("Before teardown failed: " + ex.getMessage(), ex);
-            }
-        }
-    }
-
-    @SuppressWarnings("unchecked")
-    private void executeAfterTearDownCallback() {
-        if (callbackParams.afterTearDownCallback != null) {
-            try {
-                for (ContextCallback callback : callbackParams.afterTearDownCallback) {
-                    callback.execute(this);
-                }
-            } catch (Throwable ex) {
-                throw new RuntimeException("After teardown failed: " + ex.getMessage(), ex);
-            }
-        }
-    }
-
 }
diff --git a/src/main/java/org/apache/sling/testing/mock/osgi/junit/OsgiContextBuilder.java b/src/main/java/org/apache/sling/testing/mock/osgi/junit/OsgiContextBuilder.java
index f6d6305..05d2220 100644
--- a/src/main/java/org/apache/sling/testing/mock/osgi/junit/OsgiContextBuilder.java
+++ b/src/main/java/org/apache/sling/testing/mock/osgi/junit/OsgiContextBuilder.java
@@ -18,12 +18,20 @@
  */
 package org.apache.sling.testing.mock.osgi.junit;
 
+import org.apache.sling.testing.mock.osgi.context.ContextCallback;
+import org.apache.sling.testing.mock.osgi.context.ContextPlugin;
+import org.apache.sling.testing.mock.osgi.context.ContextPlugins;
+import org.apache.sling.testing.mock.osgi.context.OsgiContextImpl;
+
+import aQute.bnd.annotation.ProviderType;
+
 /**
  * Builder class for creating {@link OsgiContext} instances with different sets of parameters.
  */
+@ProviderType
 public final class OsgiContextBuilder {
     
-    private final CallbackParams callbackParams = new CallbackParams();
+    private final ContextPlugins plugins = new ContextPlugins();
     
     /**
      * Create builder with default resource resolver type.
@@ -31,54 +39,57 @@ public final class OsgiContextBuilder {
     public OsgiContextBuilder() {}
     
     /**
-     * @param afterSetUpCallback Allows the application to register an own callback function that is called after the built-in setup rules are executed.
+     * @param <T> context type
+     * @param plugin Context plugin which listens to context lifecycle events.
      * @return this
      */
-    public OsgiContextBuilder setUp(ContextCallback... afterSetUpCallback) {
-        return afterSetUp(afterSetUpCallback);
+    @SafeVarargs
+    public final <T extends OsgiContextImpl> OsgiContextBuilder plugin(ContextPlugin<T>... plugin) {
+        plugins.addPlugin(plugin);
+        return this;
     }
 
     /**
+     * @param <T> context type
      * @param beforeSetUpCallback Allows the application to register an own callback function that is called before the built-in setup rules are executed.
      * @return this
      */
-    public OsgiContextBuilder beforeSetUp(ContextCallback... beforeSetUpCallback) {
-        callbackParams.beforeSetUpCallback = beforeSetUpCallback;
+    @SafeVarargs
+    public final <T extends OsgiContextImpl> OsgiContextBuilder beforeSetUp(ContextCallback<T>... beforeSetUpCallback) {
+        plugins.addBeforeSetUpCallback(beforeSetUpCallback);
         return this;
     }
 
     /**
+     * @param <T> context type
      * @param afterSetUpCallback Allows the application to register an own callback function that is called after the built-in setup rules are executed.
      * @return this
      */
-    public OsgiContextBuilder afterSetUp(ContextCallback... afterSetUpCallback) {
-        callbackParams.afterSetUpCallback = afterSetUpCallback;
+    @SafeVarargs
+    public final <T extends OsgiContextImpl> OsgiContextBuilder afterSetUp(ContextCallback<T>... afterSetUpCallback) {
+        plugins.addAfterSetUpCallback(afterSetUpCallback);
         return this;
     }
 
     /**
+     * @param <T> context type
      * @param beforeTearDownCallback Allows the application to register an own callback function that is called before the built-in teardown rules are executed.
      * @return this
      */
-    public OsgiContextBuilder tearDown(ContextCallback... beforeTearDownCallback) {
-        return beforeTearDown(beforeTearDownCallback);
-    }
-
-    /**
-     * @param beforeTearDownCallback Allows the application to register an own callback function that is called before the built-in teardown rules are executed.
-     * @return this
-     */
-    public OsgiContextBuilder beforeTearDown(ContextCallback... beforeTearDownCallback) {
-        callbackParams.beforeTearDownCallback = beforeTearDownCallback;
+    @SafeVarargs
+    public final <T extends OsgiContextImpl> OsgiContextBuilder beforeTearDown(ContextCallback<T>... beforeTearDownCallback) {
+        plugins.addBeforeTearDownCallback(beforeTearDownCallback);
         return this;
     }
 
     /**
+     * @param <T> context type
      * @param afterTearDownCallback Allows the application to register an own callback function that is after before the built-in teardown rules are executed.
      * @return this
      */
-    public OsgiContextBuilder afterTearDown(ContextCallback... afterTearDownCallback) {
-        callbackParams.afterTearDownCallback = afterTearDownCallback;
+    @SafeVarargs
+    public final <T extends OsgiContextImpl> OsgiContextBuilder afterTearDown(ContextCallback<T>... afterTearDownCallback) {
+        plugins.addAfterTearDownCallback(afterTearDownCallback);
         return this;
     }
 
@@ -86,7 +97,7 @@ public final class OsgiContextBuilder {
      * @return Build {@link OsgiContext} instance.
      */
     public OsgiContext build() {
-        return new OsgiContext(callbackParams);
+        return new OsgiContext(plugins);
     }
     
 }
diff --git a/src/main/java/org/apache/sling/testing/mock/osgi/junit/OsgiContextCallback.java b/src/main/java/org/apache/sling/testing/mock/osgi/junit/OsgiContextCallback.java
index 779e86f..946a9e0 100644
--- a/src/main/java/org/apache/sling/testing/mock/osgi/junit/OsgiContextCallback.java
+++ b/src/main/java/org/apache/sling/testing/mock/osgi/junit/OsgiContextCallback.java
@@ -18,10 +18,15 @@
  */
 package org.apache.sling.testing.mock.osgi.junit;
 
+import org.apache.sling.testing.mock.osgi.context.ContextCallback;
+
+import aQute.bnd.annotation.ConsumerType;
+
 /**
- * Callback-interface for application-specific setup and teardown operations to
+ * Callback interface for application-specific setup and teardown operations to
  * customize the {@link OsgiContext} JUnit rule.
  */
+@ConsumerType
 public interface OsgiContextCallback extends ContextCallback<OsgiContext> {
 
     // specialized version of ContextCallback
diff --git a/src/test/java/org/apache/sling/testing/mock/osgi/context/ContextPluginsTest.java b/src/test/java/org/apache/sling/testing/mock/osgi/context/ContextPluginsTest.java
new file mode 100644
index 0000000..c6b5462
--- /dev/null
+++ b/src/test/java/org/apache/sling/testing/mock/osgi/context/ContextPluginsTest.java
@@ -0,0 +1,175 @@
+/*
+ * 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.testing.mock.osgi.context;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+import org.apache.sling.testing.mock.osgi.junit.OsgiContext;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+
+@RunWith(MockitoJUnitRunner.class)
+@SuppressWarnings("unchecked")
+public class ContextPluginsTest {
+    
+    private OsgiContext context = new OsgiContext();
+
+    @Mock
+    private ContextPlugin plugin1;
+    @Mock
+    private ContextPlugin plugin2;
+    @Mock
+    private ContextCallback callback1;
+    @Mock
+    private ContextCallback callback2;
+    
+    @Test
+    public void testConstructorSetUp() throws Exception {
+        ContextPlugins underTest = new ContextPlugins(callback1);
+        
+        assertEquals(1, underTest.getPlugins().size());
+        
+        underTest.executeAfterSetUpCallback(context);
+        
+        verify(callback1, times(1)).execute(context);
+    }
+
+    @Test
+    public void testConstructorSetUpTearDown() throws Exception {
+        ContextPlugins underTest = new ContextPlugins(callback1, callback2);
+        
+        assertEquals(2, underTest.getPlugins().size());
+        
+        underTest.executeAfterSetUpCallback(context);
+        underTest.executeBeforeTearDownCallback(context);
+
+        verify(callback1, times(1)).execute(context);
+        verify(callback2, times(1)).execute(context);
+    }
+
+    @Test
+    public void testExecuteBeforeSetUpCallback() throws Exception {
+        ContextPlugins underTest = new ContextPlugins();
+        underTest.addPlugin(plugin1, plugin2);
+        
+        assertEquals(2, underTest.getPlugins().size());
+        
+        underTest.executeBeforeSetUpCallback(context);
+        verify(plugin1, times(1)).beforeSetUp(context);
+        verify(plugin2, times(1)).beforeSetUp(context);
+        verifyNoMoreInteractions(plugin1);
+        verifyNoMoreInteractions(plugin2);
+    }
+
+    @Test
+    public void testExecuteAfterSetUpCallback() throws Exception {
+        ContextPlugins underTest = new ContextPlugins();
+        underTest.addPlugin(plugin1, plugin2);
+        
+        assertEquals(2, underTest.getPlugins().size());
+        
+        underTest.executeAfterSetUpCallback(context);
+        verify(plugin1, times(1)).afterSetUp(context);
+        verify(plugin2, times(1)).afterSetUp(context);
+        verifyNoMoreInteractions(plugin1);
+        verifyNoMoreInteractions(plugin2);
+    }
+
+    @Test
+    public void testExecuteBeforeTearDownCallback() throws Exception {
+        ContextPlugins underTest = new ContextPlugins();
+        underTest.addPlugin(plugin1, plugin2);
+        
+        assertEquals(2, underTest.getPlugins().size());
+        
+        underTest.executeBeforeTearDownCallback(context);
+        verify(plugin1, times(1)).beforeTearDown(context);
+        verify(plugin2, times(1)).beforeTearDown(context);
+        verifyNoMoreInteractions(plugin1);
+        verifyNoMoreInteractions(plugin2);
+    }
+
+    @Test
+    public void testExecuteAfterTearDownCallback() throws Exception {
+        ContextPlugins underTest = new ContextPlugins();
+        underTest.addPlugin(plugin1, plugin2);
+        
+        assertEquals(2, underTest.getPlugins().size());
+        
+        underTest.executeAfterTearDownCallback(context);
+        verify(plugin1, times(1)).afterTearDown(context);
+        verify(plugin2, times(1)).afterTearDown(context);
+        verifyNoMoreInteractions(plugin1);
+        verifyNoMoreInteractions(plugin2);
+    }
+
+    @Test
+    public void testAddBeforeSetUpCallback() throws Exception {
+        ContextPlugins underTest = new ContextPlugins();
+        underTest.addBeforeSetUpCallback(callback1, callback2);
+        
+        assertEquals(2, underTest.getPlugins().size());
+        
+        underTest.executeBeforeSetUpCallback(context);
+        verify(callback1, times(1)).execute(context);
+        verify(callback2, times(1)).execute(context);
+    }
+
+    @Test
+    public void testAddAfterSetUpCallback() throws Exception {
+        ContextPlugins underTest = new ContextPlugins();
+        underTest.addAfterSetUpCallback(callback1, callback2);
+        
+        assertEquals(2, underTest.getPlugins().size());
+        
+        underTest.executeAfterSetUpCallback(context);
+        verify(callback1, times(1)).execute(context);
+        verify(callback2, times(1)).execute(context);
+    }
+
+    @Test
+    public void testAddBeforeTearDownCallback() throws Exception {
+        ContextPlugins underTest = new ContextPlugins();
+        underTest.addBeforeTearDownCallback(callback1, callback2);
+        
+        assertEquals(2, underTest.getPlugins().size());
+        
+        underTest.executeBeforeTearDownCallback(context);
+        verify(callback1, times(1)).execute(context);
+        verify(callback2, times(1)).execute(context);
+    }
+
+    @Test
+    public void testAddAfterTearDownCallback() throws Exception {
+        ContextPlugins underTest = new ContextPlugins();
+        underTest.addAfterTearDownCallback(callback1, callback2);
+        
+        assertEquals(2, underTest.getPlugins().size());
+        
+        underTest.executeAfterTearDownCallback(context);
+        verify(callback1, times(1)).execute(context);
+        verify(callback2, times(1)).execute(context);
+    }
+
+}
diff --git a/src/test/java/org/apache/sling/testing/mock/osgi/junit/OsgiContextTest.java b/src/test/java/org/apache/sling/testing/mock/osgi/junit/OsgiContextTest.java
index 840e951..f361345 100644
--- a/src/test/java/org/apache/sling/testing/mock/osgi/junit/OsgiContextTest.java
+++ b/src/test/java/org/apache/sling/testing/mock/osgi/junit/OsgiContextTest.java
@@ -37,6 +37,7 @@ public class OsgiContextTest {
     private final OsgiContextCallback contextAfterTeardown = mock(OsgiContextCallback.class);
 
     // Run all unit tests for each resource resolver types listed here
+    @SuppressWarnings("unchecked")
     @Rule
     public OsgiContext context = new OsgiContextBuilder()
         .beforeSetUp(contextBeforeSetup)

-- 
To stop receiving notification emails like this one, please contact
"commits@sling.apache.org" <co...@sling.apache.org>.