You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@aries.apache.org by ti...@apache.org on 2010/08/12 17:06:31 UTC
svn commit: r984819 - in /incubator/aries/trunk/quiesce:
quiesce-manager-itest/ quiesce-manager-itest/src/
quiesce-manager-itest/src/test/ quiesce-manager-itest/src/test/java/
quiesce-manager-itest/src/test/java/org/
quiesce-manager-itest/src/test/java...
Author: timothyjward
Date: Thu Aug 12 15:06:30 2010
New Revision: 984819
URL: http://svn.apache.org/viewvc?rev=984819&view=rev
Log:
ARIES-371 - Quiesce Manager implementation (from Hannah Ramlee)
Added:
incubator/aries/trunk/quiesce/quiesce-manager/ (with props)
incubator/aries/trunk/quiesce/quiesce-manager-itest/ (with props)
incubator/aries/trunk/quiesce/quiesce-manager-itest/pom.xml
incubator/aries/trunk/quiesce/quiesce-manager-itest/src/
incubator/aries/trunk/quiesce/quiesce-manager-itest/src/test/
incubator/aries/trunk/quiesce/quiesce-manager-itest/src/test/java/
incubator/aries/trunk/quiesce/quiesce-manager-itest/src/test/java/org/
incubator/aries/trunk/quiesce/quiesce-manager-itest/src/test/java/org/apache/
incubator/aries/trunk/quiesce/quiesce-manager-itest/src/test/java/org/apache/aries/
incubator/aries/trunk/quiesce/quiesce-manager-itest/src/test/java/org/apache/aries/quiesce/
incubator/aries/trunk/quiesce/quiesce-manager-itest/src/test/java/org/apache/aries/quiesce/manager/
incubator/aries/trunk/quiesce/quiesce-manager-itest/src/test/java/org/apache/aries/quiesce/manager/itest/
incubator/aries/trunk/quiesce/quiesce-manager-itest/src/test/java/org/apache/aries/quiesce/manager/itest/MockQuiesceParticipant.java
incubator/aries/trunk/quiesce/quiesce-manager-itest/src/test/java/org/apache/aries/quiesce/manager/itest/QuiesceManagerTest.java
incubator/aries/trunk/quiesce/quiesce-manager/pom.xml
incubator/aries/trunk/quiesce/quiesce-manager/src/
incubator/aries/trunk/quiesce/quiesce-manager/src/main/
incubator/aries/trunk/quiesce/quiesce-manager/src/main/java/
incubator/aries/trunk/quiesce/quiesce-manager/src/main/java/org/
incubator/aries/trunk/quiesce/quiesce-manager/src/main/java/org/apache/
incubator/aries/trunk/quiesce/quiesce-manager/src/main/java/org/apache/aries/
incubator/aries/trunk/quiesce/quiesce-manager/src/main/java/org/apache/aries/quiesce/
incubator/aries/trunk/quiesce/quiesce-manager/src/main/java/org/apache/aries/quiesce/manager/
incubator/aries/trunk/quiesce/quiesce-manager/src/main/java/org/apache/aries/quiesce/manager/impl/
incubator/aries/trunk/quiesce/quiesce-manager/src/main/java/org/apache/aries/quiesce/manager/impl/Activator.java
incubator/aries/trunk/quiesce/quiesce-manager/src/main/java/org/apache/aries/quiesce/manager/impl/QuiesceManagerImpl.java
Propchange: incubator/aries/trunk/quiesce/quiesce-manager/
------------------------------------------------------------------------------
--- svn:ignore (added)
+++ svn:ignore Thu Aug 12 15:06:30 2010
@@ -0,0 +1 @@
+target
Propchange: incubator/aries/trunk/quiesce/quiesce-manager-itest/
------------------------------------------------------------------------------
--- svn:ignore (added)
+++ svn:ignore Thu Aug 12 15:06:30 2010
@@ -0,0 +1 @@
+target
Added: incubator/aries/trunk/quiesce/quiesce-manager-itest/pom.xml
URL: http://svn.apache.org/viewvc/incubator/aries/trunk/quiesce/quiesce-manager-itest/pom.xml?rev=984819&view=auto
==============================================================================
--- incubator/aries/trunk/quiesce/quiesce-manager-itest/pom.xml (added)
+++ incubator/aries/trunk/quiesce/quiesce-manager-itest/pom.xml Thu Aug 12 15:06:30 2010
@@ -0,0 +1,157 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <parent>
+ <artifactId>quiesce</artifactId>
+ <groupId>org.apache.aries.quiesce</groupId>
+ <version>0.2-incubating-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+ <artifactId>org.apache.aries.quiesce.manager.itest</artifactId>
+ <name>Aries Quiesce Manager iTests</name>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.aries.quiesce</groupId>
+ <artifactId>org.apache.aries.quiesce.api</artifactId>
+ <scope>provided</scope>
+ <version>${version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.aries.quiesce</groupId>
+ <artifactId>org.apache.aries.quiesce.manager</artifactId>
+ <scope>provided</scope>
+ <version>${version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.compendium</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.ops4j.pax.logging</groupId>
+ <artifactId>pax-logging-api</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.ops4j.pax.logging</groupId>
+ <artifactId>pax-logging-service</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.ops4j.pax.exam</groupId>
+ <artifactId>pax-exam-container-default</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.ops4j.pax.exam</groupId>
+ <artifactId>pax-exam-junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.ops4j.pax.url</groupId>
+ <artifactId>pax-url-mvn</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.aries</groupId>
+ <artifactId>org.apache.aries.util</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.configadmin</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>commons-lang</groupId>
+ <artifactId>commons-lang</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>commons-collections</groupId>
+ <artifactId>commons-collections</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>commons-pool</groupId>
+ <artifactId>commons-pool</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.serp</artifactId>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.servicemix.tooling</groupId>
+ <artifactId>depends-maven-plugin</artifactId>
+ <version>1.2</version>
+ <executions>
+ <execution>
+ <id>generate-depends-file</id>
+ <goals>
+ <goal>generate-depends-file</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+
+ <profiles>
+ <profile>
+ <id>ci-build-profile</id>
+ <activation>
+ <property>
+ <name>maven.repo.local</name>
+ </property>
+ </activation>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <!--
+ when the local repo location has been specified, we need
+ to pass on this information to PAX mvn url
+ -->
+ <argLine>-Dorg.ops4j.pax.url.mvn.localRepository=${maven.repo.local}
+ </argLine>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ </profiles>
+</project>
Added: incubator/aries/trunk/quiesce/quiesce-manager-itest/src/test/java/org/apache/aries/quiesce/manager/itest/MockQuiesceParticipant.java
URL: http://svn.apache.org/viewvc/incubator/aries/trunk/quiesce/quiesce-manager-itest/src/test/java/org/apache/aries/quiesce/manager/itest/MockQuiesceParticipant.java?rev=984819&view=auto
==============================================================================
--- incubator/aries/trunk/quiesce/quiesce-manager-itest/src/test/java/org/apache/aries/quiesce/manager/itest/MockQuiesceParticipant.java (added)
+++ incubator/aries/trunk/quiesce/quiesce-manager-itest/src/test/java/org/apache/aries/quiesce/manager/itest/MockQuiesceParticipant.java Thu Aug 12 15:06:30 2010
@@ -0,0 +1,77 @@
+package org.apache.aries.quiesce.manager.itest;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import org.apache.aries.quiesce.manager.QuiesceCallback;
+import org.apache.aries.quiesce.participant.QuiesceParticipant;
+import org.osgi.framework.Bundle;
+
+public class MockQuiesceParticipant implements QuiesceParticipant {
+
+ public static final int RETURNIMMEDIATELY = 0;
+ public static final int NEVERRETURN = 1;
+ public static final int WAIT = 2;
+ private int behaviour;
+ private List<QuiesceCallback> callbacks = new ArrayList<QuiesceCallback>();
+ private ExecutorService executor = Executors.newCachedThreadPool();
+ private int started = 0;
+ private int finished = 0;
+
+ public MockQuiesceParticipant( int i ) {
+ behaviour = i;
+ }
+
+ public void quiesce(final QuiesceCallback callback, final List<Bundle> bundlesToQuiesce) {
+ Runnable command = new Runnable() {
+ public void run() {
+ started += 1;
+ callbacks.add(callback);
+ switch (behaviour) {
+ case 0:
+ //return immediately
+ System.out.println("MockParticipant: return immediately");
+ callback.bundleQuiesced(bundlesToQuiesce.toArray(new Bundle[bundlesToQuiesce.size()]));
+ callbacks.remove(callback);
+ finished += 1;
+ break;
+ case 1:
+ //just don't do anything
+ System.out.println("MockParticipant: just don't do anything");
+ break;
+ case 2:
+ //Wait for 5s then quiesce
+ System.out.println("MockParticipant: Wait for 5s then quiesce");
+ try {
+ Thread.sleep(5000);
+ } catch (InterruptedException e) {
+ }
+ callback.bundleQuiesced(bundlesToQuiesce.toArray(new Bundle[bundlesToQuiesce.size()]));
+ callbacks.remove(callback);
+ finished += 1;
+ break;
+ default:
+ //Unknown behaviour, don't do anything
+ }
+ }
+ };
+ executor.execute(command);
+ }
+
+ public int getStartedCount() {
+ return started;
+ }
+
+ public int getFinishedCount() {
+ return finished;
+ }
+
+ public synchronized void reset() {
+ started = 0;
+ finished = 0;
+ }
+
+
+}
Added: incubator/aries/trunk/quiesce/quiesce-manager-itest/src/test/java/org/apache/aries/quiesce/manager/itest/QuiesceManagerTest.java
URL: http://svn.apache.org/viewvc/incubator/aries/trunk/quiesce/quiesce-manager-itest/src/test/java/org/apache/aries/quiesce/manager/itest/QuiesceManagerTest.java?rev=984819&view=auto
==============================================================================
--- incubator/aries/trunk/quiesce/quiesce-manager-itest/src/test/java/org/apache/aries/quiesce/manager/itest/QuiesceManagerTest.java (added)
+++ incubator/aries/trunk/quiesce/quiesce-manager-itest/src/test/java/org/apache/aries/quiesce/manager/itest/QuiesceManagerTest.java Thu Aug 12 15:06:30 2010
@@ -0,0 +1,384 @@
+/* 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.aries.quiesce.manager.itest;
+
+import static org.junit.Assert.assertTrue;
+import static org.ops4j.pax.exam.CoreOptions.equinox;
+import static org.ops4j.pax.exam.CoreOptions.options;
+import static org.ops4j.pax.exam.CoreOptions.systemProperty;
+import static org.ops4j.pax.exam.CoreOptions.wrappedBundle;
+import static org.ops4j.pax.exam.OptionUtils.combine;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.aries.quiesce.manager.QuiesceManager;
+import org.apache.aries.quiesce.participant.QuiesceParticipant;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.CoreOptions;
+import org.ops4j.pax.exam.Inject;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.junit.JUnit4TestRunner;
+import org.ops4j.pax.exam.options.BootDelegationOption;
+import org.ops4j.pax.exam.options.MavenArtifactProvisionOption;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Filter;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.framework.Version;
+import org.osgi.util.tracker.ServiceTracker;
+
+@RunWith(JUnit4TestRunner.class)
+public class QuiesceManagerTest {
+ public static final long DEFAULT_TIMEOUT = 30000;
+ private QuiesceManager manager;
+ private Bundle b1;
+ private Bundle b2;
+ private Bundle b3;
+ private long timeoutTime;
+ private List<Bundle> bundleList;
+ private MockQuiesceParticipant participant1;
+ private MockQuiesceParticipant participant2;
+ private MockQuiesceParticipant participant3;
+
+
+ @Inject
+ protected BundleContext bundleContext;
+
+ @Before
+ public void setup() {
+ manager = getOsgiService(QuiesceManager.class);
+ b1 = bundleContext.getBundle(5);
+ b2 = bundleContext.getBundle(6);
+ b3 = bundleContext.getBundle(10);
+ participant1 = new MockQuiesceParticipant(MockQuiesceParticipant.RETURNIMMEDIATELY);
+ participant2 = new MockQuiesceParticipant(MockQuiesceParticipant.NEVERRETURN);
+ participant3 = new MockQuiesceParticipant(MockQuiesceParticipant.WAIT);
+
+ }
+
+ @Test
+ public void testNullSafe() throws Exception {
+ //Check we're null safe
+ manager.quiesce(null);
+ }
+
+ @Test
+ public void testNoParticipants() throws Exception {
+ bundleList = new ArrayList<Bundle>();
+ bundleList.add(b1);
+ assertTrue("Bundle "+b1.getSymbolicName()+" should be in active state", b1.getState() == Bundle.ACTIVE);
+ //Try quiescing one bundle with no participants
+ manager.quiesce(bundleList);
+ //quiesce is non-blocking so what do we do?
+ //verify bundle is no longer active
+ timeoutTime = System.currentTimeMillis()+5000;
+ while (System.currentTimeMillis() < timeoutTime && b1.getState() == Bundle.ACTIVE){
+ Thread.sleep(500);
+ }
+ assertTrue("Bundle "+b1.getSymbolicName()+" should not be in active state", b1.getState() != Bundle.ACTIVE);
+ b1.start();
+ }
+
+ @Test
+ public void testImmediateReturn() throws Exception {
+ bundleList = new ArrayList<Bundle>();
+ bundleList.add(b1);
+ //Register a mock participant which will report back quiesced immediately
+ ServiceRegistration sr = bundleContext.registerService(QuiesceParticipant.class.getName(), participant1, null);
+ //Try quiescing the bundle with immediate return
+ assertTrue("Bundle "+b1.getSymbolicName()+" should be in active state", b1.getState() == Bundle.ACTIVE);
+ manager.quiesce(1000,bundleList);
+ timeoutTime = System.currentTimeMillis()+5000;
+ while (System.currentTimeMillis() < timeoutTime && b1.getState() == Bundle.ACTIVE){
+ Thread.sleep(500);
+ }
+ assertTrue("Participant should have finished once", participant1.getFinishedCount() == 1);
+ assertTrue("Bundle "+b1.getSymbolicName()+" should not be in active state", b1.getState() != Bundle.ACTIVE);
+ b1.start();
+ sr.unregister();
+ participant1.reset();
+ }
+
+ @Test
+ public void testNoReturn() throws Exception {
+ //Register a mock participant which won't respond
+ ServiceRegistration sr = bundleContext.registerService(QuiesceParticipant.class.getName(), participant2, null);
+ //recreate the list as it may have been emptied?
+ bundleList = new ArrayList<Bundle>();
+ bundleList.add(b1);
+
+ //Try quiescing the bundle with no return
+ assertTrue("Bundle "+b1.getSymbolicName()+" should be in active state", b1.getState() == Bundle.ACTIVE);
+ manager.quiesce(1000,bundleList);
+ timeoutTime = System.currentTimeMillis()+5000;
+ while (System.currentTimeMillis() < timeoutTime && b1.getState() == Bundle.ACTIVE){
+ Thread.sleep(500);
+ }
+ assertTrue("Participant should have started once", participant2.getStartedCount() == 1);
+ assertTrue("Participant should not have finished", participant2.getFinishedCount() == 0);
+ assertTrue("Bundle "+b1.getSymbolicName()+" should not be in active state", b1.getState() != Bundle.ACTIVE);
+ b1.start();
+ sr.unregister();
+ participant2.reset();
+ }
+
+ @Test
+ public void testWaitAShortTime() throws Exception {
+ //Try quiescing where participant takes 5s to do the work. We should get InterruptedException
+ ServiceRegistration sr = bundleContext.registerService(QuiesceParticipant.class.getName(), participant3, null);
+ //recreate the list as it may have been emptied?
+ bundleList = new ArrayList<Bundle>();
+ bundleList.add(b1);
+
+ assertTrue("Bundle "+b1.getSymbolicName()+" should be in active state", b1.getState() == Bundle.ACTIVE);
+ manager.quiesce(10000,bundleList);
+ //timeout is > how long participant takes, and < the quiesce timeout
+ timeoutTime = System.currentTimeMillis()+7000;
+ while (System.currentTimeMillis() < timeoutTime && b1.getState() == Bundle.ACTIVE){
+ Thread.sleep(500);
+ }
+ assertTrue("Participant should have started once", participant3.getStartedCount() == 1);
+ assertTrue("Participant should finished once", participant3.getFinishedCount() == 1);
+ assertTrue("Bundle "+b1.getSymbolicName()+" should not be in active state", b1.getState() != Bundle.ACTIVE);
+ b1.start();
+ participant3.reset();
+ }
+
+ @Test
+ public void testThreeParticipants() throws Exception {
+ //Register three participants. One returns immediately, one waits 5s then returns, one never returns
+ bundleContext.registerService(QuiesceParticipant.class.getName(), participant1, null);
+ bundleContext.registerService(QuiesceParticipant.class.getName(), participant2, null);
+ bundleContext.registerService(QuiesceParticipant.class.getName(), participant3, null);
+ //recreate the list as it may have been emptied
+ bundleList = new ArrayList<Bundle>();
+ bundleList.add(b1);
+ assertTrue("Bundle "+b1.getSymbolicName()+" should be in active state", b1.getState() == Bundle.ACTIVE);
+ manager.quiesce(10000,bundleList);
+ timeoutTime = System.currentTimeMillis()+15000;
+ while (System.currentTimeMillis() < timeoutTime && b1.getState() == Bundle.ACTIVE){
+ Thread.sleep(500);
+ }
+ assertTrue("Participant 1 should have started once", participant1.getStartedCount() == 1);
+ assertTrue("Participant 1 should finished once", participant1.getFinishedCount() == 1);
+ assertTrue("Participant 2 should have started once", participant2.getStartedCount() == 1);
+ assertTrue("Participant 2 should not have finished", participant2.getFinishedCount() == 0);
+ assertTrue("Participant 3 should have started once", participant3.getStartedCount() == 1);
+ assertTrue("Participant 3 should finished once", participant3.getFinishedCount() == 1);
+ assertTrue("Bundle "+b1.getSymbolicName()+" should not be in active state", b1.getState() != Bundle.ACTIVE);
+
+ b1.start();
+ participant1.reset();
+ participant2.reset();
+ participant3.reset();
+ }
+
+ @Test
+ public void testTwoBundles() throws Exception {
+ //Register three participants. One returns immediately, one waits 5s then returns, one never returns
+ bundleContext.registerService(QuiesceParticipant.class.getName(), participant1, null);
+ bundleContext.registerService(QuiesceParticipant.class.getName(), participant2, null);
+ bundleContext.registerService(QuiesceParticipant.class.getName(), participant3, null);
+ //recreate the list as it may have been emptied
+ bundleList = new ArrayList<Bundle>();
+ bundleList.add(b1);
+ bundleList.add(b2);
+ assertTrue("Bundle "+b1.getSymbolicName()+" should be in active state", b1.getState() == Bundle.ACTIVE);
+ assertTrue("Bundle "+b2.getSymbolicName()+" should be in active state", b2.getState() == Bundle.ACTIVE);
+ manager.quiesce(10000,bundleList);
+ timeoutTime = System.currentTimeMillis()+15000;
+ while (System.currentTimeMillis() < timeoutTime && b1.getState() == Bundle.ACTIVE){
+ Thread.sleep(500);
+ }
+ assertTrue("Participant 1 should have started once", participant1.getStartedCount() == 1);
+ assertTrue("Participant 1 should finished once", participant1.getFinishedCount() == 1);
+ assertTrue("Participant 2 should have started once", participant2.getStartedCount() == 1);
+ assertTrue("Participant 2 should not have finished", participant2.getFinishedCount() == 0);
+ assertTrue("Participant 3 should have started once", participant3.getStartedCount() == 1);
+ assertTrue("Participant 3 should finished once", participant3.getFinishedCount() == 1);
+ assertTrue("Bundle "+b1.getSymbolicName()+" should not be in active state", b1.getState() != Bundle.ACTIVE);
+ assertTrue("Bundle "+b2.getSymbolicName()+" should not be in active state", b2.getState() != Bundle.ACTIVE);
+ b1.start();
+ b2.start();
+ participant1.reset();
+ participant2.reset();
+ participant3.reset();
+ }
+
+ @Test
+ public void testOverlappedQuiesces() throws Exception {
+
+ //Register three participants. One returns immediately, one waits 5s then returns, one never returns
+ bundleContext.registerService(QuiesceParticipant.class.getName(), participant1, null);
+ bundleContext.registerService(QuiesceParticipant.class.getName(), participant2, null);
+ bundleContext.registerService(QuiesceParticipant.class.getName(), participant3, null);
+ //recreate the list as it may have been emptied
+ bundleList = new ArrayList<Bundle>();
+ bundleList.add(b1);
+ bundleList.add(b2);
+ assertTrue("Bundle "+b1.getSymbolicName()+" should be in active state", b1.getState() == Bundle.ACTIVE);
+ assertTrue("Bundle "+b2.getSymbolicName()+" should be in active state", b2.getState() == Bundle.ACTIVE);
+ assertTrue("Bundle "+b3.getSymbolicName()+" should be in active state", b3.getState() == Bundle.ACTIVE);
+ manager.quiesce(10000,bundleList);
+ bundleList = new ArrayList<Bundle>();
+ bundleList.add(b2);
+ bundleList.add(b3);
+ manager.quiesce(bundleList);
+ timeoutTime = System.currentTimeMillis()+15000;
+ while (System.currentTimeMillis() < timeoutTime && b1.getState() == Bundle.ACTIVE){
+ Thread.sleep(500);
+ }
+ assertTrue("Participant 1 should have started twice", participant1.getStartedCount() == 2);
+ assertTrue("Participant 1 should finished twice", participant1.getFinishedCount() == 2);
+ assertTrue("Participant 2 should have started twice", participant2.getStartedCount() == 2);
+ assertTrue("Participant 2 should not have finished", participant2.getFinishedCount() == 0);
+ assertTrue("Participant 3 should have started twice", participant3.getStartedCount() == 2);
+ assertTrue("Participant 3 should finished twice", participant3.getFinishedCount() == 2);
+ assertTrue("Bundle "+b1.getSymbolicName()+" should not be in active state", b1.getState() != Bundle.ACTIVE);
+ assertTrue("Bundle "+b2.getSymbolicName()+" should not be in active state", b2.getState() != Bundle.ACTIVE);
+ assertTrue("Bundle "+b3.getSymbolicName()+" should not be in active state", b3.getState() != Bundle.ACTIVE);
+ participant1.reset();
+ participant2.reset();
+ participant3.reset();
+
+ }
+
+ @org.ops4j.pax.exam.junit.Configuration
+ public static Option[] configuration() {
+ Option[] options = options(
+ bootDelegation(),
+
+ // Log
+ mavenBundle("org.ops4j.pax.logging", "pax-logging-api"),
+ mavenBundle("org.ops4j.pax.logging", "pax-logging-service"),
+ // Felix Config Admin
+ mavenBundle("org.apache.felix", "org.apache.felix.configadmin"),
+ // Felix mvn url handler
+ mavenBundle("org.ops4j.pax.url", "pax-url-mvn"),
+
+ // this is how you set the default log level when using pax
+ // logging (logProfile)
+ systemProperty("org.ops4j.pax.logging.DefaultServiceLog.level").value("DEBUG"),
+
+ // Bundles
+ mavenBundle("org.osgi", "org.osgi.compendium"),
+ mavenBundle("org.apache.aries", "org.apache.aries.util"),
+ mavenBundle("commons-lang", "commons-lang"),
+ mavenBundle("commons-collections", "commons-collections"),
+ mavenBundle("commons-pool", "commons-pool"),
+ mavenBundle("org.apache.servicemix.bundles", "org.apache.servicemix.bundles.serp"),
+ mavenBundle("org.apache.aries.quiesce", "org.apache.aries.quiesce.api"),
+ mavenBundle("org.apache.aries.quiesce", "org.apache.aries.quiesce.manager"),
+
+ equinox().version("3.5.0"));
+ options = updateOptions(options);
+ return options;
+ }
+
+
+ protected Bundle getBundle(String symbolicName) {
+ return getBundle(symbolicName, null);
+ }
+
+ protected Bundle getBundle(String bundleSymbolicName, String version) {
+ Bundle result = null;
+ for (Bundle b : bundleContext.getBundles()) {
+ if (b.getSymbolicName().equals(bundleSymbolicName)) {
+ if (version == null
+ || b.getVersion().equals(Version.parseVersion(version))) {
+ result = b;
+ break;
+ }
+ }
+ }
+ return result;
+ }
+
+ public static BootDelegationOption bootDelegation() {
+ return new BootDelegationOption("org.apache.aries.unittest.fixture");
+ }
+
+ public static MavenArtifactProvisionOption mavenBundle(String groupId,
+ String artifactId) {
+ return CoreOptions.mavenBundle().groupId(groupId).artifactId(artifactId)
+ .versionAsInProject();
+ }
+
+ protected static Option[] updateOptions(Option[] options) {
+ // We need to add pax-exam-junit here when running with the ibm
+ // jdk to avoid the following exception during the test run:
+ // ClassNotFoundException: org.ops4j.pax.exam.junit.Configuration
+ if ("IBM Corporation".equals(System.getProperty("java.vendor"))) {
+ Option[] ibmOptions = options(wrappedBundle(mavenBundle(
+ "org.ops4j.pax.exam", "pax-exam-junit")));
+ options = combine(ibmOptions, options);
+ }
+
+ return options;
+ }
+
+ protected <T> T getOsgiService(Class<T> type, long timeout) {
+ return getOsgiService(type, null, timeout);
+ }
+
+ protected <T> T getOsgiService(Class<T> type) {
+ return getOsgiService(type, null, DEFAULT_TIMEOUT);
+ }
+
+ protected <T> T getOsgiService(Class<T> type, String filter, long timeout) {
+ return getOsgiService(null, type, filter, timeout);
+ }
+
+ protected <T> T getOsgiService(BundleContext bc, Class<T> type,
+ String filter, long timeout) {
+ ServiceTracker tracker = null;
+ try {
+ String flt;
+ if (filter != null) {
+ if (filter.startsWith("(")) {
+ flt = "(&(" + Constants.OBJECTCLASS + "=" + type.getName() + ")"
+ + filter + ")";
+ } else {
+ flt = "(&(" + Constants.OBJECTCLASS + "=" + type.getName() + ")("
+ + filter + "))";
+ }
+ } else {
+ flt = "(" + Constants.OBJECTCLASS + "=" + type.getName() + ")";
+ }
+ Filter osgiFilter = FrameworkUtil.createFilter(flt);
+ tracker = new ServiceTracker(bc == null ? bundleContext : bc, osgiFilter,
+ null);
+ tracker.open();
+ // Note that the tracker is not closed to keep the reference
+ // This is buggy, has the service reference may change i think
+ Object svc = type.cast(tracker.waitForService(timeout));
+ if (svc == null) {
+ throw new RuntimeException("Gave up waiting for service " + flt);
+ }
+ return type.cast(svc);
+ } catch (InvalidSyntaxException e) {
+ throw new IllegalArgumentException("Invalid filter", e);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
Added: incubator/aries/trunk/quiesce/quiesce-manager/pom.xml
URL: http://svn.apache.org/viewvc/incubator/aries/trunk/quiesce/quiesce-manager/pom.xml?rev=984819&view=auto
==============================================================================
--- incubator/aries/trunk/quiesce/quiesce-manager/pom.xml (added)
+++ incubator/aries/trunk/quiesce/quiesce-manager/pom.xml Thu Aug 12 15:06:30 2010
@@ -0,0 +1,83 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<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.aries.quiesce</groupId>
+ <artifactId>quiesce</artifactId>
+ <version>0.2-incubating-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>org.apache.aries.quiesce.manager</artifactId>
+ <packaging>bundle</packaging>
+ <name>Apache Aries Quiesce Manager</name>
+ <description>
+ Quiesce Manager.
+ </description>
+
+ <properties>
+ <aries.osgi.export.pkg>
+ </aries.osgi.export.pkg>
+ <aries.osgi.private.pkg>
+ org.apache.aries.quiesce.manager.impl
+ </aries.osgi.private.pkg>
+ <aries.osgi.activator>
+ org.apache.aries.quiesce.manager.impl.Activator
+ </aries.osgi.activator>
+ <aries.osgi.export.service>
+ org.apache.aries.quiesce.manager.QuiesceManager
+ </aries.osgi.export.service>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.aries.quiesce</groupId>
+ <artifactId>org.apache.aries.quiesce.api</artifactId>
+ <scope>provided</scope>
+ <version>${version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.compendium</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-simple</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+
+</project>
Added: incubator/aries/trunk/quiesce/quiesce-manager/src/main/java/org/apache/aries/quiesce/manager/impl/Activator.java
URL: http://svn.apache.org/viewvc/incubator/aries/trunk/quiesce/quiesce-manager/src/main/java/org/apache/aries/quiesce/manager/impl/Activator.java?rev=984819&view=auto
==============================================================================
--- incubator/aries/trunk/quiesce/quiesce-manager/src/main/java/org/apache/aries/quiesce/manager/impl/Activator.java (added)
+++ incubator/aries/trunk/quiesce/quiesce-manager/src/main/java/org/apache/aries/quiesce/manager/impl/Activator.java Thu Aug 12 15:06:30 2010
@@ -0,0 +1,39 @@
+/*
+ * 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.aries.quiesce.manager.impl;
+
+import org.apache.aries.quiesce.manager.QuiesceManager;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+
+public class Activator implements BundleActivator {
+
+ private ServiceRegistration serviceReg;
+
+ public void start(BundleContext bundleContext) throws Exception {
+ QuiesceManager manager = new QuiesceManagerImpl(bundleContext);
+ serviceReg = bundleContext.registerService(QuiesceManager.class.getName(), manager, null);
+ }
+
+
+ public void stop(BundleContext bundleContext) throws Exception {
+ if (serviceReg != null)
+ serviceReg.unregister();
+ }
+
+}
\ No newline at end of file
Added: incubator/aries/trunk/quiesce/quiesce-manager/src/main/java/org/apache/aries/quiesce/manager/impl/QuiesceManagerImpl.java
URL: http://svn.apache.org/viewvc/incubator/aries/trunk/quiesce/quiesce-manager/src/main/java/org/apache/aries/quiesce/manager/impl/QuiesceManagerImpl.java?rev=984819&view=auto
==============================================================================
--- incubator/aries/trunk/quiesce/quiesce-manager/src/main/java/org/apache/aries/quiesce/manager/impl/QuiesceManagerImpl.java (added)
+++ incubator/aries/trunk/quiesce/quiesce-manager/src/main/java/org/apache/aries/quiesce/manager/impl/QuiesceManagerImpl.java Thu Aug 12 15:06:30 2010
@@ -0,0 +1,268 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.aries.quiesce.manager.impl;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.aries.quiesce.manager.QuiesceCallback;
+import org.apache.aries.quiesce.manager.QuiesceManager;
+import org.apache.aries.quiesce.participant.QuiesceParticipant;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class QuiesceManagerImpl implements QuiesceManager {
+
+ /** Logger */
+ private static final Logger LOGGER = LoggerFactory.getLogger(QuiesceManagerImpl.class.getName());
+ /** The default timeout to use */
+ private static int defaultTimeout = 60000;
+ /** The container's {@link BundleContext} */
+ private BundleContext bundleContext = null;
+ /** The thread pool to execute timeout commands */
+ private ScheduledExecutorService timeoutExecutor = Executors.newScheduledThreadPool(10);
+ /** The thread pool to execute quiesce commands */
+ private ExecutorService executor = new ThreadPoolExecutor(0, 10, 10, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(),new ThreadFactory() {
+
+ public Thread newThread(Runnable arg0) {
+ Thread t = new Thread(arg0, "Quiesce Manager Thread");
+ t.setDaemon(true);
+ return t;
+ }
+ });
+ /** The map of bundles that are currently being quiesced */
+ private static ConcurrentHashMap<Long, Bundle> bundleMap = new ConcurrentHashMap<Long, Bundle>();
+
+
+ public QuiesceManagerImpl(BundleContext bc) {
+ bundleContext = bc;
+ }
+
+ /**
+ * Attempts to quiesce all bundles in the list. After the timeout has elapsed,
+ * or if successfully quiesced before that, the bundles are stopped. This method
+ * is non-blocking. Calling objects wishing to track the state of the bundles
+ * need to listen for the resulting stop events.
+ */
+ public void quiesce(long timeout, List<Bundle> bundles) {
+ if (bundles != null && !!!bundles.isEmpty()) {
+ //check that bundle b is not already quiescing
+ Iterator<Bundle> it = bundles.iterator();
+ Set<Bundle> bundlesToQuiesce = new HashSet<Bundle>();
+ while(it.hasNext()) {
+ Bundle b = it.next();
+ Bundle priorBundle = bundleMap.putIfAbsent(b.getBundleId(), b);
+ if (priorBundle == null) {
+ bundlesToQuiesce.add(b);
+ }else{
+ LOGGER.warn("Already quiescing bundle "+ b.getSymbolicName());
+ }
+ }
+ Runnable command = new BundleQuiescer(bundlesToQuiesce, timeout, bundleMap);
+ executor.execute(command);
+ }
+ }
+
+ /**
+ * Attempts to quiesce all bundles in the list, using the default timeout.
+ * After the timeout has elapsed, or if successfully quiesced before that,
+ * the bundles are stopped. This method is non-blocking. Calling objects
+ * wishing to track the state of the bundles need to listen for the
+ * resulting stop events.
+ */
+ public void quiesce(List<Bundle> bundlesToQuiesce) {
+ quiesce(defaultTimeout, bundlesToQuiesce);
+ }
+
+ private static boolean stopBundle(Bundle bundleToStop) {
+ try {
+ bundleToStop.stop();
+ bundleMap.remove(bundleToStop.getBundleId());
+ }catch (BundleException be) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * BundleQuiescer is used for each bundle to quiesce. It creates a callback object for each
+ * participant. Well-behaved participants will be non-blocking on their quiesce method.
+ * When all callbacks for the participants have completed, this thread will get an
+ * interrupt, so it sleeps until it hits the timeout. When complete it stops the bundle
+ * and removes the bundles from the list of those that are being quiesced.
+ */
+ private class BundleQuiescer implements Runnable {
+
+ private Set<Bundle> bundlesToQuiesce;
+ private long timeout;
+
+ public BundleQuiescer(Set<Bundle> bundlesToQuiesce, long timeout, ConcurrentHashMap<Long, Bundle> bundleMap) {
+ this.bundlesToQuiesce = new HashSet<Bundle>(bundlesToQuiesce);
+ this.timeout = timeout;
+ }
+
+ public void run() {
+ try {
+ if (bundleContext != null) {
+ ServiceReference[] serviceRefs = bundleContext.getServiceReferences(QuiesceParticipant.class.getName(), null);
+ if (serviceRefs != null) {
+ List<QuiesceParticipant> participants = new ArrayList<QuiesceParticipant>();
+ final List<QuiesceCallbackImpl> callbacks = new ArrayList<QuiesceCallbackImpl>();
+ Set<Bundle> copyOfBundles = new HashSet<Bundle>(bundlesToQuiesce);
+ Timer timer = new Timer();
+
+ //Create callback objects for all participants
+ for( ServiceReference sr : serviceRefs ) {
+ QuiesceParticipant participant = (QuiesceParticipant) bundleContext.getService(sr);
+ participants.add(participant);
+ callbacks.add(new QuiesceCallbackImpl(copyOfBundles, callbacks, timer));
+ }
+
+ //Quiesce each participant and wait for an interrupt from a callback
+ //object when all are quiesced, or the timeout to be reached
+ for( int i=0; i<participants.size(); i++ ) {
+ QuiesceParticipant participant = participants.get(i);
+ QuiesceCallbackImpl callback = callbacks.get(i);
+ List<Bundle> participantBundles = new ArrayList<Bundle>();
+ //deep copy
+ for (Bundle b : copyOfBundles) {
+ participantBundles.add(b);
+ }
+ participant.quiesce(callback, participantBundles);
+ }
+ timer.schedule(new TimerTask() {
+
+ @Override
+ public void run() {
+ //stop bundles
+ //go through callbacks and cancel all bundles
+ for ( Enumeration<Bundle> remainingBundles = bundleMap.elements(); remainingBundles.hasMoreElements(); ) {
+ Bundle b = remainingBundles.nextElement();
+ LOGGER.warn("Could not quiesce, so stopping bundle "+ b.getSymbolicName());
+ stopBundle(b);
+ }
+ /*
+ for ( QuiesceCallbackImpl cb : callbacks ) {
+ System.out.println("Clearing callback");
+ cb.clear();
+ }
+ */
+ }
+
+ }, timeout);
+ }else{
+ LOGGER.warn("No participants, so stopping bundles");
+ for ( Enumeration<Bundle> remainingBundles = bundleMap.elements(); remainingBundles.hasMoreElements(); ) {
+ Bundle b = remainingBundles.nextElement();
+ stopBundle(b);
+ }
+ }
+ }
+ } catch (InvalidSyntaxException e) {
+ LOGGER.warn("Exception trying to get service references for quiesce participants "+ e.getMessage());
+ }
+ }
+ }
+
+ /**
+ * Callback object provided for each participant for each quiesce call
+ * from the quiesce manager.
+ */
+ private static class QuiesceCallbackImpl implements QuiesceCallback {
+ //Must be a copy
+ private final Set<Bundle> toQuiesce;
+ //Must not be a copy
+ private final List<QuiesceCallbackImpl> allCallbacks;
+ //Timer so we can cancel the alarm if all done
+ private final Timer timer;
+
+ public QuiesceCallbackImpl(Collection<Bundle> toQuiesce, List<QuiesceCallbackImpl> allCallbacks, Timer timer)
+ {
+ this.toQuiesce = new HashSet<Bundle>(toQuiesce);
+ this.allCallbacks = allCallbacks;
+ this.timer = timer;
+ }
+
+ public void clear() {
+ // TODO Auto-generated method stub
+
+ }
+
+ /**
+ * Removes the bundles from the list of those to quiesce.
+ * If the list is now empty, this callback object is finished (i.e.
+ * the participant linked to this object has quiesced all the bundles
+ * requested).
+ *
+ * If all other participants have also completed, then the
+ * calling BundleQuieser thread is interrupted.
+ */
+ public void bundleQuiesced(Bundle... bundlesQuiesced) {
+
+ synchronized (allCallbacks) {
+ for(Bundle b : bundlesQuiesced) {
+ if(toQuiesce.remove(b)) {
+ if(checkOthers(b)){
+ QuiesceManagerImpl.stopBundle(b);
+ if(allCallbacksComplete()){
+ timer.cancel();
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private boolean checkOthers(Bundle b) {
+ boolean allDone = true;
+ Iterator<QuiesceCallbackImpl> it = allCallbacks.iterator();
+ while (allDone && it.hasNext()) {
+ allDone = !!!it.next().toQuiesce.contains(b);
+ }
+ return allDone;
+ }
+
+ private boolean allCallbacksComplete() {
+ boolean allDone = true;
+ Iterator<QuiesceCallbackImpl> it = allCallbacks.iterator();
+ while (allDone && it.hasNext()) {
+ allDone = !!!it.next().toQuiesce.isEmpty();
+ }
+ return allDone;
+ }
+ }
+}
\ No newline at end of file